1 package httprule
2
3 import (
4 "errors"
5 "fmt"
6 "reflect"
7 "testing"
8
9 "google.golang.org/grpc/grpclog"
10 )
11
12 func TestTokenize(t *testing.T) {
13 for _, spec := range []struct {
14 src string
15 tokens []string
16 verb string
17 }{
18 {
19 src: "",
20 tokens: []string{eof},
21 },
22 {
23 src: "v1",
24 tokens: []string{"v1", eof},
25 },
26 {
27 src: "v1/b",
28 tokens: []string{"v1", "/", "b", eof},
29 },
30 {
31 src: "v1/endpoint/*",
32 tokens: []string{"v1", "/", "endpoint", "/", "*", eof},
33 },
34 {
35 src: "v1/endpoint/**",
36 tokens: []string{"v1", "/", "endpoint", "/", "**", eof},
37 },
38 {
39 src: "v1/b/{bucket_name=*}",
40 tokens: []string{
41 "v1", "/",
42 "b", "/",
43 "{", "bucket_name", "=", "*", "}",
44 eof,
45 },
46 },
47 {
48 src: "v1/b/{bucket_name=buckets/*}",
49 tokens: []string{
50 "v1", "/",
51 "b", "/",
52 "{", "bucket_name", "=", "buckets", "/", "*", "}",
53 eof,
54 },
55 },
56 {
57 src: "v1/b/{bucket_name=buckets/*}/o",
58 tokens: []string{
59 "v1", "/",
60 "b", "/",
61 "{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
62 "o",
63 eof,
64 },
65 },
66 {
67 src: "v1/b/{bucket_name=buckets/*}/o/{name}",
68 tokens: []string{
69 "v1", "/",
70 "b", "/",
71 "{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
72 "o", "/", "{", "name", "}",
73 eof,
74 },
75 },
76 {
77 src: "v1/a=b&c=d;e=f:g/endpoint.rdf",
78 tokens: []string{
79 "v1", "/",
80 "a=b&c=d;e=f:g", "/",
81 "endpoint.rdf",
82 eof,
83 },
84 },
85 {
86 src: "v1/a/{endpoint}:a",
87 tokens: []string{
88 "v1", "/",
89 "a", "/",
90 "{", "endpoint", "}",
91 eof,
92 },
93 verb: "a",
94 },
95 {
96 src: "v1/a/{endpoint}:b:c",
97 tokens: []string{
98 "v1", "/",
99 "a", "/",
100 "{", "endpoint", "}",
101 eof,
102 },
103 verb: "b:c",
104 },
105 } {
106 tokens, verb := tokenize(spec.src)
107 if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
108 t.Errorf("tokenize(%q) = %q, _; want %q, _", spec.src, got, want)
109 }
110
111 switch {
112 case spec.verb != "":
113 if got, want := verb, spec.verb; !reflect.DeepEqual(got, want) {
114 t.Errorf("tokenize(%q) = %q, _; want %q, _", spec.src, got, want)
115 }
116
117 default:
118 if got, want := verb, ""; got != want {
119 t.Errorf("tokenize(%q) = _, %q; want _, %q", spec.src, got, want)
120 }
121
122 src := fmt.Sprintf("%s:%s", spec.src, "LOCK")
123 tokens, verb = tokenize(src)
124 if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
125 t.Errorf("tokenize(%q) = %q, _; want %q, _", src, got, want)
126 }
127 if got, want := verb, "LOCK"; got != want {
128 t.Errorf("tokenize(%q) = _, %q; want _, %q", src, got, want)
129 }
130 }
131 }
132 }
133
134 func TestParseSegments(t *testing.T) {
135 for _, spec := range []struct {
136 tokens []string
137 want []segment
138 }{
139 {
140 tokens: []string{eof},
141 want: []segment{
142 literal(eof),
143 },
144 },
145 {
146
147
148 tokens: []string{eof, "v1", eof},
149 want: []segment{
150 literal(eof),
151 },
152 },
153 {
154 tokens: []string{"v1", eof},
155 want: []segment{
156 literal("v1"),
157 },
158 },
159 {
160 tokens: []string{"/", eof},
161 want: []segment{
162 wildcard{},
163 },
164 },
165 {
166 tokens: []string{"-._~!$&'()*+,;=:@", eof},
167 want: []segment{
168 literal("-._~!$&'()*+,;=:@"),
169 },
170 },
171 {
172 tokens: []string{"%e7%ac%ac%e4%b8%80%e7%89%88", eof},
173 want: []segment{
174 literal("%e7%ac%ac%e4%b8%80%e7%89%88"),
175 },
176 },
177 {
178 tokens: []string{"v1", "/", "*", eof},
179 want: []segment{
180 literal("v1"),
181 wildcard{},
182 },
183 },
184 {
185 tokens: []string{"v1", "/", "**", eof},
186 want: []segment{
187 literal("v1"),
188 deepWildcard{},
189 },
190 },
191 {
192 tokens: []string{"{", "name", "}", eof},
193 want: []segment{
194 variable{
195 path: "name",
196 segments: []segment{
197 wildcard{},
198 },
199 },
200 },
201 },
202 {
203 tokens: []string{"{", "name", "=", "*", "}", eof},
204 want: []segment{
205 variable{
206 path: "name",
207 segments: []segment{
208 wildcard{},
209 },
210 },
211 },
212 },
213 {
214 tokens: []string{"{", "field", ".", "nested", ".", "nested2", "=", "*", "}", eof},
215 want: []segment{
216 variable{
217 path: "field.nested.nested2",
218 segments: []segment{
219 wildcard{},
220 },
221 },
222 },
223 },
224 {
225 tokens: []string{"{", "name", "=", "a", "/", "b", "/", "*", "}", eof},
226 want: []segment{
227 variable{
228 path: "name",
229 segments: []segment{
230 literal("a"),
231 literal("b"),
232 wildcard{},
233 },
234 },
235 },
236 },
237 {
238 tokens: []string{
239 "v1", "/",
240 "{",
241 "name", ".", "nested", ".", "nested2",
242 "=",
243 "a", "/", "b", "/", "*",
244 "}", "/",
245 "o", "/",
246 "{",
247 "another_name",
248 "=",
249 "a", "/", "b", "/", "*", "/", "c",
250 "}", "/",
251 "**",
252 eof,
253 },
254 want: []segment{
255 literal("v1"),
256 variable{
257 path: "name.nested.nested2",
258 segments: []segment{
259 literal("a"),
260 literal("b"),
261 wildcard{},
262 },
263 },
264 literal("o"),
265 variable{
266 path: "another_name",
267 segments: []segment{
268 literal("a"),
269 literal("b"),
270 wildcard{},
271 literal("c"),
272 },
273 },
274 deepWildcard{},
275 },
276 },
277 } {
278 p := parser{tokens: spec.tokens}
279 segs, err := p.topLevelSegments()
280 if err != nil {
281 t.Errorf("parser{%q}.segments() failed with %v; want success", spec.tokens, err)
282 continue
283 }
284 if got, want := segs, spec.want; !reflect.DeepEqual(got, want) {
285 t.Errorf("parser{%q}.segments() = %#v; want %#v", spec.tokens, got, want)
286 }
287 if got := p.tokens; len(got) > 0 {
288 t.Errorf("p.tokens = %q; want []; spec.tokens=%q", got, spec.tokens)
289 }
290 }
291 }
292
293 func TestParse(t *testing.T) {
294 for _, spec := range []struct {
295 input string
296 wantFields []string
297 wantOpCodes []int
298 wantPool []string
299 wantVerb string
300 }{
301 {
302 input: "/v1/{name}:bla:baa",
303 wantFields: []string{
304 "name",
305 },
306 wantPool: []string{"v1", "name"},
307 wantVerb: "bla:baa",
308 },
309 {
310 input: "/v1/{name}:",
311 wantFields: []string{
312 "name",
313 },
314 wantPool: []string{"v1", "name"},
315 wantVerb: "",
316 },
317 {
318 input: "/v1/{name=segment/wi:th}",
319 wantFields: []string{
320 "name",
321 },
322 wantPool: []string{"v1", "segment", "wi:th", "name"},
323 wantVerb: "",
324 },
325 } {
326 f, err := Parse(spec.input)
327 if err != nil {
328 t.Errorf("Parse(%q) failed with %v; want success", spec.input, err)
329 continue
330 }
331 tmpl := f.Compile()
332 if !reflect.DeepEqual(tmpl.Fields, spec.wantFields) {
333 t.Errorf("Parse(%q).Fields = %#v; want %#v", spec.input, tmpl.Fields, spec.wantFields)
334 }
335 if !reflect.DeepEqual(tmpl.Pool, spec.wantPool) {
336 t.Errorf("Parse(%q).Pool = %#v; want %#v", spec.input, tmpl.Pool, spec.wantPool)
337 }
338 if tmpl.Template != spec.input {
339 t.Errorf("Parse(%q).Template = %q; want %q", spec.input, tmpl.Template, spec.input)
340 }
341 if tmpl.Verb != spec.wantVerb {
342 t.Errorf("Parse(%q).Verb = %q; want %q", spec.input, tmpl.Verb, spec.wantVerb)
343 }
344 }
345 }
346
347 func TestParseError(t *testing.T) {
348 for _, spec := range []struct {
349 input string
350 wantError error
351 }{
352 {
353 input: "v1/{name}",
354 wantError: InvalidTemplateError{
355 tmpl: "v1/{name}",
356 msg: "no leading /",
357 },
358 },
359 } {
360 _, err := Parse(spec.input)
361 if err == nil {
362 t.Errorf("Parse(%q) unexpectedly did not fail", spec.input)
363 continue
364 }
365 if !errors.Is(err, spec.wantError) {
366 t.Errorf("Error did not match expected error: got %v wanted %v", err, spec.wantError)
367 }
368 }
369 }
370
371 func TestParseSegmentsWithErrors(t *testing.T) {
372 for _, spec := range []struct {
373 tokens []string
374 }{
375 {
376
377 tokens: []string{"//", eof},
378 },
379 {
380
381 tokens: []string{"a?b", eof},
382 },
383 {
384
385 tokens: []string{"%", eof},
386 },
387 {
388
389 tokens: []string{"%2", eof},
390 },
391 {
392
393 tokens: []string{"a%2z", eof},
394 },
395 {
396
397 tokens: []string{"{", "name", eof},
398 },
399 {
400
401 tokens: []string{"{", "name", "=", eof},
402 },
403 {
404
405 tokens: []string{"{", "name", "=", "*", eof},
406 },
407 {
408
409 tokens: []string{"{", "name", ".", "}", eof},
410 },
411 {
412
413 tokens: []string{"{", "name", ".", ".", "nested", "}", eof},
414 },
415 {
416
417 tokens: []string{"{", "field-name", "}", eof},
418 },
419 {
420
421 tokens: []string{"v1", "endpoint", eof},
422 },
423 {
424
425 tokens: []string{"v1", "{", "name", "}", eof},
426 },
427 } {
428 p := parser{tokens: spec.tokens}
429 segs, err := p.topLevelSegments()
430 if err == nil {
431 t.Errorf("parser{%q}.segments() succeeded; want InvalidTemplateError; accepted %#v", spec.tokens, segs)
432 continue
433 }
434 if grpclog.V(1) {
435 grpclog.Info(err)
436 }
437 }
438 }
439
View as plain text