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  }