...

Source file src/github.com/grpc-ecosystem/grpc-gateway/runtime/pattern_test.go

Documentation: github.com/grpc-ecosystem/grpc-gateway/runtime

     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  			// op code out of bound
   134  			ops: []int{-1, anything},
   135  		},
   136  		{
   137  			// op code out of bound
   138  			ops: []int{int(utilities.OpEnd), 0},
   139  		},
   140  		{
   141  			// odd number of items
   142  			ops: []int{int(utilities.OpPush)},
   143  		},
   144  		{
   145  			// negative index
   146  			ops:  []int{int(utilities.OpLitPush), -1},
   147  			pool: []string{"abc"},
   148  		},
   149  		{
   150  			// index out of bound
   151  			ops:  []int{int(utilities.OpLitPush), 1},
   152  			pool: []string{"abc"},
   153  		},
   154  		{
   155  			// negative # of segments
   156  			ops:  []int{int(utilities.OpConcatN), -1},
   157  			pool: []string{"abc"},
   158  		},
   159  		{
   160  			// negative index
   161  			ops:  []int{int(utilities.OpCapture), -1},
   162  			pool: []string{"abc"},
   163  		},
   164  		{
   165  			// index out of bound
   166  			ops:  []int{int(utilities.OpCapture), 1},
   167  			pool: []string{"abc"},
   168  		},
   169  		{
   170  			// pushM appears twice
   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