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 }