github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/bucket/versioning/versioning_test.go (about) 1 // Copyright (c) 2015-2022 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 versioning 19 20 import ( 21 "encoding/xml" 22 "strings" 23 "testing" 24 ) 25 26 func TestParseConfig(t *testing.T) { 27 testcases := []struct { 28 input string 29 err error 30 excludedPrefixes []string 31 excludeFolders bool 32 }{ 33 { 34 input: `<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 35 <Status>Enabled</Status> 36 </VersioningConfiguration>`, 37 err: nil, 38 }, 39 { 40 input: `<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 41 <Status>Enabled</Status> 42 <ExcludedPrefixes> 43 <Prefix>path/to/my/workload/_staging/</Prefix> 44 </ExcludedPrefixes> 45 <ExcludedPrefixes> 46 <Prefix>path/to/my/workload/_temporary/</Prefix> 47 </ExcludedPrefixes> 48 </VersioningConfiguration>`, 49 err: nil, 50 excludedPrefixes: []string{"path/to/my/workload/_staging/", "path/to/my/workload/_temporary/"}, 51 }, 52 { 53 input: `<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 54 <Status>Suspended</Status> 55 <ExcludedPrefixes> 56 <Prefix>path/to/my/workload/_staging</Prefix> 57 </ExcludedPrefixes> 58 </VersioningConfiguration>`, 59 err: errExcludedPrefixNotSupported, 60 }, 61 { 62 input: `<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 63 <Status>Enabled</Status> 64 <ExcludedPrefixes> 65 <Prefix>path/to/my/workload/_staging/ab/</Prefix> 66 </ExcludedPrefixes> 67 <ExcludedPrefixes> 68 <Prefix>path/to/my/workload/_staging/cd/</Prefix> 69 </ExcludedPrefixes> 70 <ExcludedPrefixes> 71 <Prefix>path/to/my/workload/_staging/ef/</Prefix> 72 </ExcludedPrefixes> 73 <ExcludedPrefixes> 74 <Prefix>path/to/my/workload/_staging/gh/</Prefix> 75 </ExcludedPrefixes> 76 <ExcludedPrefixes> 77 <Prefix>path/to/my/workload/_staging/ij/</Prefix> 78 </ExcludedPrefixes> 79 <ExcludedPrefixes> 80 <Prefix>path/to/my/workload/_staging/kl/</Prefix> 81 </ExcludedPrefixes> 82 <ExcludedPrefixes> 83 <Prefix>path/to/my/workload/_staging/mn/</Prefix> 84 </ExcludedPrefixes> 85 <ExcludedPrefixes> 86 <Prefix>path/to/my/workload/_staging/op/</Prefix> 87 </ExcludedPrefixes> 88 <ExcludedPrefixes> 89 <Prefix>path/to/my/workload/_staging/qr/</Prefix> 90 </ExcludedPrefixes> 91 <ExcludedPrefixes> 92 <Prefix>path/to/my/workload/_staging/st/</Prefix> 93 </ExcludedPrefixes> 94 <ExcludedPrefixes> 95 <Prefix>path/to/my/workload/_staging/uv/</Prefix> 96 </ExcludedPrefixes> 97 </VersioningConfiguration>`, 98 err: errTooManyExcludedPrefixes, 99 }, 100 { 101 input: `<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 102 <Status>Enabled</Status> 103 <ExcludeFolders>true</ExcludeFolders> 104 <ExcludedPrefixes> 105 <Prefix>path/to/my/workload/_staging/</Prefix> 106 </ExcludedPrefixes> 107 <ExcludedPrefixes> 108 <Prefix>path/to/my/workload/_temporary/</Prefix> 109 </ExcludedPrefixes> 110 </VersioningConfiguration>`, 111 err: nil, 112 excludedPrefixes: []string{"path/to/my/workload/_staging/", "path/to/my/workload/_temporary/"}, 113 excludeFolders: true, 114 }, 115 } 116 117 for i, tc := range testcases { 118 var v *Versioning 119 var err error 120 v, err = ParseConfig(strings.NewReader(tc.input)) 121 if tc.err != err { 122 t.Fatalf("Test %d: expected %v but got %v", i+1, tc.err, err) 123 } 124 if err != nil { 125 if tc.err == nil { 126 t.Fatalf("Test %d: failed due to %v", i+1, err) 127 } 128 } else { 129 if err := v.Validate(); tc.err != err { 130 t.Fatalf("Test %d: validation failed due to %v", i+1, err) 131 } 132 if len(tc.excludedPrefixes) > 0 { 133 var mismatch bool 134 if len(v.ExcludedPrefixes) != len(tc.excludedPrefixes) { 135 t.Fatalf("Test %d: Expected length of excluded prefixes %d but got %d", i+1, len(tc.excludedPrefixes), len(v.ExcludedPrefixes)) 136 } 137 var i int 138 var eprefix string 139 for i, eprefix = range tc.excludedPrefixes { 140 if eprefix != v.ExcludedPrefixes[i].Prefix { 141 mismatch = true 142 break 143 } 144 } 145 if mismatch { 146 t.Fatalf("Test %d: Expected excluded prefix %s but got %s", i+1, tc.excludedPrefixes[i], v.ExcludedPrefixes[i].Prefix) 147 } 148 } 149 if tc.excludeFolders != v.ExcludeFolders { 150 t.Fatalf("Test %d: Expected ExcludeFoldersr=%v but got %v", i+1, tc.excludeFolders, v.ExcludeFolders) 151 } 152 } 153 } 154 } 155 156 func TestMarshalXML(t *testing.T) { 157 // Validates if Versioning with no excluded prefixes omits 158 // ExcludedPrefixes tags 159 v := Versioning{ 160 Status: Enabled, 161 } 162 buf, err := xml.Marshal(v) 163 if err != nil { 164 t.Fatalf("Failed to marshal %v: %v", v, err) 165 } 166 167 str := string(buf) 168 if strings.Contains(str, "ExcludedPrefixes") { 169 t.Fatalf("XML shouldn't contain ExcludedPrefixes tag - %s", str) 170 } 171 } 172 173 func TestVersioningZero(t *testing.T) { 174 var v Versioning 175 if v.Enabled() { 176 t.Fatalf("Expected to be disabled but got enabled") 177 } 178 if v.Suspended() { 179 t.Fatalf("Expected to be disabled but got suspended") 180 } 181 } 182 183 func TestExcludeFolders(t *testing.T) { 184 v := Versioning{ 185 Status: Enabled, 186 ExcludeFolders: true, 187 } 188 testPrefixes := []string{"jobs/output/_temporary/", "jobs/output/", "jobs/"} 189 for i, prefix := range testPrefixes { 190 if v.PrefixEnabled(prefix) || !v.PrefixSuspended(prefix) { 191 t.Fatalf("Test %d: Expected versioning to be excluded for %s", i+1, prefix) 192 } 193 } 194 195 // Test applicability for regular objects 196 if prefix := "prefix-1/obj-1"; !v.PrefixEnabled(prefix) || v.PrefixSuspended(prefix) { 197 t.Fatalf("Expected versioning to be enabled for %s", prefix) 198 } 199 200 // Test when ExcludeFolders is disabled 201 v.ExcludeFolders = false 202 for i, prefix := range testPrefixes { 203 if !v.PrefixEnabled(prefix) || v.PrefixSuspended(prefix) { 204 t.Fatalf("Test %d: Expected versioning to be enabled for %s", i+1, prefix) 205 } 206 } 207 } 208 209 func TestExcludedPrefixesMatch(t *testing.T) { 210 v := Versioning{ 211 Status: Enabled, 212 ExcludedPrefixes: []ExcludedPrefix{{"*/_temporary/"}}, 213 } 214 215 if err := v.Validate(); err != nil { 216 t.Fatalf("Invalid test versioning config %v: %v", v, err) 217 } 218 tests := []struct { 219 prefix string 220 excluded bool 221 }{ 222 { 223 prefix: "app1-jobs/output/_temporary/attempt1/data.csv", 224 excluded: true, 225 }, 226 { 227 prefix: "app1-jobs/output/final/attempt1/data.csv", 228 excluded: false, 229 }, 230 } 231 232 for i, test := range tests { 233 if v.PrefixSuspended(test.prefix) != test.excluded { 234 if test.excluded { 235 t.Fatalf("Test %d: Expected prefix %s to be excluded from versioning", i+1, test.prefix) 236 } else { 237 t.Fatalf("Test %d: Expected prefix %s to have versioning enabled", i+1, test.prefix) 238 } 239 } 240 } 241 }