storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/wildcard/match_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2015, 2016 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package wildcard_test
    18  
    19  import (
    20  	"testing"
    21  
    22  	"storj.io/minio/pkg/wildcard"
    23  )
    24  
    25  // TestMatch - Tests validate the logic of wild card matching.
    26  // `Match` supports '*' and '?' wildcards.
    27  // Sample usage: In resource matching for bucket policy validation.
    28  func TestMatch(t *testing.T) {
    29  	testCases := []struct {
    30  		pattern string
    31  		text    string
    32  		matched bool
    33  	}{
    34  		// Test case - 1.
    35  		// Test case with pattern "*". Expected to match any text.
    36  		{
    37  			pattern: "*",
    38  			text:    "s3:GetObject",
    39  			matched: true,
    40  		},
    41  		// Test case - 2.
    42  		// Test case with empty pattern. This only matches empty string.
    43  		{
    44  			pattern: "",
    45  			text:    "s3:GetObject",
    46  			matched: false,
    47  		},
    48  		// Test case - 3.
    49  		// Test case with empty pattern. This only matches empty string.
    50  		{
    51  			pattern: "",
    52  			text:    "",
    53  			matched: true,
    54  		},
    55  		// Test case - 4.
    56  		// Test case with single "*" at the end.
    57  		{
    58  			pattern: "s3:*",
    59  			text:    "s3:ListMultipartUploadParts",
    60  			matched: true,
    61  		},
    62  		// Test case - 5.
    63  		// Test case with a no "*". In this case the pattern and text should be the same.
    64  		{
    65  			pattern: "s3:ListBucketMultipartUploads",
    66  			text:    "s3:ListBucket",
    67  			matched: false,
    68  		},
    69  		// Test case - 6.
    70  		// Test case with a no "*". In this case the pattern and text should be the same.
    71  		{
    72  			pattern: "s3:ListBucket",
    73  			text:    "s3:ListBucket",
    74  			matched: true,
    75  		},
    76  		// Test case - 7.
    77  		// Test case with a no "*". In this case the pattern and text should be the same.
    78  		{
    79  			pattern: "s3:ListBucketMultipartUploads",
    80  			text:    "s3:ListBucketMultipartUploads",
    81  			matched: true,
    82  		},
    83  		// Test case - 8.
    84  		// Test case with pattern containing key name with a prefix. Should accept the same text without a "*".
    85  		{
    86  			pattern: "my-bucket/oo*",
    87  			text:    "my-bucket/oo",
    88  			matched: true,
    89  		},
    90  		// Test case - 9.
    91  		// Test case with "*" at the end of the pattern.
    92  		{
    93  			pattern: "my-bucket/In*",
    94  			text:    "my-bucket/India/Karnataka/",
    95  			matched: true,
    96  		},
    97  		// Test case - 10.
    98  		// Test case with prefixes shuffled.
    99  		// This should fail.
   100  		{
   101  			pattern: "my-bucket/In*",
   102  			text:    "my-bucket/Karnataka/India/",
   103  			matched: false,
   104  		},
   105  		// Test case - 11.
   106  		// Test case with text expanded to the wildcards in the pattern.
   107  		{
   108  			pattern: "my-bucket/In*/Ka*/Ban",
   109  			text:    "my-bucket/India/Karnataka/Ban",
   110  			matched: true,
   111  		},
   112  		// Test case - 12.
   113  		// Test case with the  keyname part is repeated as prefix several times.
   114  		// This is valid.
   115  		{
   116  			pattern: "my-bucket/In*/Ka*/Ban",
   117  			text:    "my-bucket/India/Karnataka/Ban/Ban/Ban/Ban/Ban",
   118  			matched: true,
   119  		},
   120  		// Test case - 13.
   121  		// Test case to validate that `*` can be expanded into multiple prefixes.
   122  		{
   123  			pattern: "my-bucket/In*/Ka*/Ban",
   124  			text:    "my-bucket/India/Karnataka/Area1/Area2/Area3/Ban",
   125  			matched: true,
   126  		},
   127  		// Test case - 14.
   128  		// Test case to validate that `*` can be expanded into multiple prefixes.
   129  		{
   130  			pattern: "my-bucket/In*/Ka*/Ban",
   131  			text:    "my-bucket/India/State1/State2/Karnataka/Area1/Area2/Area3/Ban",
   132  			matched: true,
   133  		},
   134  		// Test case - 15.
   135  		// Test case where the keyname part of the pattern is expanded in the text.
   136  		{
   137  			pattern: "my-bucket/In*/Ka*/Ban",
   138  			text:    "my-bucket/India/Karnataka/Bangalore",
   139  			matched: false,
   140  		},
   141  		// Test case - 16.
   142  		// Test case with prefixes and wildcard expanded for all "*".
   143  		{
   144  			pattern: "my-bucket/In*/Ka*/Ban*",
   145  			text:    "my-bucket/India/Karnataka/Bangalore",
   146  			matched: true,
   147  		},
   148  		// Test case - 17.
   149  		// Test case with keyname part being a wildcard in the pattern.
   150  		{
   151  			pattern: "my-bucket/*",
   152  			text:    "my-bucket/India",
   153  			matched: true,
   154  		},
   155  		// Test case - 18.
   156  		{
   157  			pattern: "my-bucket/oo*",
   158  			text:    "my-bucket/odo",
   159  			matched: false,
   160  		},
   161  
   162  		// Test case with pattern containing wildcard '?'.
   163  		// Test case - 19.
   164  		// "my-bucket?/" matches "my-bucket1/", "my-bucket2/", "my-bucket3" etc...
   165  		// doesn't match "mybucket/".
   166  		{
   167  			pattern: "my-bucket?/abc*",
   168  			text:    "mybucket/abc",
   169  			matched: false,
   170  		},
   171  		// Test case - 20.
   172  		{
   173  			pattern: "my-bucket?/abc*",
   174  			text:    "my-bucket1/abc",
   175  			matched: true,
   176  		},
   177  		// Test case - 21.
   178  		{
   179  			pattern: "my-?-bucket/abc*",
   180  			text:    "my--bucket/abc",
   181  			matched: false,
   182  		},
   183  		// Test case - 22.
   184  		{
   185  			pattern: "my-?-bucket/abc*",
   186  			text:    "my-1-bucket/abc",
   187  			matched: true,
   188  		},
   189  		// Test case - 23.
   190  		{
   191  			pattern: "my-?-bucket/abc*",
   192  			text:    "my-k-bucket/abc",
   193  			matched: true,
   194  		},
   195  		// Test case - 24.
   196  		{
   197  			pattern: "my??bucket/abc*",
   198  			text:    "mybucket/abc",
   199  			matched: false,
   200  		},
   201  		// Test case - 25.
   202  		{
   203  			pattern: "my??bucket/abc*",
   204  			text:    "my4abucket/abc",
   205  			matched: true,
   206  		},
   207  		// Test case - 26.
   208  		{
   209  			pattern: "my-bucket?abc*",
   210  			text:    "my-bucket/abc",
   211  			matched: true,
   212  		},
   213  		// Test case 27-28.
   214  		// '?' matches '/' too. (works with s3).
   215  		// This is because the namespace is considered flat.
   216  		// "abc?efg" matches both "abcdefg" and "abc/efg".
   217  		{
   218  			pattern: "my-bucket/abc?efg",
   219  			text:    "my-bucket/abcdefg",
   220  			matched: true,
   221  		},
   222  		{
   223  			pattern: "my-bucket/abc?efg",
   224  			text:    "my-bucket/abc/efg",
   225  			matched: true,
   226  		},
   227  		// Test case - 29.
   228  		{
   229  			pattern: "my-bucket/abc????",
   230  			text:    "my-bucket/abc",
   231  			matched: false,
   232  		},
   233  		// Test case - 30.
   234  		{
   235  			pattern: "my-bucket/abc????",
   236  			text:    "my-bucket/abcde",
   237  			matched: false,
   238  		},
   239  		// Test case - 31.
   240  		{
   241  			pattern: "my-bucket/abc????",
   242  			text:    "my-bucket/abcdefg",
   243  			matched: true,
   244  		},
   245  		// Test case 32-34.
   246  		// test case with no '*'.
   247  		{
   248  			pattern: "my-bucket/abc?",
   249  			text:    "my-bucket/abc",
   250  			matched: false,
   251  		},
   252  		{
   253  			pattern: "my-bucket/abc?",
   254  			text:    "my-bucket/abcd",
   255  			matched: true,
   256  		},
   257  		{
   258  			pattern: "my-bucket/abc?",
   259  			text:    "my-bucket/abcde",
   260  			matched: false,
   261  		},
   262  		// Test case 35.
   263  		{
   264  			pattern: "my-bucket/mnop*?",
   265  			text:    "my-bucket/mnop",
   266  			matched: false,
   267  		},
   268  		// Test case 36.
   269  		{
   270  			pattern: "my-bucket/mnop*?",
   271  			text:    "my-bucket/mnopqrst/mnopqr",
   272  			matched: true,
   273  		},
   274  		// Test case 37.
   275  		{
   276  			pattern: "my-bucket/mnop*?",
   277  			text:    "my-bucket/mnopqrst/mnopqrs",
   278  			matched: true,
   279  		},
   280  		// Test case 38.
   281  		{
   282  			pattern: "my-bucket/mnop*?",
   283  			text:    "my-bucket/mnop",
   284  			matched: false,
   285  		},
   286  		// Test case 39.
   287  		{
   288  			pattern: "my-bucket/mnop*?",
   289  			text:    "my-bucket/mnopq",
   290  			matched: true,
   291  		},
   292  		// Test case 40.
   293  		{
   294  			pattern: "my-bucket/mnop*?",
   295  			text:    "my-bucket/mnopqr",
   296  			matched: true,
   297  		},
   298  		// Test case 41.
   299  		{
   300  			pattern: "my-bucket/mnop*?and",
   301  			text:    "my-bucket/mnopqand",
   302  			matched: true,
   303  		},
   304  		// Test case 42.
   305  		{
   306  			pattern: "my-bucket/mnop*?and",
   307  			text:    "my-bucket/mnopand",
   308  			matched: false,
   309  		},
   310  		// Test case 43.
   311  		{
   312  			pattern: "my-bucket/mnop*?and",
   313  			text:    "my-bucket/mnopqand",
   314  			matched: true,
   315  		},
   316  		// Test case 44.
   317  		{
   318  			pattern: "my-bucket/mnop*?",
   319  			text:    "my-bucket/mn",
   320  			matched: false,
   321  		},
   322  		// Test case 45.
   323  		{
   324  			pattern: "my-bucket/mnop*?",
   325  			text:    "my-bucket/mnopqrst/mnopqrs",
   326  			matched: true,
   327  		},
   328  		// Test case 46.
   329  		{
   330  			pattern: "my-bucket/mnop*??",
   331  			text:    "my-bucket/mnopqrst",
   332  			matched: true,
   333  		},
   334  		// Test case 47.
   335  		{
   336  			pattern: "my-bucket/mnop*qrst",
   337  			text:    "my-bucket/mnopabcdegqrst",
   338  			matched: true,
   339  		},
   340  		// Test case 48.
   341  		{
   342  			pattern: "my-bucket/mnop*?and",
   343  			text:    "my-bucket/mnopqand",
   344  			matched: true,
   345  		},
   346  		// Test case 49.
   347  		{
   348  			pattern: "my-bucket/mnop*?and",
   349  			text:    "my-bucket/mnopand",
   350  			matched: false,
   351  		},
   352  		// Test case 50.
   353  		{
   354  			pattern: "my-bucket/mnop*?and?",
   355  			text:    "my-bucket/mnopqanda",
   356  			matched: true,
   357  		},
   358  		// Test case 51.
   359  		{
   360  			pattern: "my-bucket/mnop*?and",
   361  			text:    "my-bucket/mnopqanda",
   362  			matched: false,
   363  		},
   364  		// Test case 52.
   365  
   366  		{
   367  			pattern: "my-?-bucket/abc*",
   368  			text:    "my-bucket/mnopqanda",
   369  			matched: false,
   370  		},
   371  	}
   372  	// Iterating over the test cases, call the function under test and asert the output.
   373  	for i, testCase := range testCases {
   374  		actualResult := wildcard.Match(testCase.pattern, testCase.text)
   375  		if testCase.matched != actualResult {
   376  			t.Errorf("Test %d: Expected the result to be `%v`, but instead found it to be `%v`", i+1, testCase.matched, actualResult)
   377  		}
   378  	}
   379  }
   380  
   381  // TestMatchSimple - Tests validate the logic of wild card matching.
   382  // `MatchSimple` supports matching for only '*' in the pattern string.
   383  func TestMatchSimple(t *testing.T) {
   384  	testCases := []struct {
   385  		pattern string
   386  		text    string
   387  		matched bool
   388  	}{
   389  		// Test case - 1.
   390  		// Test case with pattern "*". Expected to match any text.
   391  		{
   392  			pattern: "*",
   393  			text:    "s3:GetObject",
   394  			matched: true,
   395  		},
   396  		// Test case - 2.
   397  		// Test case with empty pattern. This only matches empty string.
   398  		{
   399  			pattern: "",
   400  			text:    "s3:GetObject",
   401  			matched: false,
   402  		},
   403  		// Test case - 3.
   404  		// Test case with empty pattern. This only matches empty string.
   405  		{
   406  			pattern: "",
   407  			text:    "",
   408  			matched: true,
   409  		},
   410  		// Test case - 4.
   411  		// Test case with single "*" at the end.
   412  		{
   413  			pattern: "s3:*",
   414  			text:    "s3:ListMultipartUploadParts",
   415  			matched: true,
   416  		},
   417  		// Test case - 5.
   418  		// Test case with a no "*". In this case the pattern and text should be the same.
   419  		{
   420  			pattern: "s3:ListBucketMultipartUploads",
   421  			text:    "s3:ListBucket",
   422  			matched: false,
   423  		},
   424  		// Test case - 6.
   425  		// Test case with a no "*". In this case the pattern and text should be the same.
   426  		{
   427  			pattern: "s3:ListBucket",
   428  			text:    "s3:ListBucket",
   429  			matched: true,
   430  		},
   431  		// Test case - 7.
   432  		// Test case with a no "*". In this case the pattern and text should be the same.
   433  		{
   434  			pattern: "s3:ListBucketMultipartUploads",
   435  			text:    "s3:ListBucketMultipartUploads",
   436  			matched: true,
   437  		},
   438  		// Test case - 8.
   439  		// Test case with pattern containing key name with a prefix. Should accept the same text without a "*".
   440  		{
   441  			pattern: "my-bucket/oo*",
   442  			text:    "my-bucket/oo",
   443  			matched: true,
   444  		},
   445  		// Test case - 9.
   446  		// Test case with "*" at the end of the pattern.
   447  		{
   448  			pattern: "my-bucket/In*",
   449  			text:    "my-bucket/India/Karnataka/",
   450  			matched: true,
   451  		},
   452  		// Test case - 10.
   453  		// Test case with prefixes shuffled.
   454  		// This should fail.
   455  		{
   456  			pattern: "my-bucket/In*",
   457  			text:    "my-bucket/Karnataka/India/",
   458  			matched: false,
   459  		},
   460  		// Test case - 11.
   461  		// Test case with text expanded to the wildcards in the pattern.
   462  		{
   463  			pattern: "my-bucket/In*/Ka*/Ban",
   464  			text:    "my-bucket/India/Karnataka/Ban",
   465  			matched: true,
   466  		},
   467  		// Test case - 12.
   468  		// Test case with the  keyname part is repeated as prefix several times.
   469  		// This is valid.
   470  		{
   471  			pattern: "my-bucket/In*/Ka*/Ban",
   472  			text:    "my-bucket/India/Karnataka/Ban/Ban/Ban/Ban/Ban",
   473  			matched: true,
   474  		},
   475  		// Test case - 13.
   476  		// Test case to validate that `*` can be expanded into multiple prefixes.
   477  		{
   478  			pattern: "my-bucket/In*/Ka*/Ban",
   479  			text:    "my-bucket/India/Karnataka/Area1/Area2/Area3/Ban",
   480  			matched: true,
   481  		},
   482  		// Test case - 14.
   483  		// Test case to validate that `*` can be expanded into multiple prefixes.
   484  		{
   485  			pattern: "my-bucket/In*/Ka*/Ban",
   486  			text:    "my-bucket/India/State1/State2/Karnataka/Area1/Area2/Area3/Ban",
   487  			matched: true,
   488  		},
   489  		// Test case - 15.
   490  		// Test case where the keyname part of the pattern is expanded in the text.
   491  		{
   492  			pattern: "my-bucket/In*/Ka*/Ban",
   493  			text:    "my-bucket/India/Karnataka/Bangalore",
   494  			matched: false,
   495  		},
   496  		// Test case - 16.
   497  		// Test case with prefixes and wildcard expanded for all "*".
   498  		{
   499  			pattern: "my-bucket/In*/Ka*/Ban*",
   500  			text:    "my-bucket/India/Karnataka/Bangalore",
   501  			matched: true,
   502  		},
   503  		// Test case - 17.
   504  		// Test case with keyname part being a wildcard in the pattern.
   505  		{
   506  			pattern: "my-bucket/*",
   507  			text:    "my-bucket/India",
   508  			matched: true,
   509  		},
   510  		// Test case - 18.
   511  		{
   512  			pattern: "my-bucket/oo*",
   513  			text:    "my-bucket/odo",
   514  			matched: false,
   515  		},
   516  		// Test case - 11.
   517  		{
   518  			pattern: "my-bucket/oo?*",
   519  			text:    "my-bucket/oo???",
   520  			matched: true,
   521  		},
   522  		// Test case - 12:
   523  		{
   524  			pattern: "my-bucket/oo??*",
   525  			text:    "my-bucket/odo",
   526  			matched: false,
   527  		},
   528  		// Test case - 13:
   529  		{
   530  			pattern: "?h?*",
   531  			text:    "?h?hello",
   532  			matched: true,
   533  		},
   534  	}
   535  	// Iterating over the test cases, call the function under test and asert the output.
   536  	for i, testCase := range testCases {
   537  		actualResult := wildcard.MatchSimple(testCase.pattern, testCase.text)
   538  		if testCase.matched != actualResult {
   539  			t.Errorf("Test %d: Expected the result to be `%v`, but instead found it to be `%v`", i+1, testCase.matched, actualResult)
   540  		}
   541  	}
   542  }