* util/router: sync from github * config: add watcher interface Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
		
			
				
	
	
		
			361 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			361 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package router
 | |
| 
 | |
| // download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/parse_test.go
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/unistack-org/micro/v3/logger"
 | |
| )
 | |
| 
 | |
| func TestTokenize(t *testing.T) {
 | |
| 	for _, spec := range []struct {
 | |
| 		src    string
 | |
| 		tokens []string
 | |
| 		verb   string
 | |
| 	}{
 | |
| 		{
 | |
| 			src:    "",
 | |
| 			tokens: []string{eof},
 | |
| 		},
 | |
| 		{
 | |
| 			src:    "v1",
 | |
| 			tokens: []string{"v1", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			src:    "v1/b",
 | |
| 			tokens: []string{"v1", "/", "b", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			src:    "v1/endpoint/*",
 | |
| 			tokens: []string{"v1", "/", "endpoint", "/", "*", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			src:    "v1/endpoint/**",
 | |
| 			tokens: []string{"v1", "/", "endpoint", "/", "**", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			src: "v1/b/{bucket_name=*}",
 | |
| 			tokens: []string{
 | |
| 				"v1", "/",
 | |
| 				"b", "/",
 | |
| 				"{", "bucket_name", "=", "*", "}",
 | |
| 				eof,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			src: "v1/b/{bucket_name=buckets/*}",
 | |
| 			tokens: []string{
 | |
| 				"v1", "/",
 | |
| 				"b", "/",
 | |
| 				"{", "bucket_name", "=", "buckets", "/", "*", "}",
 | |
| 				eof,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			src: "v1/b/{bucket_name=buckets/*}/o",
 | |
| 			tokens: []string{
 | |
| 				"v1", "/",
 | |
| 				"b", "/",
 | |
| 				"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
 | |
| 				"o",
 | |
| 				eof,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			src: "v1/b/{bucket_name=buckets/*}/o/{name}",
 | |
| 			tokens: []string{
 | |
| 				"v1", "/",
 | |
| 				"b", "/",
 | |
| 				"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
 | |
| 				"o", "/", "{", "name", "}",
 | |
| 				eof,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			src: "v1/a=b&c=d;e=f:g/endpoint.rdf",
 | |
| 			tokens: []string{
 | |
| 				"v1", "/",
 | |
| 				"a=b&c=d;e=f:g", "/",
 | |
| 				"endpoint.rdf",
 | |
| 				eof,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			src: "v1/a/{endpoint}:a",
 | |
| 			tokens: []string{
 | |
| 				"v1", "/",
 | |
| 				"a", "/",
 | |
| 				"{", "endpoint", "}",
 | |
| 				eof,
 | |
| 			},
 | |
| 			verb: "a",
 | |
| 		},
 | |
| 		{
 | |
| 			src: "v1/a/{endpoint}:b:c",
 | |
| 			tokens: []string{
 | |
| 				"v1", "/",
 | |
| 				"a", "/",
 | |
| 				"{", "endpoint", "}",
 | |
| 				eof,
 | |
| 			},
 | |
| 			verb: "b:c",
 | |
| 		},
 | |
| 	} {
 | |
| 		tokens, verb := tokenize(spec.src)
 | |
| 		if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
 | |
| 			t.Errorf("tokenize(%q) = %q, _; want %q, _", spec.src, got, want)
 | |
| 		}
 | |
| 
 | |
| 		switch {
 | |
| 		case spec.verb != "":
 | |
| 			if got, want := verb, spec.verb; !reflect.DeepEqual(got, want) {
 | |
| 				t.Errorf("tokenize(%q) = %q, _; want %q, _", spec.src, got, want)
 | |
| 			}
 | |
| 
 | |
| 		default:
 | |
| 			if got, want := verb, ""; got != want {
 | |
| 				t.Errorf("tokenize(%q) = _, %q; want _, %q", spec.src, got, want)
 | |
| 			}
 | |
| 
 | |
| 			src := fmt.Sprintf("%s:%s", spec.src, "LOCK")
 | |
| 			tokens, verb = tokenize(src)
 | |
| 			if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
 | |
| 				t.Errorf("tokenize(%q) = %q, _; want %q, _", src, got, want)
 | |
| 			}
 | |
| 			if got, want := verb, "LOCK"; got != want {
 | |
| 				t.Errorf("tokenize(%q) = _, %q; want _, %q", src, got, want)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestParseSegments(t *testing.T) {
 | |
| 	for _, spec := range []struct {
 | |
| 		tokens []string
 | |
| 		want   []segment
 | |
| 	}{
 | |
| 		{
 | |
| 			tokens: []string{eof},
 | |
| 			want: []segment{
 | |
| 				literal(eof),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			// Note: this case will never arise as tokenize() will never return such a sequence of tokens
 | |
| 			// and even if it does it will be treated as [eof]
 | |
| 			tokens: []string{eof, "v1", eof},
 | |
| 			want: []segment{
 | |
| 				literal(eof),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{"v1", eof},
 | |
| 			want: []segment{
 | |
| 				literal("v1"),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{"/", eof},
 | |
| 			want: []segment{
 | |
| 				wildcard{},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{"-._~!$&'()*+,;=:@", eof},
 | |
| 			want: []segment{
 | |
| 				literal("-._~!$&'()*+,;=:@"),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{"%e7%ac%ac%e4%b8%80%e7%89%88", eof},
 | |
| 			want: []segment{
 | |
| 				literal("%e7%ac%ac%e4%b8%80%e7%89%88"),
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{"v1", "/", "*", eof},
 | |
| 			want: []segment{
 | |
| 				literal("v1"),
 | |
| 				wildcard{},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{"v1", "/", "**", eof},
 | |
| 			want: []segment{
 | |
| 				literal("v1"),
 | |
| 				deepWildcard{},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{"{", "name", "}", eof},
 | |
| 			want: []segment{
 | |
| 				variable{
 | |
| 					path: "name",
 | |
| 					segments: []segment{
 | |
| 						wildcard{},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{"{", "name", "=", "*", "}", eof},
 | |
| 			want: []segment{
 | |
| 				variable{
 | |
| 					path: "name",
 | |
| 					segments: []segment{
 | |
| 						wildcard{},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{"{", "field", ".", "nested", ".", "nested2", "=", "*", "}", eof},
 | |
| 			want: []segment{
 | |
| 				variable{
 | |
| 					path: "field.nested.nested2",
 | |
| 					segments: []segment{
 | |
| 						wildcard{},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{"{", "name", "=", "a", "/", "b", "/", "*", "}", eof},
 | |
| 			want: []segment{
 | |
| 				variable{
 | |
| 					path: "name",
 | |
| 					segments: []segment{
 | |
| 						literal("a"),
 | |
| 						literal("b"),
 | |
| 						wildcard{},
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			tokens: []string{
 | |
| 				"v1", "/",
 | |
| 				"{",
 | |
| 				"name", ".", "nested", ".", "nested2",
 | |
| 				"=",
 | |
| 				"a", "/", "b", "/", "*",
 | |
| 				"}", "/",
 | |
| 				"o", "/",
 | |
| 				"{",
 | |
| 				"another_name",
 | |
| 				"=",
 | |
| 				"a", "/", "b", "/", "*", "/", "c",
 | |
| 				"}", "/",
 | |
| 				"**",
 | |
| 				eof,
 | |
| 			},
 | |
| 			want: []segment{
 | |
| 				literal("v1"),
 | |
| 				variable{
 | |
| 					path: "name.nested.nested2",
 | |
| 					segments: []segment{
 | |
| 						literal("a"),
 | |
| 						literal("b"),
 | |
| 						wildcard{},
 | |
| 					},
 | |
| 				},
 | |
| 				literal("o"),
 | |
| 				variable{
 | |
| 					path: "another_name",
 | |
| 					segments: []segment{
 | |
| 						literal("a"),
 | |
| 						literal("b"),
 | |
| 						wildcard{},
 | |
| 						literal("c"),
 | |
| 					},
 | |
| 				},
 | |
| 				deepWildcard{},
 | |
| 			},
 | |
| 		},
 | |
| 	} {
 | |
| 		p := parser{tokens: spec.tokens}
 | |
| 		segs, err := p.topLevelSegments()
 | |
| 		if err != nil {
 | |
| 			t.Errorf("parser{%q}.segments() failed with %v; want success", spec.tokens, err)
 | |
| 			continue
 | |
| 		}
 | |
| 		if got, want := segs, spec.want; !reflect.DeepEqual(got, want) {
 | |
| 			t.Errorf("parser{%q}.segments() = %#v; want %#v", spec.tokens, got, want)
 | |
| 		}
 | |
| 		if got := p.tokens; len(got) > 0 {
 | |
| 			t.Errorf("p.tokens = %q; want []; spec.tokens=%q", got, spec.tokens)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestParseSegmentsWithErrors(t *testing.T) {
 | |
| 	for _, spec := range []struct {
 | |
| 		tokens []string
 | |
| 	}{
 | |
| 		{
 | |
| 			// double slash
 | |
| 			tokens: []string{"//", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// invalid literal
 | |
| 			tokens: []string{"a?b", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// invalid percent-encoding
 | |
| 			tokens: []string{"%", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// invalid percent-encoding
 | |
| 			tokens: []string{"%2", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// invalid percent-encoding
 | |
| 			tokens: []string{"a%2z", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// unterminated variable
 | |
| 			tokens: []string{"{", "name", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// unterminated variable
 | |
| 			tokens: []string{"{", "name", "=", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// unterminated variable
 | |
| 			tokens: []string{"{", "name", "=", "*", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// empty component in field path
 | |
| 			tokens: []string{"{", "name", ".", "}", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// empty component in field path
 | |
| 			tokens: []string{"{", "name", ".", ".", "nested", "}", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// invalid character in identifier
 | |
| 			tokens: []string{"{", "field-name", "}", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// no slash between segments
 | |
| 			tokens: []string{"v1", "endpoint", eof},
 | |
| 		},
 | |
| 		{
 | |
| 			// no slash between segments
 | |
| 			tokens: []string{"v1", "{", "name", "}", eof},
 | |
| 		},
 | |
| 	} {
 | |
| 		p := parser{tokens: spec.tokens}
 | |
| 		segs, err := p.topLevelSegments()
 | |
| 		if err == nil {
 | |
| 			t.Errorf("parser{%q}.segments() succeeded; want InvalidTemplateError; accepted %#v", spec.tokens, segs)
 | |
| 			continue
 | |
| 		}
 | |
| 		logger.Info(context.TODO(), err.Error())
 | |
| 	}
 | |
| }
 |