...

Source file src/github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule/parse_test.go

Documentation: github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule

     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  			// Note: this case will never arise as tokenize() will never return such a sequence of tokens
   147  			// and even if it does it will be treated as [eof]
   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  			// double slash
   377  			tokens: []string{"//", eof},
   378  		},
   379  		{
   380  			// invalid literal
   381  			tokens: []string{"a?b", eof},
   382  		},
   383  		{
   384  			// invalid percent-encoding
   385  			tokens: []string{"%", eof},
   386  		},
   387  		{
   388  			// invalid percent-encoding
   389  			tokens: []string{"%2", eof},
   390  		},
   391  		{
   392  			// invalid percent-encoding
   393  			tokens: []string{"a%2z", eof},
   394  		},
   395  		{
   396  			// unterminated variable
   397  			tokens: []string{"{", "name", eof},
   398  		},
   399  		{
   400  			// unterminated variable
   401  			tokens: []string{"{", "name", "=", eof},
   402  		},
   403  		{
   404  			// unterminated variable
   405  			tokens: []string{"{", "name", "=", "*", eof},
   406  		},
   407  		{
   408  			// empty component in field path
   409  			tokens: []string{"{", "name", ".", "}", eof},
   410  		},
   411  		{
   412  			// empty component in field path
   413  			tokens: []string{"{", "name", ".", ".", "nested", "}", eof},
   414  		},
   415  		{
   416  			// invalid character in identifier
   417  			tokens: []string{"{", "field-name", "}", eof},
   418  		},
   419  		{
   420  			// no slash between segments
   421  			tokens: []string{"v1", "endpoint", eof},
   422  		},
   423  		{
   424  			// no slash between segments
   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