From da77deba78c8a7447b4a38324d2422a5df293b26 Mon Sep 17 00:00:00 2001 From: clsr Date: Fri, 18 Aug 2017 13:46:10 +0200 Subject: Initial commit --- response_test.go | 317 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 response_test.go (limited to 'response_test.go') 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)) + } + } + } +} -- cgit