194 lines
4.0 KiB
Go
194 lines
4.0 KiB
Go
|
package tusd
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
"net/http"
|
||
|
"net/http/httptest"
|
||
|
"os"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
type patchStore struct {
|
||
|
zeroStore
|
||
|
t *testing.T
|
||
|
called bool
|
||
|
}
|
||
|
|
||
|
func (s patchStore) GetInfo(id string) (FileInfo, error) {
|
||
|
if id != "yes" {
|
||
|
return FileInfo{}, os.ErrNotExist
|
||
|
}
|
||
|
|
||
|
return FileInfo{
|
||
|
Offset: 5,
|
||
|
Size: 20,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (s patchStore) WriteChunk(id string, offset int64, src io.Reader) error {
|
||
|
if s.called {
|
||
|
s.t.Errorf("WriteChunk must be called only once")
|
||
|
}
|
||
|
s.called = true
|
||
|
|
||
|
if offset != 5 {
|
||
|
s.t.Errorf("Expected offset to be 5 (got %v)", offset)
|
||
|
}
|
||
|
|
||
|
data, err := ioutil.ReadAll(src)
|
||
|
if err != nil {
|
||
|
s.t.Error(err)
|
||
|
}
|
||
|
|
||
|
if string(data) != "hello" {
|
||
|
s.t.Errorf("Expected source to be 'hello'")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func TestPatch(t *testing.T) {
|
||
|
handler, _ := NewHandler(Config{
|
||
|
MaxSize: 100,
|
||
|
DataStore: patchStore{
|
||
|
t: t,
|
||
|
},
|
||
|
})
|
||
|
|
||
|
// Test successful request
|
||
|
req, _ := http.NewRequest("PATCH", "yes", strings.NewReader("hello"))
|
||
|
req.Header.Set("TUS-Resumable", "1.0.0")
|
||
|
req.Header.Set("Offset", "5")
|
||
|
w := httptest.NewRecorder()
|
||
|
handler.ServeHTTP(w, req)
|
||
|
if w.Code != http.StatusNoContent {
|
||
|
t.Errorf("Expected %v (got %v)", http.StatusNoContent, w.Code)
|
||
|
}
|
||
|
|
||
|
// Test non-existing file
|
||
|
req, _ = http.NewRequest("PATCH", "no", nil)
|
||
|
req.Header.Set("TUS-Resumable", "1.0.0")
|
||
|
req.Header.Set("Offset", "0")
|
||
|
w = httptest.NewRecorder()
|
||
|
handler.ServeHTTP(w, req)
|
||
|
if w.Code != http.StatusNotFound {
|
||
|
t.Errorf("Expected %v (got %v)", http.StatusNotFound, w.Code)
|
||
|
}
|
||
|
|
||
|
// Test wrong offset
|
||
|
req, _ = http.NewRequest("PATCH", "yes", nil)
|
||
|
req.Header.Set("TUS-Resumable", "1.0.0")
|
||
|
req.Header.Set("Offset", "4")
|
||
|
w = httptest.NewRecorder()
|
||
|
handler.ServeHTTP(w, req)
|
||
|
if w.Code != http.StatusConflict {
|
||
|
t.Errorf("Expected %v (got %v)", http.StatusConflict, w.Code)
|
||
|
}
|
||
|
|
||
|
// Test exceeding file size
|
||
|
req, _ = http.NewRequest("PATCH", "yes", strings.NewReader("hellothisismorethan15bytes"))
|
||
|
req.Header.Set("TUS-Resumable", "1.0.0")
|
||
|
req.Header.Set("Offset", "5")
|
||
|
w = httptest.NewRecorder()
|
||
|
handler.ServeHTTP(w, req)
|
||
|
if w.Code != http.StatusRequestEntityTooLarge {
|
||
|
t.Errorf("Expected %v (got %v)", http.StatusRequestEntityTooLarge, w.Code)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type overflowPatchStore struct {
|
||
|
zeroStore
|
||
|
t *testing.T
|
||
|
called bool
|
||
|
}
|
||
|
|
||
|
func (s overflowPatchStore) GetInfo(id string) (FileInfo, error) {
|
||
|
if id != "yes" {
|
||
|
return FileInfo{}, os.ErrNotExist
|
||
|
}
|
||
|
|
||
|
return FileInfo{
|
||
|
Offset: 5,
|
||
|
Size: 20,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (s overflowPatchStore) WriteChunk(id string, offset int64, src io.Reader) error {
|
||
|
if s.called {
|
||
|
s.t.Errorf("WriteChunk must be called only once")
|
||
|
}
|
||
|
s.called = true
|
||
|
|
||
|
if offset != 5 {
|
||
|
s.t.Errorf("Expected offset to be 5 (got %v)", offset)
|
||
|
}
|
||
|
|
||
|
data, err := ioutil.ReadAll(src)
|
||
|
if err != nil {
|
||
|
s.t.Error(err)
|
||
|
}
|
||
|
|
||
|
if len(data) != 15 {
|
||
|
s.t.Errorf("Expected 15 bytes got %v", len(data))
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// noEOFReader implements io.Reader, io.Writer, io.Closer but does not return
|
||
|
// an io.EOF when the internal buffer is empty. This way we can simulate slow
|
||
|
// networks.
|
||
|
type noEOFReader struct {
|
||
|
closed bool
|
||
|
buffer []byte
|
||
|
}
|
||
|
|
||
|
func (r *noEOFReader) Read(dst []byte) (int, error) {
|
||
|
if r.closed && len(r.buffer) == 0 {
|
||
|
return 0, io.EOF
|
||
|
}
|
||
|
|
||
|
n := copy(dst, r.buffer)
|
||
|
r.buffer = r.buffer[n:]
|
||
|
return n, nil
|
||
|
}
|
||
|
|
||
|
func (r *noEOFReader) Close() error {
|
||
|
r.closed = true
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (r *noEOFReader) Write(src []byte) (int, error) {
|
||
|
r.buffer = append(r.buffer, src...)
|
||
|
return len(src), nil
|
||
|
}
|
||
|
|
||
|
func TestPatchOverflow(t *testing.T) {
|
||
|
handler, _ := NewHandler(Config{
|
||
|
MaxSize: 100,
|
||
|
DataStore: overflowPatchStore{
|
||
|
t: t,
|
||
|
},
|
||
|
})
|
||
|
|
||
|
body := &noEOFReader{}
|
||
|
|
||
|
go func() {
|
||
|
body.Write([]byte("hellothisismorethan15bytes"))
|
||
|
body.Close()
|
||
|
}()
|
||
|
|
||
|
// Test too big body exceeding file size
|
||
|
req, _ := http.NewRequest("PATCH", "yes", body)
|
||
|
req.Header.Set("TUS-Resumable", "1.0.0")
|
||
|
req.Header.Set("Offset", "5")
|
||
|
req.Header.Set("Content-Length", "3")
|
||
|
w := httptest.NewRecorder()
|
||
|
handler.ServeHTTP(w, req)
|
||
|
if w.Code != http.StatusNoContent {
|
||
|
t.Errorf("Expected %v (got %v)", http.StatusNoContent, w.Code)
|
||
|
}
|
||
|
}
|