/* * MinIO Cloud Storage, (C) 2020 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package objectlock import ( "encoding/xml" "fmt" "net/http" "reflect" "strings" "testing" "time" xhttp "github.com/minio/minio/cmd/http" ) func TestParseMode(t *testing.T) { testCases := []struct { value string expectedMode Mode }{ { value: "governance", expectedMode: Governance, }, { value: "complIAnce", expectedMode: Compliance, }, { value: "gce", expectedMode: Invalid, }, } for _, tc := range testCases { if parseMode(tc.value) != tc.expectedMode { t.Errorf("Expected Mode %s, got %s", tc.expectedMode, parseMode(tc.value)) } } } func TestParseLegalHoldStatus(t *testing.T) { tests := []struct { value string expectedStatus LegalHoldStatus }{ { value: "ON", expectedStatus: ON, }, { value: "Off", expectedStatus: OFF, }, { value: "x", expectedStatus: "", }, } for _, tt := range tests { actualStatus := parseLegalHoldStatus(tt.value) if actualStatus != tt.expectedStatus { t.Errorf("Expected legal hold status %s, got %s", tt.expectedStatus, actualStatus) } } } // TestUnmarshalDefaultRetention checks if default retention // marshaling and unmarshaling work as expected func TestUnmarshalDefaultRetention(t *testing.T) { days := uint64(4) years := uint64(1) zerodays := uint64(0) invalidDays := uint64(maximumRetentionDays + 1) tests := []struct { value DefaultRetention expectedErr error expectErr bool }{ { value: DefaultRetention{Mode: "retain"}, expectedErr: fmt.Errorf("unknown retention mode retain"), expectErr: true, }, { value: DefaultRetention{Mode: "GOVERNANCE"}, expectedErr: fmt.Errorf("either Days or Years must be specified"), expectErr: true, }, { value: DefaultRetention{Mode: "GOVERNANCE", Days: &days}, expectedErr: nil, expectErr: false, }, { value: DefaultRetention{Mode: "GOVERNANCE", Years: &years}, expectedErr: nil, expectErr: false, }, { value: DefaultRetention{Mode: "GOVERNANCE", Days: &days, Years: &years}, expectedErr: fmt.Errorf("either Days or Years must be specified, not both"), expectErr: true, }, { value: DefaultRetention{Mode: "GOVERNANCE", Days: &zerodays}, expectedErr: fmt.Errorf("Default retention period must be a positive integer value for 'Days'"), expectErr: true, }, { value: DefaultRetention{Mode: "GOVERNANCE", Days: &invalidDays}, expectedErr: fmt.Errorf("Default retention period too large for 'Days' %d", invalidDays), expectErr: true, }, } for _, tt := range tests { d, err := xml.MarshalIndent(&tt.value, "", "\t") if err != nil { t.Fatal(err) } var dr DefaultRetention err = xml.Unmarshal(d, &dr) if tt.expectedErr == nil { if err != nil { t.Fatalf("error: expected = , got = %v", err) } } else if err == nil { t.Fatalf("error: expected = %v, got = ", tt.expectedErr) } else if tt.expectedErr.Error() != err.Error() { t.Fatalf("error: expected = %v, got = %v", tt.expectedErr, err) } } } func TestParseObjectLockConfig(t *testing.T) { tests := []struct { value string expectedErr error expectErr bool }{ { value: "yes", expectedErr: fmt.Errorf("only 'Enabled' value is allowd to ObjectLockEnabled element"), expectErr: true, }, { value: "EnabledCOMPLIANCE0", expectedErr: fmt.Errorf("Default retention period must be a positive integer value for 'Days'"), expectErr: true, }, { value: "EnabledCOMPLIANCE30", expectedErr: nil, expectErr: false, }, } for _, tt := range tests { _, err := ParseObjectLockConfig(strings.NewReader(tt.value)) if tt.expectedErr == nil { if err != nil { t.Fatalf("error: expected = , got = %v", err) } } else if err == nil { t.Fatalf("error: expected = %v, got = ", tt.expectedErr) } else if tt.expectedErr.Error() != err.Error() { t.Fatalf("error: expected = %v, got = %v", tt.expectedErr, err) } } } func TestParseObjectRetention(t *testing.T) { tests := []struct { value string expectedErr error expectErr bool }{ { value: "string2020-01-02T15:04:05Z", expectedErr: ErrUnknownWORMModeDirective, expectErr: true, }, { value: "COMPLIANCE2017-01-02T15:04:05Z", expectedErr: ErrPastObjectLockRetainDate, expectErr: true, }, { value: "GOVERNANCE2057-01-02T15:04:05Z", expectedErr: nil, expectErr: false, }, } for _, tt := range tests { _, err := ParseObjectRetention(strings.NewReader(tt.value)) if tt.expectedErr == nil { if err != nil { t.Fatalf("error: expected = , got = %v", err) } } else if err == nil { t.Fatalf("error: expected = %v, got = ", tt.expectedErr) } else if tt.expectedErr.Error() != err.Error() { t.Fatalf("error: expected = %v, got = %v", tt.expectedErr, err) } } } func TestIsObjectLockRequested(t *testing.T) { tests := []struct { header http.Header expectedVal bool }{ { header: http.Header{ "Authorization": []string{"AWS4-HMAC-SHA256 "}, "X-Amz-Content-Sha256": []string{""}, "Content-Encoding": []string{""}, }, expectedVal: false, }, { header: http.Header{ xhttp.AmzObjectLockLegalHold: []string{""}, }, expectedVal: true, }, { header: http.Header{ xhttp.AmzObjectLockRetainUntilDate: []string{""}, xhttp.AmzObjectLockMode: []string{""}, }, expectedVal: true, }, { header: http.Header{ xhttp.AmzObjectLockBypassGovernance: []string{""}, }, expectedVal: false, }, } for _, tt := range tests { actualVal := IsObjectLockRequested(tt.header) if actualVal != tt.expectedVal { t.Fatalf("error: expected %v, actual %v", tt.expectedVal, actualVal) } } } func TestIsObjectLockGovernanceBypassSet(t *testing.T) { tests := []struct { header http.Header expectedVal bool }{ { header: http.Header{ "Authorization": []string{"AWS4-HMAC-SHA256 "}, "X-Amz-Content-Sha256": []string{""}, "Content-Encoding": []string{""}, }, expectedVal: false, }, { header: http.Header{ xhttp.AmzObjectLockLegalHold: []string{""}, }, expectedVal: false, }, { header: http.Header{ xhttp.AmzObjectLockRetainUntilDate: []string{""}, xhttp.AmzObjectLockMode: []string{""}, }, expectedVal: false, }, { header: http.Header{ xhttp.AmzObjectLockBypassGovernance: []string{""}, }, expectedVal: false, }, { header: http.Header{ xhttp.AmzObjectLockBypassGovernance: []string{"true"}, }, expectedVal: true, }, } for _, tt := range tests { actualVal := IsObjectLockGovernanceBypassSet(tt.header) if actualVal != tt.expectedVal { t.Fatalf("error: expected %v, actual %v", tt.expectedVal, actualVal) } } } func TestParseObjectLockRetentionHeaders(t *testing.T) { tests := []struct { header http.Header expectedErr error }{ { header: http.Header{ "Authorization": []string{"AWS4-HMAC-SHA256 "}, "X-Amz-Content-Sha256": []string{""}, "Content-Encoding": []string{""}, }, expectedErr: ErrObjectLockInvalidHeaders, }, { header: http.Header{ xhttp.AmzObjectLockMode: []string{"lock"}, xhttp.AmzObjectLockRetainUntilDate: []string{"2017-01-02"}, }, expectedErr: ErrUnknownWORMModeDirective, }, { header: http.Header{ xhttp.AmzObjectLockMode: []string{"governance"}, }, expectedErr: ErrObjectLockInvalidHeaders, }, { header: http.Header{ xhttp.AmzObjectLockRetainUntilDate: []string{"2017-01-02"}, xhttp.AmzObjectLockMode: []string{"governance"}, }, expectedErr: ErrInvalidRetentionDate, }, { header: http.Header{ xhttp.AmzObjectLockRetainUntilDate: []string{"2017-01-02T15:04:05Z"}, xhttp.AmzObjectLockMode: []string{"governance"}, }, expectedErr: ErrPastObjectLockRetainDate, }, { header: http.Header{ xhttp.AmzObjectLockMode: []string{"governance"}, xhttp.AmzObjectLockRetainUntilDate: []string{"2017-01-02T15:04:05Z"}, }, expectedErr: ErrPastObjectLockRetainDate, }, { header: http.Header{ xhttp.AmzObjectLockMode: []string{"governance"}, xhttp.AmzObjectLockRetainUntilDate: []string{"2087-01-02T15:04:05Z"}, }, expectedErr: nil, }, } for i, tt := range tests { _, _, err := ParseObjectLockRetentionHeaders(tt.header) if tt.expectedErr == nil { if err != nil { t.Fatalf("Case %d error: expected = , got = %v", i, err) } } else if err == nil { t.Fatalf("Case %d error: expected = %v, got = ", i, tt.expectedErr) } else if tt.expectedErr.Error() != err.Error() { t.Fatalf("Case %d error: expected = %v, got = %v", i, tt.expectedErr, err) } } } func TestGetObjectRetentionMeta(t *testing.T) { tests := []struct { metadata map[string]string expected ObjectRetention }{ { metadata: map[string]string{ "Authorization": "AWS4-HMAC-SHA256 ", "X-Amz-Content-Sha256": "", "Content-Encoding": "", }, expected: ObjectRetention{}, }, { metadata: map[string]string{ "x-amz-object-lock-mode": "governance", }, expected: ObjectRetention{Mode: Governance}, }, { metadata: map[string]string{ "x-amz-object-lock-retain-until-date": "2020-02-01", }, expected: ObjectRetention{RetainUntilDate: RetentionDate{time.Date(2020, 2, 1, 12, 0, 0, 0, time.UTC)}}, }, } for i, tt := range tests { o := GetObjectRetentionMeta(tt.metadata) if o.Mode != tt.expected.Mode { t.Fatalf("Case %d expected %v, got %v", i, tt.expected.Mode, o.Mode) } } } func TestGetObjectLegalHoldMeta(t *testing.T) { tests := []struct { metadata map[string]string expected ObjectLegalHold }{ { metadata: map[string]string{ "x-amz-object-lock-mode": "governance", }, expected: ObjectLegalHold{}, }, { metadata: map[string]string{ "x-amz-object-lock-legal-hold": "on", }, expected: ObjectLegalHold{Status: ON}, }, { metadata: map[string]string{ "x-amz-object-lock-legal-hold": "off", }, expected: ObjectLegalHold{Status: OFF}, }, { metadata: map[string]string{ "x-amz-object-lock-legal-hold": "X", }, expected: ObjectLegalHold{Status: ""}, }, } for i, tt := range tests { o := GetObjectLegalHoldMeta(tt.metadata) if o.Status != tt.expected.Status { t.Fatalf("Case %d expected %v, got %v", i, tt.expected.Status, o.Status) } } } func TestParseObjectLegalHold(t *testing.T) { tests := []struct { value string expectedErr error expectErr bool }{ { value: "string", expectedErr: ErrMalformedXML, expectErr: true, }, { value: "ON", expectedErr: nil, expectErr: false, }, { value: "On", expectedErr: ErrMalformedXML, expectErr: true, }, } for i, tt := range tests { _, err := ParseObjectLegalHold(strings.NewReader(tt.value)) if tt.expectedErr == nil { if err != nil { t.Fatalf("Case %d error: expected = , got = %v", i, err) } } else if err == nil { t.Fatalf("Case %d error: expected = %v, got = ", i, tt.expectedErr) } else if tt.expectedErr.Error() != err.Error() { t.Fatalf("Case %d error: expected = %v, got = %v", i, tt.expectedErr, err) } } } func TestFilterObjectLockMetadata(t *testing.T) { tests := []struct { metadata map[string]string filterRetention bool filterLegalHold bool expected map[string]string }{ { metadata: map[string]string{ "Authorization": "AWS4-HMAC-SHA256 ", "X-Amz-Content-Sha256": "", "Content-Encoding": "", }, expected: map[string]string{ "Authorization": "AWS4-HMAC-SHA256 ", "X-Amz-Content-Sha256": "", "Content-Encoding": "", }, }, { metadata: map[string]string{ "x-amz-object-lock-mode": "governance", }, expected: map[string]string{ "x-amz-object-lock-mode": "governance", }, filterRetention: false, }, { metadata: map[string]string{ "x-amz-object-lock-mode": "governance", "x-amz-object-lock-retain-until-date": "2020-02-01", }, expected: map[string]string{}, filterRetention: true, }, { metadata: map[string]string{ "x-amz-object-lock-legal-hold": "off", }, expected: map[string]string{}, filterLegalHold: true, }, { metadata: map[string]string{ "x-amz-object-lock-legal-hold": "on", }, expected: map[string]string{"x-amz-object-lock-legal-hold": "on"}, filterLegalHold: false, }, { metadata: map[string]string{ "x-amz-object-lock-legal-hold": "on", "x-amz-object-lock-mode": "governance", "x-amz-object-lock-retain-until-date": "2020-02-01", }, expected: map[string]string{}, filterRetention: true, filterLegalHold: true, }, { metadata: map[string]string{ "x-amz-object-lock-legal-hold": "on", "x-amz-object-lock-mode": "governance", "x-amz-object-lock-retain-until-date": "2020-02-01", }, expected: map[string]string{"x-amz-object-lock-legal-hold": "on", "x-amz-object-lock-mode": "governance", "x-amz-object-lock-retain-until-date": "2020-02-01"}, }, } for i, tt := range tests { o := FilterObjectLockMetadata(tt.metadata, tt.filterRetention, tt.filterLegalHold) if !reflect.DeepEqual(o, tt.metadata) { t.Fatalf("Case %d expected %v, got %v", i, tt.metadata, o) } } }