summaryrefslogtreecommitdiffstats
path: root/header_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'header_test.go')
-rw-r--r--header_test.go200
1 files changed, 200 insertions, 0 deletions
diff --git a/header_test.go b/header_test.go
new file mode 100644
index 0000000..b035078
--- /dev/null
+++ b/header_test.go
@@ -0,0 +1,200 @@
+package cnp
+
+import "testing"
+
+var escapes = map[string]string{
+ "": ``,
+ "ContNet": `ContNet`,
+ " ": `\_`,
+ "=": `\-`,
+ "\n": `\n`,
+ "\x00": `\0`,
+ "\\": `\\`,
+ "a\nb c=d\x00e\\f": `a\nb\_c\-d\0e\\f`,
+ "\n\n\n": `\n\n\n`,
+ "===": `\-\-\-`,
+ " ": `\_\_\_`,
+ "\x00\x00\x00": `\0\0\0`,
+ "\\\\\\": `\\\\\\`,
+ " =\n\x00\\": `\_\-\n\0\\`,
+ "\b5Ὂg̀9! ℃ᾭG": "\b5Ὂg̀9!\\_℃ᾭG",
+ "\xff\x00\xee\xaa\xee": "\xff\\0\xee\xaa\xee",
+ "\x00\x10\x20\x30\x40": "\\0\x10\\_\x30\x40",
+ "\x10\x50\x90\xe0": "\x10\x50\x90\xe0",
+ "Hello, 世界": `Hello,\_世界`,
+ "\xed\x9f\xbf": "\xed\x9f\xbf",
+ "\xee\x80\x80": "\xee\x80\x80",
+ "\xef\xbf\xbd": "\xef\xbf\xbd",
+ "\x80\x80\x80\x80": "\x80\x80\x80\x80",
+}
+
+func TestEscape(t *testing.T) {
+ for k, v := range escapes {
+ e := string(Escape(k))
+ if e != v {
+ t.Errorf("Escape(%q) -> %q, expected %q", k, e, v)
+ }
+ }
+ for i := 0; i <= 255; i++ {
+ switch i {
+ case '\x00', ' ', '=', '\n', '\\':
+ continue
+ default:
+ s := string([]byte{byte(i)})
+ b := Escape(s)
+ if s != string(b) {
+ t.Errorf("Escape(%q) -> %q, expected %q", s, b, s)
+ }
+ }
+ }
+}
+
+func TestUnEscape(t *testing.T) {
+ for k, v := range escapes {
+ u, err := Unescape([]byte(v))
+ if err != nil {
+ t.Errorf("Unescape(%q): error: %s", v, err)
+ } else if u != k {
+ t.Errorf("Unescape(%q) -> %q, expected %q", v, u, k)
+ }
+ }
+ for i := 0; i <= 255; i++ {
+ switch i {
+ case '\x00', ' ', '=', '\n', '\\':
+ continue
+ default:
+ b := []byte{byte(i)}
+ s, err := Unescape(b)
+ if err != nil {
+ t.Errorf("Unescape(%q): error: %s", b, err)
+ } else if string(b) != s {
+ t.Errorf("Escape(%q) -> %q, expected %q", b, s, b)
+ }
+ }
+ }
+}
+
+var headers = map[string]struct {
+ h Header
+ e error
+}{
+ // invalid version
+ "cnp/0.3ok\n": {e: ErrorSyntax{}},
+ "cwp/0.3 ok\n": {e: ErrorSyntax{}},
+ "cnp/0.03 ok\n": {e: ErrorSyntax{}},
+ "cnp/00.3 ok\n": {e: ErrorSyntax{}},
+ "cnp/0..3 ok\n": {e: ErrorSyntax{}},
+ "cnp/.3 ok\n": {e: ErrorSyntax{}},
+ "cnp/0. ok\n": {e: ErrorSyntax{}},
+ "cnp/. ok\n": {e: ErrorSyntax{}},
+ "cnp/0,3 ok\n": {e: ErrorSyntax{}},
+ "/0.3 ok\n": {e: ErrorSyntax{}},
+ "0.3 ok\n": {e: ErrorSyntax{}},
+ "cnp/ ok\n": {e: ErrorSyntax{}},
+ "cnp ok\n": {e: ErrorSyntax{}},
+ "cnp.0.3 ok\n": {e: ErrorSyntax{}},
+ "cnp/03 ok\n": {e: ErrorSyntax{}},
+ "cnp/3 ok\n": {e: ErrorSyntax{}},
+ "cnp/0 ok\n": {e: ErrorSyntax{}},
+ "cnp/0 3 ok\n": {e: ErrorSyntax{}},
+ "cnp/0/3 ok\n": {e: ErrorSyntax{}},
+
+ // missing/invalid intent
+ "cnp/0.3\n": {e: ErrorSyntax{}},
+ "cnp/0.3 \n": {e: ErrorSyntax{}},
+ "cnp/0.3 o\x00k\n": {e: ErrorSyntax{}},
+ "cnp/0.3 foo=bar\n": {e: ErrorSyntax{}},
+
+ // missing/invalid line end
+ "cnp/0.3 ok \n": {e: ErrorSyntax{}},
+ "cnp/0.3 ok\n\n": {e: ErrorSyntax{}},
+ "cnp/0.3 ok": {e: ErrorSyntax{}},
+ "cnp/0.3 ok ": {e: ErrorSyntax{}},
+ "cnp/0.3 ok foo=bar \n": {e: ErrorSyntax{}},
+ "cnp/0.3 ok foo=bar": {e: ErrorSyntax{}},
+ "cnp/0.3 ok = =\n": {e: ErrorSyntax{}},
+
+ // spaces
+ "cnp/0.3 ok foo=bar\n": {e: ErrorSyntax{}},
+ "cnp/0.3 ok\n": {e: ErrorSyntax{}},
+ "cnp/0.3\tok\n": {e: ErrorSyntax{}},
+
+ // invalid params
+ "cnp/0.3 ok foo==bar\n": {e: ErrorSyntax{}},
+ "cnp/0.3 ok foo=bar=baz\n": {e: ErrorSyntax{}},
+ "cnp/0.3 ok foo=bar baz=quux \n": {e: ErrorSyntax{}},
+ "cnp/0.3 ok foo\\-bar\n": {e: ErrorSyntax{}},
+
+ // invalid escape sequences
+ "cnp/0.3 o\\k\n": {e: ErrorSyntax{}},
+ "cnp/0.3 ok qwe=\\\n": {e: ErrorSyntax{}},
+ "cnp/0.3 ok fo\\o=bar\n": {e: ErrorSyntax{}},
+
+ // valid
+ "cnp/0.0 ok\n": {h: Header{0, 0, "ok", nil}},
+ "cnp/0.3 ok\n": {h: Header{0, 3, "ok", nil}},
+ "cnp/1.0 ok\n": {h: Header{1, 0, "ok", nil}},
+ "cnp/123456.987654 ok\n": {h: Header{123456, 987654, "ok", nil}},
+ "cnp/0.1 ok\n": {h: Header{0, 1, "ok", nil}},
+ "cnp/0.3 ok\r\n": {h: Header{0, 3, "ok\r", nil}},
+ "cnp/0.3 foo\\nbar\n": {h: Header{0, 3, "foo\nbar", nil}},
+ "cnp/0.3 \\-\\_\\n\\0\\\\\n": {h: Header{0, 3, "= \n\x00\\", nil}},
+
+ // valid with params
+ "cnp/0.3 ok type=text/plain\n": {h: Header{0, 3, "ok", Parameters{"type": "text/plain"}}},
+ "cnp/0.3 ok baz=quux foo=bar qwe=asd\n": {h: Header{0, 3, "ok", Parameters{"foo": "bar", "baz": "quux", "qwe": "asd"}}},
+ "cnp/0.3 ok = \\_=\\0 \\-=\\n\r\n": {h: Header{0, 3, "ok", Parameters{"": "", "=": "\n\r", " ": "\x00"}}},
+}
+
+func TestHeaderParse(t *testing.T) {
+ for raw, tst := range headers {
+ hdr, err := ParseHeader([]byte(raw))
+ if err != nil || tst.e != nil {
+ if !errorEqual(err, tst.e) {
+ t.Errorf("ParseHeader(%q): expected error %+v, got %+v (%v)", raw, tst.e, err, err)
+ }
+ } else if !headerEqual(tst.h, hdr) {
+ t.Errorf("ParseHeader(%q): expected %+v, got %+v", raw, tst.h, hdr)
+ }
+ }
+}
+
+func TestHeaderCompose(t *testing.T) {
+ for raw, tst := range headers {
+ if tst.e != nil {
+ continue
+ }
+ // Parameters.Write currently sorts parameter keys
+ /*if len(tst.h.Parameters) > 1 {
+ continue // can't depend on parameter order
+ }*/
+ str := tst.h.String()
+ if raw != str {
+ t.Errorf("%+v.String(): expected %q, got %q", tst.h, raw, str)
+ }
+ }
+}
+
+func TestNewHeader(t *testing.T) {
+ raw := "cnp/0.3 ok baz=quux foo=bar qwe=asd\n"
+ hdr := Header{0, 3, "ok", Parameters{"foo": "bar", "baz": "quux", "qwe": "asd"}}
+ h := NewHeader("ok", Parameters{"foo": "bar", "baz": "quux", "qwe": "asd"})
+ if !headerEqual(hdr, h) {
+ t.Errorf("%+v: expected %+v", h, hdr)
+ }
+ s := h.String()
+ if raw != s {
+ t.Errorf("%q: expected %q", h, hdr)
+ }
+
+ raw = "cnp/0.3 ok\n"
+ hdr = Header{0, 3, "ok", nil}
+ h = NewHeader("ok", nil)
+ if !headerEqual(hdr, h) {
+ t.Errorf("%+v: expected %+v", h, hdr)
+ }
+ s = h.String()
+ if raw != s {
+ t.Errorf("%q: expected %q", h, hdr)
+ }
+}