1 package runtime
2
3 import (
4 "fmt"
5 "reflect"
6 "strings"
7 "testing"
8
9 "github.com/grpc-ecosystem/grpc-gateway/utilities"
10 )
11
12 const (
13 validVersion = 1
14 anything = 0
15 )
16
17 func TestNewPattern(t *testing.T) {
18 for _, spec := range []struct {
19 ops []int
20 pool []string
21 verb string
22
23 stackSizeWant, tailLenWant int
24 }{
25 {},
26 {
27 ops: []int{int(utilities.OpNop), anything},
28 stackSizeWant: 0,
29 tailLenWant: 0,
30 },
31 {
32 ops: []int{int(utilities.OpPush), anything},
33 stackSizeWant: 1,
34 tailLenWant: 0,
35 },
36 {
37 ops: []int{int(utilities.OpLitPush), 0},
38 pool: []string{"abc"},
39 stackSizeWant: 1,
40 tailLenWant: 0,
41 },
42 {
43 ops: []int{int(utilities.OpPushM), anything},
44 stackSizeWant: 1,
45 tailLenWant: 0,
46 },
47 {
48 ops: []int{
49 int(utilities.OpPush), anything,
50 int(utilities.OpConcatN), 1,
51 },
52 stackSizeWant: 1,
53 tailLenWant: 0,
54 },
55 {
56 ops: []int{
57 int(utilities.OpPush), anything,
58 int(utilities.OpConcatN), 1,
59 int(utilities.OpCapture), 0,
60 },
61 pool: []string{"abc"},
62 stackSizeWant: 1,
63 tailLenWant: 0,
64 },
65 {
66 ops: []int{
67 int(utilities.OpPush), anything,
68 int(utilities.OpLitPush), 0,
69 int(utilities.OpLitPush), 1,
70 int(utilities.OpPushM), anything,
71 int(utilities.OpConcatN), 2,
72 int(utilities.OpCapture), 2,
73 },
74 pool: []string{"lit1", "lit2", "var1"},
75 stackSizeWant: 4,
76 tailLenWant: 0,
77 },
78 {
79 ops: []int{
80 int(utilities.OpPushM), anything,
81 int(utilities.OpConcatN), 1,
82 int(utilities.OpCapture), 2,
83 int(utilities.OpLitPush), 0,
84 int(utilities.OpLitPush), 1,
85 },
86 pool: []string{"lit1", "lit2", "var1"},
87 stackSizeWant: 2,
88 tailLenWant: 2,
89 },
90 {
91 ops: []int{
92 int(utilities.OpLitPush), 0,
93 int(utilities.OpLitPush), 1,
94 int(utilities.OpPushM), anything,
95 int(utilities.OpLitPush), 2,
96 int(utilities.OpConcatN), 3,
97 int(utilities.OpLitPush), 3,
98 int(utilities.OpCapture), 4,
99 },
100 pool: []string{"lit1", "lit2", "lit3", "lit4", "var1"},
101 stackSizeWant: 4,
102 tailLenWant: 2,
103 },
104 {
105 ops: []int{int(utilities.OpLitPush), 0},
106 pool: []string{"abc"},
107 verb: "LOCK",
108 stackSizeWant: 1,
109 tailLenWant: 0,
110 },
111 } {
112 pat, err := NewPattern(validVersion, spec.ops, spec.pool, spec.verb)
113 if err != nil {
114 t.Errorf("NewPattern(%d, %v, %q, %q) failed with %v; want success", validVersion, spec.ops, spec.pool, spec.verb, err)
115 continue
116 }
117 if got, want := pat.stacksize, spec.stackSizeWant; got != want {
118 t.Errorf("pat.stacksize = %d; want %d", got, want)
119 }
120 if got, want := pat.tailLen, spec.tailLenWant; got != want {
121 t.Errorf("pat.stacksize = %d; want %d", got, want)
122 }
123 }
124 }
125
126 func TestNewPatternWithWrongOp(t *testing.T) {
127 for _, spec := range []struct {
128 ops []int
129 pool []string
130 verb string
131 }{
132 {
133
134 ops: []int{-1, anything},
135 },
136 {
137
138 ops: []int{int(utilities.OpEnd), 0},
139 },
140 {
141
142 ops: []int{int(utilities.OpPush)},
143 },
144 {
145
146 ops: []int{int(utilities.OpLitPush), -1},
147 pool: []string{"abc"},
148 },
149 {
150
151 ops: []int{int(utilities.OpLitPush), 1},
152 pool: []string{"abc"},
153 },
154 {
155
156 ops: []int{int(utilities.OpConcatN), -1},
157 pool: []string{"abc"},
158 },
159 {
160
161 ops: []int{int(utilities.OpCapture), -1},
162 pool: []string{"abc"},
163 },
164 {
165
166 ops: []int{int(utilities.OpCapture), 1},
167 pool: []string{"abc"},
168 },
169 {
170
171 ops: []int{
172 int(utilities.OpPushM), anything,
173 int(utilities.OpLitPush), 0,
174 int(utilities.OpPushM), anything,
175 },
176 pool: []string{"abc"},
177 },
178 } {
179 _, err := NewPattern(validVersion, spec.ops, spec.pool, spec.verb)
180 if err == nil {
181 t.Errorf("NewPattern(%d, %v, %q, %q) succeeded; want failure with %v", validVersion, spec.ops, spec.pool, spec.verb, ErrInvalidPattern)
182 continue
183 }
184 if err != ErrInvalidPattern {
185 t.Errorf("NewPattern(%d, %v, %q, %q) failed with %v; want failure with %v", validVersion, spec.ops, spec.pool, spec.verb, err, ErrInvalidPattern)
186 continue
187 }
188 }
189 }
190
191 func TestNewPatternWithStackUnderflow(t *testing.T) {
192 for _, spec := range []struct {
193 ops []int
194 pool []string
195 verb string
196 }{
197 {
198 ops: []int{int(utilities.OpConcatN), 1},
199 },
200 {
201 ops: []int{int(utilities.OpCapture), 0},
202 pool: []string{"abc"},
203 },
204 } {
205 _, err := NewPattern(validVersion, spec.ops, spec.pool, spec.verb)
206 if err == nil {
207 t.Errorf("NewPattern(%d, %v, %q, %q) succeeded; want failure with %v", validVersion, spec.ops, spec.pool, spec.verb, ErrInvalidPattern)
208 continue
209 }
210 if err != ErrInvalidPattern {
211 t.Errorf("NewPattern(%d, %v, %q, %q) failed with %v; want failure with %v", validVersion, spec.ops, spec.pool, spec.verb, err, ErrInvalidPattern)
212 continue
213 }
214 }
215 }
216
217 func TestMatch(t *testing.T) {
218 for _, spec := range []struct {
219 ops []int
220 pool []string
221 verb string
222
223 match []string
224 notMatch []string
225 }{
226 {
227 match: []string{""},
228 notMatch: []string{"example"},
229 },
230 {
231 ops: []int{int(utilities.OpNop), anything},
232 match: []string{""},
233 notMatch: []string{"example", "path/to/example"},
234 },
235 {
236 ops: []int{int(utilities.OpPush), anything},
237 match: []string{"abc", "def"},
238 notMatch: []string{"", "abc/def"},
239 },
240 {
241 ops: []int{int(utilities.OpLitPush), 0},
242 pool: []string{"v1"},
243 match: []string{"v1"},
244 notMatch: []string{"", "v2"},
245 },
246 {
247 ops: []int{int(utilities.OpPushM), anything},
248 match: []string{"", "abc", "abc/def", "abc/def/ghi"},
249 },
250 {
251 ops: []int{
252 int(utilities.OpPushM), anything,
253 int(utilities.OpLitPush), 0,
254 },
255 pool: []string{"tail"},
256 match: []string{"tail", "abc/tail", "abc/def/tail"},
257 notMatch: []string{
258 "", "abc", "abc/def",
259 "tail/extra", "abc/tail/extra", "abc/def/tail/extra",
260 },
261 },
262 {
263 ops: []int{
264 int(utilities.OpLitPush), 0,
265 int(utilities.OpLitPush), 1,
266 int(utilities.OpPush), anything,
267 int(utilities.OpConcatN), 1,
268 int(utilities.OpCapture), 2,
269 },
270 pool: []string{"v1", "bucket", "name"},
271 match: []string{"v1/bucket/my-bucket", "v1/bucket/our-bucket"},
272 notMatch: []string{
273 "",
274 "v1",
275 "v1/bucket",
276 "v2/bucket/my-bucket",
277 "v1/pubsub/my-topic",
278 },
279 },
280 {
281 ops: []int{
282 int(utilities.OpLitPush), 0,
283 int(utilities.OpLitPush), 1,
284 int(utilities.OpPushM), anything,
285 int(utilities.OpConcatN), 2,
286 int(utilities.OpCapture), 2,
287 },
288 pool: []string{"v1", "o", "name"},
289 match: []string{
290 "v1/o",
291 "v1/o/my-bucket",
292 "v1/o/our-bucket",
293 "v1/o/my-bucket/dir",
294 "v1/o/my-bucket/dir/dir2",
295 "v1/o/my-bucket/dir/dir2/obj",
296 },
297 notMatch: []string{
298 "",
299 "v1",
300 "v2/o/my-bucket",
301 "v1/b/my-bucket",
302 },
303 },
304 {
305 ops: []int{
306 int(utilities.OpLitPush), 0,
307 int(utilities.OpLitPush), 1,
308 int(utilities.OpPush), anything,
309 int(utilities.OpConcatN), 2,
310 int(utilities.OpCapture), 2,
311 int(utilities.OpLitPush), 3,
312 int(utilities.OpPush), anything,
313 int(utilities.OpConcatN), 1,
314 int(utilities.OpCapture), 4,
315 },
316 pool: []string{"v2", "b", "name", "o", "oname"},
317 match: []string{
318 "v2/b/my-bucket/o/obj",
319 "v2/b/our-bucket/o/obj",
320 "v2/b/my-bucket/o/dir",
321 },
322 notMatch: []string{
323 "",
324 "v2",
325 "v2/b",
326 "v2/b/my-bucket",
327 "v2/b/my-bucket/o",
328 },
329 },
330 {
331 ops: []int{int(utilities.OpLitPush), 0},
332 pool: []string{"v1"},
333 verb: "LOCK",
334 match: []string{"v1:LOCK"},
335 notMatch: []string{"v1", "LOCK"},
336 },
337 } {
338 pat, err := NewPattern(validVersion, spec.ops, spec.pool, spec.verb)
339 if err != nil {
340 t.Errorf("NewPattern(%d, %v, %q, %q) failed with %v; want success", validVersion, spec.ops, spec.pool, spec.verb, err)
341 continue
342 }
343
344 for _, path := range spec.match {
345 _, err = pat.Match(segments(path))
346 if err != nil {
347 t.Errorf("pat.Match(%q) failed with %v; want success; pattern = (%v, %q)", path, err, spec.ops, spec.pool)
348 }
349 }
350
351 for _, path := range spec.notMatch {
352 _, err = pat.Match(segments(path))
353 if err == nil {
354 t.Errorf("pat.Match(%q) succeeded; want failure with %v; pattern = (%v, %q)", path, ErrNotMatch, spec.ops, spec.pool)
355 continue
356 }
357 if err != ErrNotMatch {
358 t.Errorf("pat.Match(%q) failed with %v; want failure with %v; pattern = (%v, %q)", spec.notMatch, err, ErrNotMatch, spec.ops, spec.pool)
359 }
360 }
361 }
362 }
363
364 func TestMatchWithBinding(t *testing.T) {
365 for _, spec := range []struct {
366 ops []int
367 pool []string
368 path string
369 verb string
370
371 want map[string]string
372 }{
373 {
374 want: make(map[string]string),
375 },
376 {
377 ops: []int{int(utilities.OpNop), anything},
378 want: make(map[string]string),
379 },
380 {
381 ops: []int{int(utilities.OpPush), anything},
382 path: "abc",
383 want: make(map[string]string),
384 },
385 {
386 ops: []int{int(utilities.OpPush), anything},
387 verb: "LOCK",
388 path: "abc:LOCK",
389 want: make(map[string]string),
390 },
391 {
392 ops: []int{int(utilities.OpLitPush), 0},
393 pool: []string{"endpoint"},
394 path: "endpoint",
395 want: make(map[string]string),
396 },
397 {
398 ops: []int{int(utilities.OpPushM), anything},
399 path: "abc/def/ghi",
400 want: make(map[string]string),
401 },
402 {
403 ops: []int{
404 int(utilities.OpLitPush), 0,
405 int(utilities.OpLitPush), 1,
406 int(utilities.OpPush), anything,
407 int(utilities.OpConcatN), 1,
408 int(utilities.OpCapture), 2,
409 },
410 pool: []string{"v1", "bucket", "name"},
411 path: "v1/bucket/my-bucket",
412 want: map[string]string{
413 "name": "my-bucket",
414 },
415 },
416 {
417 ops: []int{
418 int(utilities.OpLitPush), 0,
419 int(utilities.OpLitPush), 1,
420 int(utilities.OpPush), anything,
421 int(utilities.OpConcatN), 1,
422 int(utilities.OpCapture), 2,
423 },
424 pool: []string{"v1", "bucket", "name"},
425 verb: "LOCK",
426 path: "v1/bucket/my-bucket:LOCK",
427 want: map[string]string{
428 "name": "my-bucket",
429 },
430 },
431 {
432 ops: []int{
433 int(utilities.OpLitPush), 0,
434 int(utilities.OpLitPush), 1,
435 int(utilities.OpPushM), anything,
436 int(utilities.OpConcatN), 2,
437 int(utilities.OpCapture), 2,
438 },
439 pool: []string{"v1", "o", "name"},
440 path: "v1/o/my-bucket/dir/dir2/obj",
441 want: map[string]string{
442 "name": "o/my-bucket/dir/dir2/obj",
443 },
444 },
445 {
446 ops: []int{
447 int(utilities.OpLitPush), 0,
448 int(utilities.OpLitPush), 1,
449 int(utilities.OpPushM), anything,
450 int(utilities.OpLitPush), 2,
451 int(utilities.OpConcatN), 3,
452 int(utilities.OpCapture), 4,
453 int(utilities.OpLitPush), 3,
454 },
455 pool: []string{"v1", "o", ".ext", "tail", "name"},
456 path: "v1/o/my-bucket/dir/dir2/obj/.ext/tail",
457 want: map[string]string{
458 "name": "o/my-bucket/dir/dir2/obj/.ext",
459 },
460 },
461 {
462 ops: []int{
463 int(utilities.OpLitPush), 0,
464 int(utilities.OpLitPush), 1,
465 int(utilities.OpPush), anything,
466 int(utilities.OpConcatN), 2,
467 int(utilities.OpCapture), 2,
468 int(utilities.OpLitPush), 3,
469 int(utilities.OpPush), anything,
470 int(utilities.OpConcatN), 1,
471 int(utilities.OpCapture), 4,
472 },
473 pool: []string{"v2", "b", "name", "o", "oname"},
474 path: "v2/b/my-bucket/o/obj",
475 want: map[string]string{
476 "name": "b/my-bucket",
477 "oname": "obj",
478 },
479 },
480 } {
481 pat, err := NewPattern(validVersion, spec.ops, spec.pool, spec.verb)
482 if err != nil {
483 t.Errorf("NewPattern(%d, %v, %q, %q) failed with %v; want success", validVersion, spec.ops, spec.pool, spec.verb, err)
484 continue
485 }
486
487 got, err := pat.Match(segments(spec.path))
488 if err != nil {
489 t.Errorf("pat.Match(%q) failed with %v; want success; pattern = (%v, %q)", spec.path, err, spec.ops, spec.pool)
490 }
491 if !reflect.DeepEqual(got, spec.want) {
492 t.Errorf("pat.Match(%q) = %q; want %q; pattern = (%v, %q)", spec.path, got, spec.want, spec.ops, spec.pool)
493 }
494 }
495 }
496
497 func segments(path string) (components []string, verb string) {
498 if path == "" {
499 return nil, ""
500 }
501 components = strings.Split(path, "/")
502 l := len(components)
503 c := components[l-1]
504 if idx := strings.LastIndex(c, ":"); idx >= 0 {
505 components[l-1], verb = c[:idx], c[idx+1:]
506 }
507 return components, verb
508 }
509
510 func TestPatternString(t *testing.T) {
511 for _, spec := range []struct {
512 ops []int
513 pool []string
514
515 want string
516 }{
517 {
518 want: "/",
519 },
520 {
521 ops: []int{int(utilities.OpNop), anything},
522 want: "/",
523 },
524 {
525 ops: []int{int(utilities.OpPush), anything},
526 want: "/*",
527 },
528 {
529 ops: []int{int(utilities.OpLitPush), 0},
530 pool: []string{"endpoint"},
531 want: "/endpoint",
532 },
533 {
534 ops: []int{int(utilities.OpPushM), anything},
535 want: "/**",
536 },
537 {
538 ops: []int{
539 int(utilities.OpPush), anything,
540 int(utilities.OpConcatN), 1,
541 },
542 want: "/*",
543 },
544 {
545 ops: []int{
546 int(utilities.OpPush), anything,
547 int(utilities.OpConcatN), 1,
548 int(utilities.OpCapture), 0,
549 },
550 pool: []string{"name"},
551 want: "/{name=*}",
552 },
553 {
554 ops: []int{
555 int(utilities.OpLitPush), 0,
556 int(utilities.OpLitPush), 1,
557 int(utilities.OpPush), anything,
558 int(utilities.OpConcatN), 2,
559 int(utilities.OpCapture), 2,
560 int(utilities.OpLitPush), 3,
561 int(utilities.OpPushM), anything,
562 int(utilities.OpLitPush), 4,
563 int(utilities.OpConcatN), 3,
564 int(utilities.OpCapture), 6,
565 int(utilities.OpLitPush), 5,
566 },
567 pool: []string{"v1", "buckets", "bucket_name", "objects", ".ext", "tail", "name"},
568 want: "/v1/{bucket_name=buckets/*}/{name=objects/**/.ext}/tail",
569 },
570 } {
571 p, err := NewPattern(validVersion, spec.ops, spec.pool, "")
572 if err != nil {
573 t.Errorf("NewPattern(%d, %v, %q, %q) failed with %v; want success", validVersion, spec.ops, spec.pool, "", err)
574 continue
575 }
576 if got, want := p.String(), spec.want; got != want {
577 t.Errorf("%#v.String() = %q; want %q", p, got, want)
578 }
579
580 verb := "LOCK"
581 p, err = NewPattern(validVersion, spec.ops, spec.pool, verb)
582 if err != nil {
583 t.Errorf("NewPattern(%d, %v, %q, %q) failed with %v; want success", validVersion, spec.ops, spec.pool, verb, err)
584 continue
585 }
586 if got, want := p.String(), fmt.Sprintf("%s:%s", spec.want, verb); got != want {
587 t.Errorf("%#v.String() = %q; want %q", p, got, want)
588 }
589 }
590 }
591
View as plain text