github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/bucket/lifecycle/filter_test.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package lifecycle
    19  
    20  import (
    21  	"encoding/xml"
    22  	"fmt"
    23  	"testing"
    24  
    25  	"github.com/dustin/go-humanize"
    26  )
    27  
    28  // TestUnsupportedFilters checks if parsing Filter xml with
    29  // unsupported elements returns appropriate errors
    30  func TestUnsupportedFilters(t *testing.T) {
    31  	testCases := []struct {
    32  		inputXML    string
    33  		expectedErr error
    34  	}{
    35  		{ // Filter with And tags
    36  			inputXML: ` <Filter>
    37  						<And>
    38  							<Prefix>key-prefix</Prefix>
    39  						</And>
    40  						</Filter>`,
    41  			expectedErr: errXMLNotWellFormed,
    42  		},
    43  		{ // Filter with Tag tags
    44  			inputXML: ` <Filter>
    45  						<Tag>
    46  							<Key>key1</Key>
    47  							<Value>value1</Value>
    48  						</Tag>
    49  						</Filter>`,
    50  			expectedErr: nil,
    51  		},
    52  		{ // Filter with Prefix tag
    53  			inputXML: ` <Filter>
    54  							<Prefix>key-prefix</Prefix>
    55  						</Filter>`,
    56  			expectedErr: nil,
    57  		},
    58  		{ // Filter without And and multiple Tag tags
    59  			inputXML: ` <Filter>
    60  							<Prefix>key-prefix</Prefix>
    61  							<Tag>
    62  								<Key>key1</Key>
    63  								<Value>value1</Value>
    64  							</Tag>
    65  							<Tag>
    66  								<Key>key2</Key>
    67  								<Value>value2</Value>
    68  							</Tag>
    69  						</Filter>`,
    70  			expectedErr: errInvalidFilter,
    71  		},
    72  		{ // Filter with And, Prefix & multiple Tag tags
    73  			inputXML: ` <Filter>
    74  							<And>
    75  							<Prefix>key-prefix</Prefix>
    76  							<Tag>
    77  								<Key>key1</Key>
    78  								<Value>value1</Value>
    79  							</Tag>
    80  							<Tag>
    81  								<Key>key2</Key>
    82  								<Value>value2</Value>
    83  							</Tag>
    84  							</And>
    85  						</Filter>`,
    86  			expectedErr: nil,
    87  		},
    88  		{ // Filter with And and multiple Tag tags
    89  			inputXML: ` <Filter>
    90  							<And>
    91  							<Prefix></Prefix>
    92  							<Tag>
    93  								<Key>key1</Key>
    94  								<Value>value1</Value>
    95  							</Tag>
    96  							<Tag>
    97  								<Key>key2</Key>
    98  								<Value>value2</Value>
    99  							</Tag>
   100  							</And>
   101  						</Filter>`,
   102  			expectedErr: nil,
   103  		},
   104  		{ // Filter without And and single Tag tag
   105  			inputXML: ` <Filter>
   106  							<Prefix>key-prefix</Prefix>
   107  							<Tag>
   108  								<Key>key1</Key>
   109  								<Value>value1</Value>
   110  							</Tag>
   111  						</Filter>`,
   112  			expectedErr: errInvalidFilter,
   113  		},
   114  	}
   115  	for i, tc := range testCases {
   116  		t.Run(fmt.Sprintf("Test %d", i+1), func(t *testing.T) {
   117  			var filter Filter
   118  			err := xml.Unmarshal([]byte(tc.inputXML), &filter)
   119  			if err != nil {
   120  				t.Fatalf("%d: Expected no error but got %v", i+1, err)
   121  			}
   122  			err = filter.Validate()
   123  			if err != tc.expectedErr {
   124  				t.Fatalf("%d: Expected %v but got %v", i+1, tc.expectedErr, err)
   125  			}
   126  		})
   127  	}
   128  }
   129  
   130  func TestObjectSizeFilters(t *testing.T) {
   131  	f1 := Filter{
   132  		set: true,
   133  		Prefix: Prefix{
   134  			string: "doc/",
   135  			set:    true,
   136  			Unused: struct{}{},
   137  		},
   138  		ObjectSizeGreaterThan: 100 * humanize.MiByte,
   139  		ObjectSizeLessThan:    100 * humanize.GiByte,
   140  	}
   141  	b, err := xml.Marshal(f1)
   142  	if err != nil {
   143  		t.Fatalf("Failed to marshal %v", f1)
   144  	}
   145  	var f2 Filter
   146  	err = xml.Unmarshal(b, &f2)
   147  	if err != nil {
   148  		t.Fatalf("Failed to unmarshal %s", string(b))
   149  	}
   150  	if f1.ObjectSizeLessThan != f2.ObjectSizeLessThan {
   151  		t.Fatalf("Expected %v but got %v", f1.ObjectSizeLessThan, f2.And.ObjectSizeLessThan)
   152  	}
   153  	if f1.ObjectSizeGreaterThan != f2.ObjectSizeGreaterThan {
   154  		t.Fatalf("Expected %v but got %v", f1.ObjectSizeGreaterThan, f2.And.ObjectSizeGreaterThan)
   155  	}
   156  
   157  	f1 = Filter{
   158  		set: true,
   159  		And: And{
   160  			ObjectSizeGreaterThan: 100 * humanize.MiByte,
   161  			ObjectSizeLessThan:    1 * humanize.GiByte,
   162  			Prefix:                Prefix{},
   163  		},
   164  		andSet: true,
   165  	}
   166  	b, err = xml.Marshal(f1)
   167  	if err != nil {
   168  		t.Fatalf("Failed to marshal %v", f1)
   169  	}
   170  	f2 = Filter{}
   171  	err = xml.Unmarshal(b, &f2)
   172  	if err != nil {
   173  		t.Fatalf("Failed to unmarshal %s", string(b))
   174  	}
   175  	if f1.And.ObjectSizeLessThan != f2.And.ObjectSizeLessThan {
   176  		t.Fatalf("Expected %v but got %v", f1.And.ObjectSizeLessThan, f2.And.ObjectSizeLessThan)
   177  	}
   178  	if f1.And.ObjectSizeGreaterThan != f2.And.ObjectSizeGreaterThan {
   179  		t.Fatalf("Expected %v but got %v", f1.And.ObjectSizeGreaterThan, f2.And.ObjectSizeGreaterThan)
   180  	}
   181  
   182  	fiGt := Filter{
   183  		ObjectSizeGreaterThan: 1 * humanize.MiByte,
   184  	}
   185  	fiLt := Filter{
   186  		ObjectSizeLessThan: 100 * humanize.MiByte,
   187  	}
   188  	fiLtAndGt := Filter{
   189  		And: And{
   190  			ObjectSizeGreaterThan: 1 * humanize.MiByte,
   191  			ObjectSizeLessThan:    100 * humanize.MiByte,
   192  		},
   193  	}
   194  
   195  	tests := []struct {
   196  		filter  Filter
   197  		objSize int64
   198  		want    bool
   199  	}{
   200  		{
   201  			filter:  fiLt,
   202  			objSize: 101 * humanize.MiByte,
   203  			want:    false,
   204  		},
   205  		{
   206  			filter:  fiLt,
   207  			objSize: 99 * humanize.MiByte,
   208  			want:    true,
   209  		},
   210  		{
   211  			filter:  fiGt,
   212  			objSize: 1*humanize.MiByte - 1,
   213  			want:    false,
   214  		},
   215  		{
   216  			filter:  fiGt,
   217  			objSize: 1*humanize.MiByte + 1,
   218  			want:    true,
   219  		},
   220  		{
   221  			filter:  fiLtAndGt,
   222  			objSize: 1*humanize.MiByte - 1,
   223  			want:    false,
   224  		},
   225  		{
   226  			filter:  fiLtAndGt,
   227  			objSize: 2 * humanize.MiByte,
   228  			want:    true,
   229  		},
   230  		{
   231  			filter:  fiLtAndGt,
   232  			objSize: 100*humanize.MiByte + 1,
   233  			want:    false,
   234  		},
   235  	}
   236  	for i, test := range tests {
   237  		t.Run(fmt.Sprintf("Test %d", i+1), func(t *testing.T) {
   238  			if got := test.filter.BySize(test.objSize); got != test.want {
   239  				t.Fatalf("Expected %v but got %v", test.want, got)
   240  			}
   241  		})
   242  	}
   243  }
   244  
   245  func TestTestTags(t *testing.T) {
   246  	noTags := Filter{
   247  		set: true,
   248  		And: And{
   249  			Tags: []Tag{},
   250  		},
   251  		andSet: true,
   252  	}
   253  
   254  	oneTag := Filter{
   255  		set: true,
   256  		And: And{
   257  			Tags: []Tag{{Key: "FOO", Value: "1"}},
   258  		},
   259  		andSet: true,
   260  	}
   261  
   262  	twoTags := Filter{
   263  		set: true,
   264  		And: And{
   265  			Tags: []Tag{{Key: "FOO", Value: "1"}, {Key: "BAR", Value: "2"}},
   266  		},
   267  		andSet: true,
   268  	}
   269  
   270  	tests := []struct {
   271  		filter   Filter
   272  		userTags string
   273  		want     bool
   274  	}{
   275  		{
   276  			filter:   noTags,
   277  			userTags: "",
   278  			want:     true,
   279  		},
   280  		{
   281  			filter:   noTags,
   282  			userTags: "A=3",
   283  			want:     true,
   284  		},
   285  		{
   286  			filter:   oneTag,
   287  			userTags: "A=3",
   288  			want:     false,
   289  		},
   290  		{
   291  			filter:   oneTag,
   292  			userTags: "FOO=1",
   293  			want:     true,
   294  		},
   295  		{
   296  			filter:   oneTag,
   297  			userTags: "A=B&FOO=1",
   298  			want:     true,
   299  		},
   300  		{
   301  			filter:   twoTags,
   302  			userTags: "",
   303  			want:     false,
   304  		},
   305  		{
   306  			filter:   twoTags,
   307  			userTags: "FOO=1",
   308  			want:     false,
   309  		},
   310  		{
   311  			filter:   twoTags,
   312  			userTags: "BAR=2",
   313  			want:     false,
   314  		},
   315  		{
   316  			filter:   twoTags,
   317  			userTags: "FOO=2&BAR=2",
   318  			want:     false,
   319  		},
   320  		{
   321  			filter:   twoTags,
   322  			userTags: "F=1&B=2",
   323  			want:     false,
   324  		},
   325  		{
   326  			filter:   twoTags,
   327  			userTags: "FOO=1&BAR=2",
   328  			want:     true,
   329  		},
   330  		{
   331  			filter:   twoTags,
   332  			userTags: "BAR=2&FOO=1",
   333  			want:     true,
   334  		},
   335  	}
   336  	for i, test := range tests {
   337  		t.Run(fmt.Sprintf("Test %d", i+1), func(t *testing.T) {
   338  			if got := test.filter.TestTags(test.userTags); got != test.want {
   339  				t.Errorf("Expected %v but got %v", test.want, got)
   340  			}
   341  		})
   342  	}
   343  }