go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/web/negotiate_content_type_test.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package web 9 10 import ( 11 "fmt" 12 "net/http" 13 "testing" 14 15 "go.charczuk.com/sdk/assert" 16 ) 17 18 func Test_NegotiateContentType(t *testing.T) { 19 testCases := [...]struct { 20 s string 21 available []string 22 expect string 23 expectErr error 24 }{ 25 /* if no accept header, use first accepted result */ 26 {"", []string{"x/y", "foo/bar", "moo/loo"}, "x/y", nil}, 27 28 /* examples from rfc2616 */ 29 {"audio/*; q=0.2, audio/basic", []string{"audio/mp3", "audio/ogg-vorbis"}, "audio/mp3", nil}, 30 {"text/plain; q=0.5, text/html,text/x-dvi; q=0.8, text/x-c", []string{"text/html", "text/plain"}, "text/html", nil}, 31 {"text/*, text/html, text/html;level=1, */*", []string{"text/html", "text/plain"}, "text/html", nil}, 32 {"text/*, text/html, text/plain;level=1, */*", []string{"text/html", "text/plain"}, "text/html", nil}, 33 {"text/*, text/html, text/plain;level=1, */*", []string{"text/x-c", "text/html", "text/plain"}, "text/html", nil}, 34 35 /* test how q=0 interacts */ 36 {"text/html, */*;q=0", []string{"x/y"}, "", nil}, 37 {"text/html, */*;q=0", []string{"text/html"}, "text/html", nil}, 38 39 /* test wildcard matching */ 40 {"text/html, */*", []string{"x/y"}, "x/y", nil}, 41 {"text/html, text/*", []string{"text/plain"}, "text/plain", nil}, 42 43 /* test Not Acceptable results */ 44 {"text/html, image/png; q=0.4", []string{"foo/bar"}, "", nil}, 45 46 /* test matching order */ 47 {"text/html, image/png", []string{"text/html", "image/png"}, "text/html", nil}, 48 {"text/html, image/png", []string{"image/png", "text/html"}, "image/png", nil}, 49 {"text/html, image/png; q=0.4", []string{"image/png"}, "image/png", nil}, 50 {"text/html, image/png; q=0.4", []string{"text/html"}, "text/html", nil}, 51 {"text/html, image/png; q=0.4", []string{"image/png", "text/html"}, "text/html", nil}, 52 {"text/html, image/png; q=0.4", []string{"text/html", "image/png"}, "text/html", nil}, 53 {"text/html;q=0.4, image/png", []string{"image/png"}, "image/png", nil}, 54 {"text/html;q=0.4, image/png", []string{"text/html"}, "text/html", nil}, 55 {"text/html;q=0.4, image/png", []string{"image/png", "text/html"}, "image/png", nil}, 56 {"text/html;q=0.4, image/png", []string{"text/html", "image/png"}, "image/png", nil}, 57 {"image/png, image/*;q=0.4", []string{"image/jpg", "image/png"}, "image/png", nil}, 58 {"image/png, image/*;q=0.4", []string{"image/jpg"}, "image/jpg", nil}, 59 {"image/png, image/*;q=0.4", []string{"image/jpg", "image/gif"}, "image/jpg", nil}, 60 {"image/png, image/*", []string{"image/jpg", "image/gif"}, "image/jpg", nil}, 61 {"image/png, image/*", []string{"image/gif", "image/jpg"}, "image/gif", nil}, 62 {"image/png, image/*", []string{"image/gif", "image/png"}, "image/png", nil}, 63 {"image/png, image/*", []string{"image/png", "image/gif"}, "image/png", nil}, 64 65 /* test parsing errors */ 66 {",", nil, "", errNegotiateContentTypeAvailableEmpty}, 67 {",", []string{"text/html"}, "", errParseAcceptInvalidTag}, 68 {"foo*/bar", []string{"text/html"}, "", errParseAcceptInvalidTag}, 69 {"foo/bar", []string{"text/"}, "", errNegotiateContentTypeInvalidAvailableContentType}, 70 {"foo/bar", []string{"/html"}, "", errNegotiateContentTypeInvalidAvailableContentType}, 71 } 72 73 for index, tt := range testCases { 74 r := &http.Request{Header: http.Header{"Accept": {tt.s}}} 75 actual, err := NegotiateContentType(r, tt.available...) 76 if tt.expectErr != nil { 77 assert.ItsNotNil(t, err, fmt.Sprintf("tc index: %d, input: %s", index, tt.s)) 78 assert.ItsEqual(t, tt.expectErr.Error(), err.Error(), fmt.Sprintf("tc index: %d, input: %s", index, tt.s)) 79 } else { 80 assert.ItsNil(t, err, fmt.Sprintf("tc index: %d, input: %s", index, tt.s)) 81 assert.ItsEqual(t, tt.expect, actual, fmt.Sprintf("tc index: %d, input: %s", index, tt.s)) 82 } 83 } 84 } 85 86 func Test_parseAccept(t *testing.T) { 87 testCases := [...]struct { 88 Input string 89 Expect []acceptMediaType 90 ExpectErr error 91 }{ 92 /* empty case */ 93 {"", nil, nil}, 94 95 /* error cases */ 96 {",", nil, errParseAcceptInvalidTag}, 97 {"/", nil, fmt.Errorf("mime: no media type")}, 98 {"foo/", nil, fmt.Errorf("mime: expected token after slash")}, 99 {"/bar", nil, fmt.Errorf("mime: no media type")}, 100 {"foo//", nil, fmt.Errorf("mime: expected token after slash")}, 101 {"foo//*", nil, fmt.Errorf("mime: expected token after slash")}, 102 {"foo/bar/*", nil, fmt.Errorf("mime: unexpected content after media subtype")}, 103 {"*foo/bar", nil, errParseAcceptInvalidTag}, 104 {"foo/*bar", nil, errParseAcceptInvalidTag}, 105 {"foo abcd/bar", nil, fmt.Errorf("mime: expected slash after first token")}, 106 {"foo/bar abcd", nil, fmt.Errorf("mime: unexpected content after media subtype")}, 107 {"foo/bar;q=foo", nil, fmt.Errorf("strconv.ParseFloat: parsing \"foo\": invalid syntax")}, 108 {"foo/bar;level=foo", nil, fmt.Errorf("strconv.Atoi: parsing \"foo\": invalid syntax")}, 109 110 /* valid cases */ 111 {"text/html, image/png", []acceptMediaType{ 112 {RawMediaType: "text/html", Type: "text", Subtype: "html", Q: 1.0, RawParameters: map[string]string{}}, 113 {RawMediaType: "image/png", Type: "image", Subtype: "png", Q: 1.0, RawParameters: map[string]string{}}, 114 }, nil}, 115 {"text/html,image/png", []acceptMediaType{ 116 {RawMediaType: "text/html", Type: "text", Subtype: "html", Q: 1.0, RawParameters: map[string]string{}}, 117 {RawMediaType: "image/png", Type: "image", Subtype: "png", Q: 1.0, RawParameters: map[string]string{}}, 118 }, nil}, 119 {"text/html, image/*", []acceptMediaType{ 120 {RawMediaType: "text/html", Type: "text", Subtype: "html", Q: 1.0, RawParameters: map[string]string{}}, 121 {RawMediaType: "image/*", Type: "image", Subtype: "*", Q: 1.0, RawParameters: map[string]string{}}, 122 }, nil}, 123 {"*/*, image/*", []acceptMediaType{ 124 {RawMediaType: "*/*", Type: "*", Subtype: "*", Q: 1.0, RawParameters: map[string]string{}}, 125 {RawMediaType: "image/*", Type: "image", Subtype: "*", Q: 1.0, RawParameters: map[string]string{}}, 126 }, nil}, 127 {"image/png, text/html", []acceptMediaType{ 128 {RawMediaType: "image/png", Type: "image", Subtype: "png", Q: 1.0, RawParameters: map[string]string{}}, 129 {RawMediaType: "text/html", Type: "text", Subtype: "html", Q: 1.0, RawParameters: map[string]string{}}, 130 }, nil}, 131 {"text/html, image/png; q=0.5", []acceptMediaType{ 132 {RawMediaType: "text/html", Type: "text", Subtype: "html", Q: 1.0, RawParameters: map[string]string{}}, 133 {RawMediaType: "image/png", Type: "image", Subtype: "png", Q: 0.5, RawParameters: map[string]string{"q": "0.5"}}, 134 }, nil}, 135 {"text/html; q=0.5; level=1, image/png; q=0.5", []acceptMediaType{ 136 {RawMediaType: "text/html", Type: "text", Subtype: "html", Q: 0.5, Level: 1, RawParameters: map[string]string{"q": "0.5", "level": "1"}}, 137 {RawMediaType: "image/png", Type: "image", Subtype: "png", Q: 0.5, RawParameters: map[string]string{"q": "0.5"}}, 138 }, nil}, 139 } 140 141 for _, tc := range testCases { 142 tags, err := parseAccept(tc.Input) 143 if tc.ExpectErr != nil { 144 assert.ItsNotNil(t, err) 145 assert.ItsEqual(t, tc.ExpectErr.Error(), err.Error()) 146 } else { 147 assert.ItsNil(t, err) 148 assert.ItsEqual(t, tc.Expect, tags) 149 } 150 } 151 } 152 153 func Test_splitOn(t *testing.T) { 154 testCases := [...]struct { 155 Input string 156 Split rune 157 ExpectedBefore string 158 ExpectedAfter string 159 }{ 160 {"", 0, "", ""}, 161 {"foo", rune('/'), "foo", ""}, 162 {"foo/", rune('/'), "foo", ""}, 163 {"foo/bar", rune('/'), "foo", "bar"}, 164 {"foo|bar", rune('/'), "foo|bar", ""}, 165 {"/bar", rune('/'), "", "bar"}, 166 {"//bar", rune('/'), "", "/bar"}, 167 {"/", rune('/'), "", ""}, 168 {"/bar", rune('/'), "", "bar"}, 169 {"foo/bar/baz", rune('/'), "foo", "bar/baz"}, 170 {"foo/bar//", rune('/'), "foo", "bar//"}, 171 } 172 173 for _, tc := range testCases { 174 before, after := splitOn(tc.Input, tc.Split) 175 assert.ItsEqual(t, tc.ExpectedBefore, before, fmt.Sprintf("invalid before: %s", tc.Input)) 176 assert.ItsEqual(t, tc.ExpectedAfter, after, fmt.Sprintf("invalid after: %s", tc.Input)) 177 } 178 }