Start with core protocol tests
This commit is contained in:
parent
11343caaae
commit
96e431cfda
|
@ -6,10 +6,13 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var fileUrlMatcher = regexp.MustCompilePOSIX("^/([a-z0-9]{32})$")
|
||||
|
||||
// HandlerConfig holds the configuration for a tus Handler.
|
||||
type HandlerConfig struct {
|
||||
// Dir points to a filesystem path used by tus to store uploaded and partial
|
||||
|
@ -57,6 +60,7 @@ type Handler struct {
|
|||
sendError chan<- error
|
||||
}
|
||||
|
||||
// ServeHTTP processes an incoming request according to the tus protocol.
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// Verify that url matches BasePath
|
||||
absPath := r.URL.Path
|
||||
|
@ -83,6 +87,20 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if matches := fileUrlMatcher.FindStringSubmatch(relPath); matches != nil {
|
||||
//id := matches[1]
|
||||
if r.Method == "PATCH" {
|
||||
return
|
||||
}
|
||||
|
||||
// handle invalid method
|
||||
allowed := "PATCH"
|
||||
w.Header().Set("Allow", allowed)
|
||||
err := errors.New(r.Method + " used against file creation url. Allowed: "+allowed)
|
||||
h.err(err, w, http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// handle unknown url
|
||||
err := errors.New("unknown url: " + absPath + " - does not match file pattern")
|
||||
h.err(err, w, http.StatusNotFound)
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// handler_test.go focuses on functional tests that verify that the Handler
|
||||
// implements the tus protocol correctly.
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
@ -49,37 +53,45 @@ func (s *TestSetup) Teardown() {
|
|||
|
||||
var Protocol_FileCreation_Tests = []struct {
|
||||
Description string
|
||||
Method string
|
||||
Headers map[string]string
|
||||
ExpectStatusCode int
|
||||
ExpectHeaders map[string]string
|
||||
MatchLocation *regexp.Regexp
|
||||
*TestRequest
|
||||
}{
|
||||
{
|
||||
Description: "Bad method",
|
||||
TestRequest: &TestRequest{
|
||||
Method: "PUT",
|
||||
ExpectStatusCode: http.StatusMethodNotAllowed,
|
||||
ExpectHeaders: map[string]string{"Allow": "POST"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Missing Final-Length header",
|
||||
TestRequest: &TestRequest{
|
||||
ExpectStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Invalid Final-Length header",
|
||||
TestRequest: &TestRequest{
|
||||
Headers: map[string]string{"Final-Length": "fuck"},
|
||||
ExpectStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Negative Final-Length header",
|
||||
TestRequest: &TestRequest{
|
||||
Headers: map[string]string{"Final-Length": "-10"},
|
||||
ExpectStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Valid Request",
|
||||
TestRequest: &TestRequest{
|
||||
Headers: map[string]string{"Final-Length": "1024"},
|
||||
ExpectStatusCode: http.StatusCreated,
|
||||
MatchLocation: regexp.MustCompile("^http://.+" + regexp.QuoteMeta(basePath) + "[a-z0-9]{32}$"),
|
||||
MatchHeaders: map[string]*regexp.Regexp{
|
||||
"Location": regexp.MustCompile("^http://.+" + regexp.QuoteMeta(basePath) + "[a-z0-9]{32}$"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -87,56 +99,125 @@ func TestProtocol_FileCreation(t *testing.T) {
|
|||
setup := Setup()
|
||||
defer setup.Teardown()
|
||||
|
||||
Tests:
|
||||
for _, test := range Protocol_FileCreation_Tests {
|
||||
t.Logf("test: %s", test.Description)
|
||||
|
||||
method := test.Method
|
||||
if method == "" {
|
||||
method = "POST"
|
||||
test.Url = setup.Server.URL + setup.Handler.config.BasePath
|
||||
if test.Method == "" {
|
||||
test.Method = "POST"
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, setup.Server.URL+setup.Handler.config.BasePath, nil)
|
||||
if err := test.Do(); err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestRequest is a test helper that performs and validates requests according
|
||||
// to the struct fields below.
|
||||
type TestRequest struct {
|
||||
Method string
|
||||
Url string
|
||||
Headers map[string]string
|
||||
ExpectStatusCode int
|
||||
ExpectHeaders map[string]string
|
||||
MatchHeaders map[string]*regexp.Regexp
|
||||
Response *http.Response
|
||||
}
|
||||
|
||||
func (r *TestRequest) Do() error {
|
||||
req, err := http.NewRequest(r.Method, r.Url, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
for key, val := range test.Headers {
|
||||
for key, val := range r.Headers {
|
||||
req.Header.Set(key, val)
|
||||
}
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if res.StatusCode != r.ExpectStatusCode {
|
||||
return fmt.Errorf("unexpected status code: %d, expected: %d", res.StatusCode, r.ExpectStatusCode)
|
||||
}
|
||||
|
||||
if test.ExpectStatusCode != 0 && test.ExpectStatusCode != res.StatusCode {
|
||||
t.Errorf("bad status: %d, expected: %d - %s", res.StatusCode, test.ExpectStatusCode, body)
|
||||
continue Tests
|
||||
}
|
||||
|
||||
for key, val := range test.ExpectHeaders {
|
||||
for key, val := range r.ExpectHeaders {
|
||||
if got := res.Header.Get(key); got != val {
|
||||
t.Errorf("expected \"%s: %s\" header, but got: \"%s: %s\"", key, val, key, got)
|
||||
continue Tests
|
||||
return fmt.Errorf("expected \"%s: %s\" header, but got: \"%s: %s\"", key, val, key, got)
|
||||
}
|
||||
}
|
||||
|
||||
if test.MatchLocation != nil {
|
||||
location := res.Header.Get("Location")
|
||||
if !test.MatchLocation.MatchString(location) {
|
||||
t.Errorf("location \"%s\" did not match: \"%s\"", location, test.MatchLocation.String())
|
||||
for key, matcher := range r.MatchHeaders {
|
||||
got := res.Header.Get(key)
|
||||
if !matcher.MatchString(got) {
|
||||
return fmt.Errorf("expected %s header to match: %s but got: %s", key, matcher.String(), got)
|
||||
}
|
||||
}
|
||||
|
||||
r.Response = res
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var Protocol_Core_Tests = []struct {
|
||||
Description string
|
||||
FinalLength int64
|
||||
Requests []TestRequest
|
||||
}{
|
||||
{
|
||||
Description: "Bad method",
|
||||
FinalLength: 1024,
|
||||
Requests: []TestRequest{
|
||||
{
|
||||
Method: "PUT",
|
||||
ExpectStatusCode: http.StatusMethodNotAllowed,
|
||||
ExpectHeaders: map[string]string{"Allow": "PATCH"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestProtocol_Core(t *testing.T) {
|
||||
setup := Setup()
|
||||
defer setup.Teardown()
|
||||
|
||||
Tests:
|
||||
for _, test := range Protocol_Core_Tests {
|
||||
t.Logf("test: %s", test.Description)
|
||||
|
||||
location := createFile(setup, test.FinalLength)
|
||||
for _, request := range test.Requests {
|
||||
request.Url = location
|
||||
if err := request.Do(); err != nil {
|
||||
t.Error(err)
|
||||
continue Tests
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var Protocol_Core_Tests = []struct {
|
||||
}{}
|
||||
// createFile is a test helper that creates a new file and returns the url.
|
||||
func createFile(setup *TestSetup, finalLength int64) (url string) {
|
||||
req := TestRequest{
|
||||
Method: "POST",
|
||||
Url: setup.Server.URL + setup.Handler.config.BasePath,
|
||||
Headers: map[string]string{"Final-Length": fmt.Sprintf("%d", finalLength)},
|
||||
ExpectStatusCode: http.StatusCreated,
|
||||
}
|
||||
|
||||
if err := req.Do(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
location := req.Response.Header.Get("Location")
|
||||
if location == "" {
|
||||
panic("empty Location header")
|
||||
}
|
||||
|
||||
return location
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue