storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/bucket/replication/replication_test.go (about) 1 package replication 2 3 import ( 4 "bytes" 5 "fmt" 6 "testing" 7 ) 8 9 func TestParseAndValidateReplicationConfig(t *testing.T) { 10 testCases := []struct { 11 inputConfig string 12 expectedParsingErr error 13 expectedValidationErr error 14 destBucket string 15 sameTarget bool 16 }{ 17 { //1 Invalid delete marker status in replication config 18 inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>string</Status></DeleteMarkerReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 19 destBucket: "destinationbucket", 20 sameTarget: false, 21 expectedParsingErr: nil, 22 expectedValidationErr: errInvalidDeleteMarkerReplicationStatus, 23 }, 24 //2 Invalid delete replication status in replication config 25 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 26 destBucket: "destinationbucket", 27 sameTarget: false, 28 expectedParsingErr: nil, 29 expectedValidationErr: errDeleteReplicationMissing, 30 }, 31 //3 valid replication config 32 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 33 destBucket: "destinationbucket", 34 sameTarget: false, 35 expectedParsingErr: nil, 36 expectedValidationErr: nil, 37 }, 38 //4 missing role in config 39 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 40 // destination bucket in config different from bucket specified 41 destBucket: "destinationbucket", 42 sameTarget: false, 43 expectedParsingErr: nil, 44 expectedValidationErr: errRoleArnMissing, 45 }, 46 //5 replication destination in different rules not identical 47 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule><Rule><Status>Enabled</Status><Priority>3</Priority><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket2</Bucket></Destination></Rule></ReplicationConfiguration>`, 48 destBucket: "destinationbucket", 49 sameTarget: false, 50 expectedParsingErr: nil, 51 expectedValidationErr: errReplicationDestinationMismatch, 52 }, 53 //6 missing rule status in replication config 54 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 55 destBucket: "destinationbucket", 56 sameTarget: false, 57 expectedParsingErr: nil, 58 expectedValidationErr: errEmptyRuleStatus, 59 }, 60 //7 invalid rule status in replication config 61 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enssabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 62 destBucket: "destinationbucket", 63 sameTarget: false, 64 expectedParsingErr: nil, 65 expectedValidationErr: errInvalidRuleStatus, 66 }, 67 //8 invalid rule id exceeds length allowed in replication config 68 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><ID>vsUVERgOc8zZYagLSzSa5lE8qeI6nh1lyLNS4R9W052yfecrhhepGboswSWMMNO8CPcXM4GM3nKyQ72EadlMzzZBFoYWKn7ju5GoE5w9c57a0piHR1vexpdd9FrMquiruvAJ0MTGVupm0EegMVxoIOdjx7VgZhGrmi2XDvpVEFT7WmYMA9fSK297XkTHWyECaNHBySJ1Qp4vwX8tPNauKpfHx4kzUpnKe1PZbptGMWbY5qTcwlNuMhVSmgFffShq</ID><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 69 destBucket: "destinationbucket", 70 sameTarget: false, 71 expectedParsingErr: nil, 72 expectedValidationErr: errInvalidRuleID, 73 }, 74 //9 invalid priority status in replication config 75 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 76 destBucket: "destinationbucket", 77 sameTarget: false, 78 expectedParsingErr: nil, 79 expectedValidationErr: errReplicationUniquePriority, 80 }, 81 //10 no rule in replication config 82 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role></ReplicationConfiguration>`, 83 destBucket: "destinationbucket", 84 sameTarget: false, 85 expectedParsingErr: nil, 86 expectedValidationErr: errReplicationNoRule, 87 }, 88 //11 no destination in replication config 89 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination></Destination></Rule></ReplicationConfiguration>`, 90 destBucket: "destinationbucket", 91 sameTarget: false, 92 expectedParsingErr: Errorf("invalid destination '%v'", ""), 93 expectedValidationErr: nil, 94 }, 95 //12 destination not matching ARN in replication config 96 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>destinationbucket2</Bucket></Destination></Rule></ReplicationConfiguration>`, 97 destBucket: "destinationbucket", 98 sameTarget: false, 99 expectedParsingErr: fmt.Errorf("invalid destination '%v'", "destinationbucket2"), 100 expectedValidationErr: nil, 101 }, 102 } 103 for i, tc := range testCases { 104 t.Run(fmt.Sprintf("Test %d", i+1), func(t *testing.T) { 105 cfg, err := ParseConfig(bytes.NewReader([]byte(tc.inputConfig))) 106 if err != nil && tc.expectedParsingErr != nil && err.Error() != tc.expectedParsingErr.Error() { 107 t.Fatalf("%d: Expected '%v' during parsing but got '%v'", i+1, tc.expectedParsingErr, err) 108 } 109 if err == nil && tc.expectedParsingErr != nil { 110 t.Fatalf("%d: Expected '%v' during parsing but got '%v'", i+1, tc.expectedParsingErr, err) 111 } 112 if tc.expectedParsingErr != nil { 113 // We already expect a parsing error, 114 // no need to continue this test. 115 return 116 } 117 err = cfg.Validate(tc.destBucket, tc.sameTarget) 118 if err != tc.expectedValidationErr { 119 t.Fatalf("%d: Expected %v during parsing but got %v", i+1, tc.expectedValidationErr, err) 120 } 121 }) 122 } 123 } 124 func TestReplicate(t *testing.T) { 125 cfgs := []Config{ 126 { //Config0 - Replication config has no filters, all replication enabled 127 Rules: []Rule{ 128 { 129 Status: Enabled, 130 Priority: 3, 131 DeleteMarkerReplication: DeleteMarkerReplication{Status: Enabled}, 132 DeleteReplication: DeleteReplication{Status: Enabled}, 133 Filter: Filter{}, 134 }, 135 }, 136 }, 137 { //Config1 - Replication config has no filters, delete,delete-marker replication disabled 138 Rules: []Rule{ 139 { 140 Status: Enabled, 141 Priority: 3, 142 DeleteMarkerReplication: DeleteMarkerReplication{Status: Disabled}, 143 DeleteReplication: DeleteReplication{Status: Disabled}, 144 Filter: Filter{}, 145 }, 146 }, 147 }, 148 { //Config2 - Replication config has filters and more than 1 matching rule, delete,delete-marker replication disabled 149 Rules: []Rule{ 150 { 151 Status: Enabled, 152 Priority: 2, 153 DeleteMarkerReplication: DeleteMarkerReplication{Status: Disabled}, 154 DeleteReplication: DeleteReplication{Status: Enabled}, 155 Filter: Filter{Prefix: "xy", And: And{}, Tag: Tag{Key: "k1", Value: "v1"}}, 156 }, 157 { 158 Status: Enabled, 159 Priority: 1, 160 DeleteMarkerReplication: DeleteMarkerReplication{Status: Enabled}, 161 DeleteReplication: DeleteReplication{Status: Disabled}, 162 Filter: Filter{Prefix: "xyz"}, 163 }, 164 }, 165 }, 166 { //Config3 - Replication config has filters and no overlapping rules 167 Rules: []Rule{ 168 { 169 Status: Enabled, 170 Priority: 2, 171 DeleteMarkerReplication: DeleteMarkerReplication{Status: Disabled}, 172 DeleteReplication: DeleteReplication{Status: Enabled}, 173 Filter: Filter{Prefix: "xy", And: And{}, Tag: Tag{Key: "k1", Value: "v1"}}, 174 }, 175 { 176 Status: Enabled, 177 Priority: 1, 178 DeleteMarkerReplication: DeleteMarkerReplication{Status: Enabled}, 179 DeleteReplication: DeleteReplication{Status: Disabled}, 180 Filter: Filter{Prefix: "abc"}, 181 }, 182 }, 183 }, 184 } 185 testCases := []struct { 186 opts ObjectOpts 187 c Config 188 expectedResult bool 189 }{ 190 // using config 1 - no filters, all replication enabled 191 {ObjectOpts{}, cfgs[0], false}, //1. invalid ObjectOpts missing object name 192 {ObjectOpts{Name: "c1test"}, cfgs[0], true}, //2. valid ObjectOpts passing empty Filter 193 {ObjectOpts{Name: "c1test", VersionID: "vid"}, cfgs[0], true}, //3. valid ObjectOpts passing empty Filter 194 195 {ObjectOpts{Name: "c1test", DeleteMarker: true, OpType: DeleteReplicationType}, cfgs[0], true}, //4. DeleteMarker version replication valid case - matches DeleteMarkerReplication status 196 {ObjectOpts{Name: "c1test", VersionID: "vid", OpType: DeleteReplicationType}, cfgs[0], true}, //5. permanent delete of version, matches DeleteReplication status - valid case 197 {ObjectOpts{Name: "c1test", VersionID: "vid", DeleteMarker: true, OpType: DeleteReplicationType}, cfgs[0], true}, //6. permanent delete of version, matches DeleteReplication status 198 {ObjectOpts{Name: "c1test", VersionID: "vid", DeleteMarker: true, SSEC: true, OpType: DeleteReplicationType}, cfgs[0], false}, //7. permanent delete of version, disqualified by SSE-C 199 {ObjectOpts{Name: "c1test", DeleteMarker: true, SSEC: true, OpType: DeleteReplicationType}, cfgs[0], false}, //8. setting DeleteMarker on SSE-C encrypted object, disqualified by SSE-C 200 {ObjectOpts{Name: "c1test", SSEC: true}, cfgs[0], false}, //9. replication of SSE-C encrypted object, disqualified 201 202 // using config 2 - no filters, only replication of object, metadata enabled 203 {ObjectOpts{Name: "c2test"}, cfgs[1], true}, //10. valid ObjectOpts passing empty Filter 204 {ObjectOpts{Name: "c2test", DeleteMarker: true, OpType: DeleteReplicationType}, cfgs[1], false}, //11. DeleteMarker version replication not allowed due to DeleteMarkerReplication status 205 {ObjectOpts{Name: "c2test", VersionID: "vid", OpType: DeleteReplicationType}, cfgs[1], false}, //12. permanent delete of version, disallowed by DeleteReplication status 206 {ObjectOpts{Name: "c2test", VersionID: "vid", DeleteMarker: true, OpType: DeleteReplicationType}, cfgs[1], false}, //13. permanent delete of DeleteMarker version, disallowed by DeleteReplication status 207 {ObjectOpts{Name: "c2test", VersionID: "vid", DeleteMarker: true, SSEC: true, OpType: DeleteReplicationType}, cfgs[1], false}, //14. permanent delete of version, disqualified by SSE-C & DeleteReplication status 208 {ObjectOpts{Name: "c2test", DeleteMarker: true, SSEC: true, OpType: DeleteReplicationType}, cfgs[1], false}, //15. setting DeleteMarker on SSE-C encrypted object, disqualified by SSE-C & DeleteMarkerReplication status 209 {ObjectOpts{Name: "c2test", SSEC: true}, cfgs[1], false}, //16. replication of SSE-C encrypted object, disqualified by default 210 // using config 2 - has more than one rule with overlapping prefixes 211 {ObjectOpts{Name: "xy/c3test", UserTags: "k1=v1"}, cfgs[2], true}, //17. matches rule 1 for replication of content/metadata 212 {ObjectOpts{Name: "xyz/c3test", UserTags: "k1=v1"}, cfgs[2], true}, //18. matches rule 1 for replication of content/metadata 213 {ObjectOpts{Name: "xyz/c3test", UserTags: "k1=v1", DeleteMarker: true, OpType: DeleteReplicationType}, cfgs[2], false}, //19. matches rule 1 - DeleteMarker replication disallowed by rule 214 {ObjectOpts{Name: "xyz/c3test", UserTags: "k1=v1", DeleteMarker: true, VersionID: "vid", OpType: DeleteReplicationType}, cfgs[2], true}, //20. matches rule 1 - DeleteReplication allowed by rule for permanent delete of DeleteMarker 215 {ObjectOpts{Name: "xyz/c3test", UserTags: "k1=v1", VersionID: "vid", OpType: DeleteReplicationType}, cfgs[2], true}, //21. matches rule 1 - DeleteReplication allowed by rule for permanent delete of version 216 {ObjectOpts{Name: "xyz/c3test"}, cfgs[2], true}, //22. matches rule 2 for replication of content/metadata 217 {ObjectOpts{Name: "xy/c3test", UserTags: "k1=v2"}, cfgs[2], false}, //23. does not match rule1 because tag value does not pass filter 218 {ObjectOpts{Name: "xyz/c3test", DeleteMarker: true, OpType: DeleteReplicationType}, cfgs[2], true}, //24. matches rule 2 - DeleteMarker replication allowed by rule 219 {ObjectOpts{Name: "xyz/c3test", DeleteMarker: true, VersionID: "vid", OpType: DeleteReplicationType}, cfgs[2], false}, //25. matches rule 2 - DeleteReplication disallowed by rule for permanent delete of DeleteMarker 220 {ObjectOpts{Name: "xyz/c3test", VersionID: "vid", OpType: DeleteReplicationType}, cfgs[2], false}, //26. matches rule 1 - DeleteReplication disallowed by rule for permanent delete of version 221 {ObjectOpts{Name: "abc/c3test"}, cfgs[2], false}, //27. matches no rule because object prefix does not match 222 223 // using config 3 - has no overlapping rules 224 {ObjectOpts{Name: "xy/c4test", UserTags: "k1=v1"}, cfgs[3], true}, //28. matches rule 1 for replication of content/metadata 225 {ObjectOpts{Name: "xa/c4test", UserTags: "k1=v1"}, cfgs[3], false}, //29. no rule match object prefix not in rules 226 {ObjectOpts{Name: "xyz/c4test", DeleteMarker: true, OpType: DeleteReplicationType}, cfgs[3], false}, //30. rule 1 not matched because of tags filter 227 {ObjectOpts{Name: "xyz/c4test", UserTags: "k1=v1", DeleteMarker: true, OpType: DeleteReplicationType}, cfgs[3], false}, //31. matches rule 1 - DeleteMarker replication disallowed by rule 228 {ObjectOpts{Name: "xyz/c4test", UserTags: "k1=v1", DeleteMarker: true, VersionID: "vid", OpType: DeleteReplicationType}, cfgs[3], true}, //32. matches rule 1 - DeleteReplication allowed by rule for permanent delete of DeleteMarker 229 {ObjectOpts{Name: "xyz/c4test", UserTags: "k1=v1", VersionID: "vid", OpType: DeleteReplicationType}, cfgs[3], true}, //33. matches rule 1 - DeleteReplication allowed by rule for permanent delete of version 230 {ObjectOpts{Name: "abc/c4test"}, cfgs[3], true}, //34. matches rule 2 for replication of content/metadata 231 {ObjectOpts{Name: "abc/c4test", UserTags: "k1=v2"}, cfgs[3], true}, //35. matches rule 2 for replication of content/metadata 232 {ObjectOpts{Name: "abc/c4test", DeleteMarker: true, OpType: DeleteReplicationType}, cfgs[3], true}, //36. matches rule 2 - DeleteMarker replication allowed by rule 233 {ObjectOpts{Name: "abc/c4test", DeleteMarker: true, VersionID: "vid", OpType: DeleteReplicationType}, cfgs[3], false}, //37. matches rule 2 - DeleteReplication disallowed by rule for permanent delete of DeleteMarker 234 {ObjectOpts{Name: "abc/c4test", VersionID: "vid", OpType: DeleteReplicationType}, cfgs[3], false}, //38. matches rule 2 - DeleteReplication disallowed by rule for permanent delete of version 235 236 } 237 238 for i, testCase := range testCases { 239 result := testCase.c.Replicate(testCase.opts) 240 241 if result != testCase.expectedResult { 242 t.Fatalf("case %v: expected: %v, got: %v", i+1, testCase.expectedResult, result) 243 } 244 } 245 } 246 247 func TestHasActiveRules(t *testing.T) { 248 testCases := []struct { 249 inputConfig string 250 prefix string 251 expectedNonRec bool 252 expectedRec bool 253 }{ 254 255 // case 1 - only one rule which is in Disabled status 256 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Disabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Prefix>key-prefix</Prefix><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 257 prefix: "miss/prefix", 258 expectedNonRec: false, 259 expectedRec: false, 260 }, 261 // case 2 - only one rule which matches prefix filter 262 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Filter><Prefix>key/prefix</Prefix></Filter><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 263 prefix: "key/prefix1", 264 expectedNonRec: true, 265 expectedRec: true, 266 }, 267 // case 3 - empty prefix 268 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 269 prefix: "key-prefix", 270 expectedNonRec: true, 271 expectedRec: true, 272 }, 273 // case 4 - has Filter based on prefix 274 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Filter><Prefix>testdir/dir1/</Prefix></Filter><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 275 prefix: "testdir/", 276 expectedNonRec: false, 277 expectedRec: true, 278 }, 279 //case 5 - has filter with prefix and tags, here we are not matching on tags 280 {inputConfig: `<ReplicationConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Role>arn:aws:iam::AcctID:role/role-name</Role><Rule><Status>Enabled</Status><DeleteMarkerReplication><Status>Disabled</Status></DeleteMarkerReplication><DeleteReplication><Status>Disabled</Status></DeleteReplication><Filter> 281 <And><Prefix>key-prefix</Prefix><Tag><Key>key1</Key><Value>value1</Value></Tag><Tag><Key>key2</Key><Value>value2</Value></Tag></And></Filter><Destination><Bucket>arn:aws:s3:::destinationbucket</Bucket></Destination></Rule></ReplicationConfiguration>`, 282 prefix: "testdir/", 283 expectedNonRec: true, 284 expectedRec: true, 285 }, 286 } 287 288 for i, tc := range testCases { 289 tc := tc 290 t.Run(fmt.Sprintf("Test_%d", i+1), func(t *testing.T) { 291 cfg, err := ParseConfig(bytes.NewReader([]byte(tc.inputConfig))) 292 if err != nil { 293 t.Fatalf("Got unexpected error: %v", err) 294 } 295 if got := cfg.HasActiveRules(tc.prefix, false); got != tc.expectedNonRec { 296 t.Fatalf("Expected result with recursive set to false: `%v`, got: `%v`", tc.expectedNonRec, got) 297 } 298 if got := cfg.HasActiveRules(tc.prefix, true); got != tc.expectedRec { 299 t.Fatalf("Expected result with recursive set to true: `%v`, got: `%v`", tc.expectedRec, got) 300 } 301 302 }) 303 304 } 305 }