summaryrefslogtreecommitdiffstats
path: root/response_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'response_test.go')
-rw-r--r--response_test.go317
1 files changed, 317 insertions, 0 deletions
diff --git a/response_test.go b/response_test.go
new file mode 100644
index 0000000..8033335
--- /dev/null
+++ b/response_test.go
@@ -0,0 +1,317 @@
+package cnp
+
+import (
+ "bytes"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+)
+
+var (
+ responseTests = []responseTest{
+ // invalid intent
+ {"", nil, ErrorInvalid{}, ErrorInvalid{}},
+ {"foo", nil, ErrorInvalid{}, ErrorInvalid{}},
+ {"example.com/path", nil, ErrorInvalid{}, ErrorInvalid{}},
+ {"errors", nil, ErrorInvalid{}, ErrorInvalid{}},
+ {" ok", nil, ErrorInvalid{}, ErrorInvalid{}},
+ {"ok ", nil, ErrorInvalid{}, ErrorInvalid{}},
+ {"оk", nil, ErrorInvalid{}, ErrorInvalid{}},
+
+ // invalid response params
+ {"ok", Parameters{"length": "w"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"length": "-1"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"length": "03"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"name": "/.."}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"name": "/"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"name": "foo/bar"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"name": "foo/bar"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"name": "foo\x00bar"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"type": "foo"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"type": "\x00"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"type": "text/plain\x00"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"type": "foo/bar "}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"type": " foo/bar"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"type": "foo /bar"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"type": "foo/ bar"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"type": "foo/bar\n"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"type": "foo/b(r"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "now"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "today"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "Thu Jan 1 00:00:00 UTC 1970"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "1970-01-01 00:00:00+00:00"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "1970-01-01 00:00:00 UTC"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "1970-01-01 00:00:00+0000"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "1970-01-01 00:00:00+00"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "1970-01-01T00:00:00+00:00"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "1970-01-01T00:00:00 UTC"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "1970-01-01T00:00:00+0000"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "1970-01-01T00:00:00+00"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0000-00-01T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0000-01-00T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0000-01-01T24:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0000-01-01T00:60:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0000-01-01T00:00:60Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0000-11-31T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0001-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0002-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0003-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0005-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "0100-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "1000-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "123-01-01T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "12345-01-01T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "-5-01-01T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"time": "-2005-01-01T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "now"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "today"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "Thu Jan 1 00:00:00 UTC 1970"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "1970-01-01 00:00:00+00:00"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "1970-01-01 00:00:00 UTC"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "1970-01-01 00:00:00+0000"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "1970-01-01 00:00:00+00"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "1970-01-01T00:00:00+00:00"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "1970-01-01T00:00:00 UTC"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "1970-01-01T00:00:00+0000"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "1970-01-01T00:00:00+00"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0000-00-01T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0000-01-00T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0000-01-01T24:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0000-01-01T00:60:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0000-01-01T00:00:60Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0000-11-31T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0001-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0002-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0003-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0005-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "0100-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "1000-02-29T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "123-01-01T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "12345-01-01T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "-5-01-01T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"ok", Parameters{"modified": "-2005-01-01T00:00:00Z"}, nil, ErrorInvalid{}},
+ {"redirect", Parameters{"location": "foo"}, nil, ErrorInvalid{}},
+ {"redirect", Parameters{"location": "foo bar/baz quux"}, nil, ErrorInvalid{}},
+ {"redirect", Parameters{"location": "/foo\x00bar"}, nil, ErrorInvalid{}},
+ {"error", Parameters{"reason": "ok"}, nil, ErrorInvalid{}},
+ {"error", Parameters{"reason": "not supported"}, nil, ErrorInvalid{}},
+ {"error", Parameters{"reason": "syntax\n"}, nil, ErrorInvalid{}},
+ {"error", Parameters{"reason": " server_error"}, nil, ErrorInvalid{}},
+ {"error", Parameters{"reason": "invalid "}, nil, ErrorInvalid{}},
+
+ // invalid: redirect *requires* the location parameter
+ {"redirect", nil, nil, ErrorInvalid{}},
+ {"redirect", Parameters{"location": ""}, nil, ErrorInvalid{}},
+
+ // valid simple responses
+ {"ok", nil, nil, nil},
+ {"not_modified", nil, nil, nil},
+ {"error", nil, nil, nil},
+
+ // valid responses with parameters
+ {"ok", Parameters{"length": "", "name": "", "type": "", "time": "", "reason": "", "location": "", "modified": "", "": "", "q\x00we": "=a s\nd"}, nil, nil},
+
+ {"ok", Parameters{"length": "0"}, nil, nil},
+ {"ok", Parameters{"length": "1"}, nil, nil},
+ {"ok", Parameters{"length": "12345670089000000"}, nil, nil},
+ {"ok", Parameters{"length": "12345678900"}, nil, nil},
+
+ {"ok", Parameters{"name": "foobar"}, nil, nil},
+ {"ok", Parameters{"name": "foo bar"}, nil, nil},
+ {"ok", Parameters{"name": "foo=bar\nbaz\rquux"}, nil, nil},
+ {"ok", Parameters{"name": "..-~!foo bar\nbaz\rquux=qwe\\asd"}, nil, nil},
+ {"ok", Parameters{"name": strings.Repeat("w", 1024*8)}, nil, nil},
+ {"ok", Parameters{"name": " "}, nil, nil},
+ {"ok", Parameters{"name": ".."}, nil, nil},
+ {"ok", Parameters{"name": "."}, nil, nil},
+
+ {"ok", Parameters{"type": "foo/bar"}, nil, nil},
+ {"ok", Parameters{"type": "application/octet-stream"}, nil, nil},
+ {"ok", Parameters{"type": "x-test/x-testing"}, nil, nil},
+ {"ok", Parameters{"type": "application/vnd.testing.test-test.5+xml"}, nil, nil},
+
+ {"ok", Parameters{"modified": "1970-01-01T00:00:00Z"}, nil, nil},
+ {"ok", Parameters{"modified": "0000-01-01T00:00:00Z"}, nil, nil},
+ {"ok", Parameters{"modified": "9999-12-31T23:59:59Z"}, nil, nil},
+ {"ok", Parameters{"modified": "0123-05-06T07:08:09Z"}, nil, nil},
+ {"ok", Parameters{"modified": "0000-02-29T00:00:00Z"}, nil, nil},
+ {"ok", Parameters{"modified": "2000-02-29T00:00:00Z"}, nil, nil},
+
+ {"ok", Parameters{"time": "1970-01-01T00:00:00Z"}, nil, nil},
+ {"ok", Parameters{"time": "0000-01-01T00:00:00Z"}, nil, nil},
+ {"ok", Parameters{"time": "9999-12-31T23:59:59Z"}, nil, nil},
+ {"ok", Parameters{"time": "0123-05-06T07:08:09Z"}, nil, nil},
+ {"ok", Parameters{"time": "0000-02-29T00:00:00Z"}, nil, nil},
+ {"ok", Parameters{"time": "2000-02-29T00:00:00Z"}, nil, nil},
+
+ {"error", Parameters{"reason": "syntax"}, nil, nil},
+ {"error", Parameters{"reason": "version"}, nil, nil},
+ {"error", Parameters{"reason": "invalid"}, nil, nil},
+ {"error", Parameters{"reason": "not_supported"}, nil, nil},
+ {"error", Parameters{"reason": "too_large"}, nil, nil},
+ {"error", Parameters{"reason": "not_found"}, nil, nil},
+ {"error", Parameters{"reason": "denied"}, nil, nil},
+ {"error", Parameters{"reason": "rejected"}, nil, nil},
+ {"error", Parameters{"reason": "server_error"}, nil, nil},
+
+ {"redirect", Parameters{"location": "/"}, nil, nil},
+ {"redirect", Parameters{"location": "foo/bar"}, nil, nil},
+ {"redirect", Parameters{"location": "foo/"}, nil, nil},
+ {"redirect", Parameters{"location": "/bar"}, nil, nil},
+ {"redirect", Parameters{"location": "[::1]:12345/ foo\n\x01\xff/"}, nil, nil},
+ {"redirect", Parameters{"location": "/../../////././.."}, nil, nil},
+ }
+)
+
+type responseTest struct {
+ i string
+ p Parameters
+ e, v error
+}
+
+func TestNewResponse(t *testing.T) {
+ for _, tst := range responseTests {
+ resp, err := NewResponse(tst.i, []byte{})
+ if !errorEqual(tst.e, err) {
+ t.Errorf("NewResponse(%q): expected error %+v, got %+v (%v)", tst.i, tst.e, err, err)
+ continue
+ }
+ if tst.e == nil {
+ if tst.i != resp.ResponseIntent() {
+ t.Errorf("NewResponse(%q): got unexpected intent %q", tst.i, resp.ResponseIntent())
+ }
+ }
+ }
+}
+
+func TestWriteParseResponse(t *testing.T) {
+ for _, tst := range responseTests {
+ if tst.e != nil {
+ continue // skip invalid intents
+ }
+ resp := &Response{Message{Header{0, 3, "ok", Parameters{}}, nil, nil}}
+ if err := resp.SetResponseIntent(tst.i); err != nil {
+ t.Errorf("SetResponseIntent(%q) error: %v", tst.i, err)
+ continue
+ }
+ for k, v := range tst.p {
+ resp.SetParam(k, v)
+ }
+
+ var buf bytes.Buffer
+ if err := resp.Write(&buf); err != nil {
+ t.Errorf("%+v.Write: error %v", resp, err)
+ continue
+ }
+
+ s := buf.String()
+ resp2, err := ParseResponse(strings.NewReader(s))
+ if err != nil {
+ t.Errorf("ParseResponse(%q) error: %v", s, err)
+ continue
+ }
+ if !msgEqual(&resp.Message, &resp2.Message) {
+ t.Errorf("Write/Parse: expected %+v, got %+v", resp, resp2)
+ continue
+ }
+ }
+}
+
+func TestResponseValidate(t *testing.T) {
+ for _, tst := range responseTests {
+ resp := &Response{Message{Header{Intent: tst.i, Parameters: tst.p}, nil, nil}}
+ if err := resp.Validate(); !errorEqual(tst.v, err) {
+ t.Errorf("%+v.Validate(): expected error %+v, got %+v (%v)", resp, tst.v, err, err)
+ }
+ }
+}
+
+func TestResponseGetSet(t *testing.T) {
+ e := func(k, v string, expect, err error) {
+ if !errorEqual(err, expect) {
+ t.Errorf("setting param %q to %q: expected %+v, got %+v (%v)", k, v, expect, err, err)
+ }
+ }
+ c := func(k, v string, expect error, p string) {
+ if expect == nil && v != p {
+ t.Errorf("getting param %q: expected %q, got %q", k, v, p)
+ }
+ }
+
+ for _, tst := range responseTests {
+ resp, _ := NewResponse("ok", nil)
+ if err := resp.SetResponseIntent(tst.i); !errorEqual(err, tst.e) {
+ t.Errorf("setting response intent to %q: expected %+v, got %+v (%v)", tst.i, tst.e, err, err)
+ continue
+ }
+ if tst.e == nil && resp.ResponseIntent() != tst.i {
+ t.Errorf("getting response intent: expected %q, got %q", tst.i, resp.ResponseIntent())
+ continue
+ }
+ for k, v := range tst.p {
+ switch k {
+ case "length":
+ n, _ := strconv.ParseUint(v, 10, 63)
+ resp.SetLength(int64(n))
+ s := strconv.FormatInt(resp.Length(), 10)
+ if s == "0" && v == "" {
+ s = ""
+ }
+ c(k, v, tst.v, s)
+ case "name":
+ e(k, v, tst.v, resp.SetName(v))
+ c(k, v, tst.v, resp.Name())
+ case "type":
+ e(k, v, tst.v, resp.SetType(v))
+ t := resp.Type()
+ if t == "application/octet-stream" && v == "" {
+ t = ""
+ }
+ c(k, v, tst.v, t)
+ case "time":
+ var tm time.Time
+ if v != "" {
+ tm, _ = time.Parse(time.RFC3339, v)
+ }
+ resp.SetTime(tm)
+ tm2 := ""
+ if !resp.Time().IsZero() {
+ tm2 = resp.Time().Format(time.RFC3339)
+ }
+ c(k, v, tst.v, tm2)
+ case "modified":
+ var tm time.Time
+ if v != "" {
+ tm, _ = time.Parse(time.RFC3339, v)
+ }
+ resp.SetModified(tm)
+ tm2 := ""
+ if !resp.Modified().IsZero() {
+ tm2 = resp.Modified().Format(time.RFC3339)
+ }
+ c(k, v, tst.v, tm2)
+ case "location":
+ ss := strings.SplitN(v, "/", 2)
+ if len(ss) != 2 {
+ continue // invalid location
+ }
+ e(k, v, tst.v, resp.SetLocation(ss[0], "/"+ss[1]))
+ host, path, err := resp.Location()
+ if err != nil {
+ t.Errorf("getting parameter location: error %v", err)
+ } else {
+ c(k, v, tst.v, host+path)
+ }
+ case "reason":
+ e(k, v, tst.v, resp.SetReason(v))
+ c(k, v, tst.v, resp.Reason())
+ default:
+ resp.SetParam(k, v)
+ c(k, v, tst.v, resp.Param(k))
+ }
+ }
+ }
+}