| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737 |
- package s3api
- import (
- "fmt"
- "io"
- "net/http"
- "strings"
- "testing"
- "time"
- "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
- )
- func TestValidateRetention(t *testing.T) {
- tests := []struct {
- name string
- retention *ObjectRetention
- expectError bool
- errorMsg string
- }{
- {
- name: "Valid GOVERNANCE retention",
- retention: &ObjectRetention{
- Mode: s3_constants.RetentionModeGovernance,
- RetainUntilDate: timePtr(time.Now().Add(24 * time.Hour)),
- },
- expectError: false,
- },
- {
- name: "Valid COMPLIANCE retention",
- retention: &ObjectRetention{
- Mode: s3_constants.RetentionModeCompliance,
- RetainUntilDate: timePtr(time.Now().Add(24 * time.Hour)),
- },
- expectError: false,
- },
- {
- name: "Missing Mode",
- retention: &ObjectRetention{
- RetainUntilDate: timePtr(time.Now().Add(24 * time.Hour)),
- },
- expectError: true,
- errorMsg: "retention configuration must specify Mode",
- },
- {
- name: "Missing RetainUntilDate",
- retention: &ObjectRetention{
- Mode: s3_constants.RetentionModeGovernance,
- },
- expectError: true,
- errorMsg: "retention configuration must specify RetainUntilDate",
- },
- {
- name: "Invalid Mode",
- retention: &ObjectRetention{
- Mode: "INVALID_MODE",
- RetainUntilDate: timePtr(time.Now().Add(24 * time.Hour)),
- },
- expectError: true,
- errorMsg: "invalid retention mode",
- },
- {
- name: "Past RetainUntilDate",
- retention: &ObjectRetention{
- Mode: s3_constants.RetentionModeGovernance,
- RetainUntilDate: timePtr(time.Now().Add(-24 * time.Hour)),
- },
- expectError: true,
- errorMsg: "retain until date must be in the future",
- },
- {
- name: "Empty retention",
- retention: &ObjectRetention{},
- expectError: true,
- errorMsg: "retention configuration must specify Mode",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- err := ValidateRetention(tt.retention)
- if tt.expectError {
- if err == nil {
- t.Errorf("Expected error but got none")
- } else if !strings.Contains(err.Error(), tt.errorMsg) {
- t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
- }
- } else {
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- }
- })
- }
- }
- func TestValidateLegalHold(t *testing.T) {
- tests := []struct {
- name string
- legalHold *ObjectLegalHold
- expectError bool
- errorMsg string
- }{
- {
- name: "Valid ON status",
- legalHold: &ObjectLegalHold{
- Status: s3_constants.LegalHoldOn,
- },
- expectError: false,
- },
- {
- name: "Valid OFF status",
- legalHold: &ObjectLegalHold{
- Status: s3_constants.LegalHoldOff,
- },
- expectError: false,
- },
- {
- name: "Invalid status",
- legalHold: &ObjectLegalHold{
- Status: "INVALID_STATUS",
- },
- expectError: true,
- errorMsg: "invalid legal hold status",
- },
- {
- name: "Empty status",
- legalHold: &ObjectLegalHold{
- Status: "",
- },
- expectError: true,
- errorMsg: "invalid legal hold status",
- },
- {
- name: "Lowercase on",
- legalHold: &ObjectLegalHold{
- Status: "on",
- },
- expectError: true,
- errorMsg: "invalid legal hold status",
- },
- {
- name: "Lowercase off",
- legalHold: &ObjectLegalHold{
- Status: "off",
- },
- expectError: true,
- errorMsg: "invalid legal hold status",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- err := ValidateLegalHold(tt.legalHold)
- if tt.expectError {
- if err == nil {
- t.Errorf("Expected error but got none")
- } else if !strings.Contains(err.Error(), tt.errorMsg) {
- t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
- }
- } else {
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- }
- })
- }
- }
- func TestParseObjectRetention(t *testing.T) {
- tests := []struct {
- name string
- xmlBody string
- expectError bool
- errorMsg string
- expectedResult *ObjectRetention
- }{
- {
- name: "Valid retention XML",
- xmlBody: `<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- <Mode>GOVERNANCE</Mode>
- <RetainUntilDate>2024-12-31T23:59:59Z</RetainUntilDate>
- </Retention>`,
- expectError: false,
- expectedResult: &ObjectRetention{
- Mode: "GOVERNANCE",
- RetainUntilDate: timePtr(time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC)),
- },
- },
- {
- name: "Valid compliance retention XML",
- xmlBody: `<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- <Mode>COMPLIANCE</Mode>
- <RetainUntilDate>2025-01-01T00:00:00Z</RetainUntilDate>
- </Retention>`,
- expectError: false,
- expectedResult: &ObjectRetention{
- Mode: "COMPLIANCE",
- RetainUntilDate: timePtr(time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)),
- },
- },
- {
- name: "Empty XML body",
- xmlBody: "",
- expectError: true,
- errorMsg: "error parsing XML",
- },
- {
- name: "Invalid XML",
- xmlBody: `<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Mode>GOVERNANCE</Mode><RetainUntilDate>invalid-date</RetainUntilDate></Retention>`,
- expectError: true,
- errorMsg: "cannot parse",
- },
- {
- name: "Malformed XML",
- xmlBody: "<Retention><Mode>GOVERNANCE</Mode><RetainUntilDate>2024-12-31T23:59:59Z</Retention>",
- expectError: true,
- errorMsg: "error parsing XML",
- },
- {
- name: "Missing Mode",
- xmlBody: `<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- <RetainUntilDate>2024-12-31T23:59:59Z</RetainUntilDate>
- </Retention>`,
- expectError: false,
- expectedResult: &ObjectRetention{
- Mode: "",
- RetainUntilDate: timePtr(time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC)),
- },
- },
- {
- name: "Missing RetainUntilDate",
- xmlBody: `<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- <Mode>GOVERNANCE</Mode>
- </Retention>`,
- expectError: false,
- expectedResult: &ObjectRetention{
- Mode: "GOVERNANCE",
- RetainUntilDate: nil,
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- // Create a mock HTTP request with XML body
- req := &http.Request{
- Body: io.NopCloser(strings.NewReader(tt.xmlBody)),
- }
- result, err := parseObjectRetention(req)
- if tt.expectError {
- if err == nil {
- t.Errorf("Expected error but got none")
- } else if !strings.Contains(err.Error(), tt.errorMsg) {
- t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
- }
- } else {
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- if result == nil {
- t.Errorf("Expected result but got nil")
- } else {
- if result.Mode != tt.expectedResult.Mode {
- t.Errorf("Expected Mode %s, got %s", tt.expectedResult.Mode, result.Mode)
- }
- if tt.expectedResult.RetainUntilDate == nil {
- if result.RetainUntilDate != nil {
- t.Errorf("Expected RetainUntilDate to be nil, got %v", result.RetainUntilDate)
- }
- } else if result.RetainUntilDate == nil {
- t.Errorf("Expected RetainUntilDate to be %v, got nil", tt.expectedResult.RetainUntilDate)
- } else if !result.RetainUntilDate.Equal(*tt.expectedResult.RetainUntilDate) {
- t.Errorf("Expected RetainUntilDate %v, got %v", tt.expectedResult.RetainUntilDate, result.RetainUntilDate)
- }
- }
- }
- })
- }
- }
- func TestParseObjectLegalHold(t *testing.T) {
- tests := []struct {
- name string
- xmlBody string
- expectError bool
- errorMsg string
- expectedResult *ObjectLegalHold
- }{
- {
- name: "Valid legal hold ON",
- xmlBody: `<LegalHold xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- <Status>ON</Status>
- </LegalHold>`,
- expectError: false,
- expectedResult: &ObjectLegalHold{
- Status: "ON",
- },
- },
- {
- name: "Valid legal hold OFF",
- xmlBody: `<LegalHold xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- <Status>OFF</Status>
- </LegalHold>`,
- expectError: false,
- expectedResult: &ObjectLegalHold{
- Status: "OFF",
- },
- },
- {
- name: "Empty XML body",
- xmlBody: "",
- expectError: true,
- errorMsg: "error parsing XML",
- },
- {
- name: "Invalid XML",
- xmlBody: "<LegalHold><Status>ON</Status>",
- expectError: true,
- errorMsg: "error parsing XML",
- },
- {
- name: "Missing Status",
- xmlBody: `<LegalHold xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- </LegalHold>`,
- expectError: false,
- expectedResult: &ObjectLegalHold{
- Status: "",
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- // Create a mock HTTP request with XML body
- req := &http.Request{
- Body: io.NopCloser(strings.NewReader(tt.xmlBody)),
- }
- result, err := parseObjectLegalHold(req)
- if tt.expectError {
- if err == nil {
- t.Errorf("Expected error but got none")
- } else if !strings.Contains(err.Error(), tt.errorMsg) {
- t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
- }
- } else {
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- if result == nil {
- t.Errorf("Expected result but got nil")
- } else {
- if result.Status != tt.expectedResult.Status {
- t.Errorf("Expected Status %s, got %s", tt.expectedResult.Status, result.Status)
- }
- }
- }
- })
- }
- }
- func TestParseObjectLockConfiguration(t *testing.T) {
- tests := []struct {
- name string
- xmlBody string
- expectError bool
- errorMsg string
- expectedResult *ObjectLockConfiguration
- }{
- {
- name: "Valid object lock configuration",
- xmlBody: `<ObjectLockConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- <ObjectLockEnabled>Enabled</ObjectLockEnabled>
- </ObjectLockConfiguration>`,
- expectError: false,
- expectedResult: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- },
- },
- {
- name: "Valid object lock configuration with rule",
- xmlBody: `<ObjectLockConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- <ObjectLockEnabled>Enabled</ObjectLockEnabled>
- <Rule>
- <DefaultRetention>
- <Mode>GOVERNANCE</Mode>
- <Days>30</Days>
- </DefaultRetention>
- </Rule>
- </ObjectLockConfiguration>`,
- expectError: false,
- expectedResult: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- Rule: &ObjectLockRule{
- DefaultRetention: &DefaultRetention{
- Mode: "GOVERNANCE",
- Days: 30,
- },
- },
- },
- },
- {
- name: "Empty XML body",
- xmlBody: "",
- expectError: true,
- errorMsg: "error parsing XML",
- },
- {
- name: "Invalid XML",
- xmlBody: "<ObjectLockConfiguration><ObjectLockEnabled>Enabled</ObjectLockEnabled>",
- expectError: true,
- errorMsg: "error parsing XML",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- // Create a mock HTTP request with XML body
- req := &http.Request{
- Body: io.NopCloser(strings.NewReader(tt.xmlBody)),
- }
- result, err := parseObjectLockConfiguration(req)
- if tt.expectError {
- if err == nil {
- t.Errorf("Expected error but got none")
- } else if !strings.Contains(err.Error(), tt.errorMsg) {
- t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
- }
- } else {
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- if result == nil {
- t.Errorf("Expected result but got nil")
- } else {
- if result.ObjectLockEnabled != tt.expectedResult.ObjectLockEnabled {
- t.Errorf("Expected ObjectLockEnabled %s, got %s", tt.expectedResult.ObjectLockEnabled, result.ObjectLockEnabled)
- }
- if tt.expectedResult.Rule == nil {
- if result.Rule != nil {
- t.Errorf("Expected Rule to be nil, got %v", result.Rule)
- }
- } else if result.Rule == nil {
- t.Errorf("Expected Rule to be non-nil")
- } else {
- if result.Rule.DefaultRetention == nil {
- t.Errorf("Expected DefaultRetention to be non-nil")
- } else {
- if result.Rule.DefaultRetention.Mode != tt.expectedResult.Rule.DefaultRetention.Mode {
- t.Errorf("Expected DefaultRetention Mode %s, got %s", tt.expectedResult.Rule.DefaultRetention.Mode, result.Rule.DefaultRetention.Mode)
- }
- if result.Rule.DefaultRetention.Days != tt.expectedResult.Rule.DefaultRetention.Days {
- t.Errorf("Expected DefaultRetention Days %d, got %d", tt.expectedResult.Rule.DefaultRetention.Days, result.Rule.DefaultRetention.Days)
- }
- }
- }
- }
- }
- })
- }
- }
- func TestValidateObjectLockConfiguration(t *testing.T) {
- tests := []struct {
- name string
- config *ObjectLockConfiguration
- expectError bool
- errorMsg string
- }{
- {
- name: "Valid config with ObjectLockEnabled only",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- },
- expectError: false,
- },
- {
- name: "Missing ObjectLockEnabled",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "",
- },
- expectError: true,
- errorMsg: "object lock configuration must specify ObjectLockEnabled",
- },
- {
- name: "Valid config with rule and days",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- Rule: &ObjectLockRule{
- DefaultRetention: &DefaultRetention{
- Mode: "GOVERNANCE",
- Days: 30,
- DaysSet: true,
- },
- },
- },
- expectError: false,
- },
- {
- name: "Valid config with rule and years",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- Rule: &ObjectLockRule{
- DefaultRetention: &DefaultRetention{
- Mode: "COMPLIANCE",
- Years: 1,
- YearsSet: true,
- },
- },
- },
- expectError: false,
- },
- {
- name: "Invalid ObjectLockEnabled value",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "InvalidValue",
- },
- expectError: true,
- errorMsg: "invalid object lock enabled value",
- },
- {
- name: "Invalid rule - missing mode",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- Rule: &ObjectLockRule{
- DefaultRetention: &DefaultRetention{
- Days: 30,
- },
- },
- },
- expectError: true,
- errorMsg: "default retention must specify Mode",
- },
- {
- name: "Invalid rule - both days and years",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- Rule: &ObjectLockRule{
- DefaultRetention: &DefaultRetention{
- Mode: "GOVERNANCE",
- Days: 30,
- Years: 1,
- DaysSet: true,
- YearsSet: true,
- },
- },
- },
- expectError: true,
- errorMsg: "default retention cannot specify both Days and Years",
- },
- {
- name: "Invalid rule - neither days nor years",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- Rule: &ObjectLockRule{
- DefaultRetention: &DefaultRetention{
- Mode: "GOVERNANCE",
- },
- },
- },
- expectError: true,
- errorMsg: "default retention must specify either Days or Years",
- },
- {
- name: "Invalid rule - invalid mode",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- Rule: &ObjectLockRule{
- DefaultRetention: &DefaultRetention{
- Mode: "INVALID_MODE",
- Days: 30,
- DaysSet: true,
- },
- },
- },
- expectError: true,
- errorMsg: "invalid default retention mode",
- },
- {
- name: "Invalid rule - days out of range",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- Rule: &ObjectLockRule{
- DefaultRetention: &DefaultRetention{
- Mode: "GOVERNANCE",
- Days: 50000,
- DaysSet: true,
- },
- },
- },
- expectError: true,
- errorMsg: fmt.Sprintf("default retention days must be between 0 and %d", MaxRetentionDays),
- },
- {
- name: "Invalid rule - years out of range",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- Rule: &ObjectLockRule{
- DefaultRetention: &DefaultRetention{
- Mode: "GOVERNANCE",
- Years: 200,
- YearsSet: true,
- },
- },
- },
- expectError: true,
- errorMsg: fmt.Sprintf("default retention years must be between 0 and %d", MaxRetentionYears),
- },
- {
- name: "Invalid rule - missing DefaultRetention",
- config: &ObjectLockConfiguration{
- ObjectLockEnabled: "Enabled",
- Rule: &ObjectLockRule{
- DefaultRetention: nil,
- },
- },
- expectError: true,
- errorMsg: "rule configuration must specify DefaultRetention",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- err := ValidateObjectLockConfiguration(tt.config)
- if tt.expectError {
- if err == nil {
- t.Errorf("Expected error but got none")
- } else if !strings.Contains(err.Error(), tt.errorMsg) {
- t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
- }
- } else {
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- }
- })
- }
- }
- func TestValidateDefaultRetention(t *testing.T) {
- tests := []struct {
- name string
- retention *DefaultRetention
- expectError bool
- errorMsg string
- }{
- {
- name: "Valid retention with days",
- retention: &DefaultRetention{
- Mode: "GOVERNANCE",
- Days: 30,
- DaysSet: true,
- },
- expectError: false,
- },
- {
- name: "Valid retention with years",
- retention: &DefaultRetention{
- Mode: "COMPLIANCE",
- Years: 1,
- YearsSet: true,
- },
- expectError: false,
- },
- {
- name: "Missing mode",
- retention: &DefaultRetention{
- Days: 30,
- DaysSet: true,
- },
- expectError: true,
- errorMsg: "default retention must specify Mode",
- },
- {
- name: "Invalid mode",
- retention: &DefaultRetention{
- Mode: "INVALID",
- Days: 30,
- DaysSet: true,
- },
- expectError: true,
- errorMsg: "invalid default retention mode",
- },
- {
- name: "Both days and years specified",
- retention: &DefaultRetention{
- Mode: "GOVERNANCE",
- Days: 30,
- Years: 1,
- DaysSet: true,
- YearsSet: true,
- },
- expectError: true,
- errorMsg: "default retention cannot specify both Days and Years",
- },
- {
- name: "Neither days nor years specified",
- retention: &DefaultRetention{
- Mode: "GOVERNANCE",
- },
- expectError: true,
- errorMsg: "default retention must specify either Days or Years",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- err := validateDefaultRetention(tt.retention)
- if tt.expectError {
- if err == nil {
- t.Errorf("Expected error but got none")
- } else if !strings.Contains(err.Error(), tt.errorMsg) {
- t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
- }
- } else {
- if err != nil {
- t.Errorf("Unexpected error: %v", err)
- }
- }
- })
- }
- }
- // Helper function to create a time pointer
- func timePtr(t time.Time) *time.Time {
- return &t
- }
|