You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							849 lines
						
					
					
						
							32 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							849 lines
						
					
					
						
							32 KiB
						
					
					
				
								package s3api
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"bytes"
							 | 
						|
									"fmt"
							 | 
						|
									"net/http"
							 | 
						|
									"net/url"
							 | 
						|
									"testing"
							 | 
						|
									"time"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// TestConditionalHeadersWithExistingObjects tests conditional headers against existing objects
							 | 
						|
								// This addresses the PR feedback about missing test coverage for object existence scenarios
							 | 
						|
								func TestConditionalHeadersWithExistingObjects(t *testing.T) {
							 | 
						|
									bucket := "test-bucket"
							 | 
						|
									object := "/test-object"
							 | 
						|
								
							 | 
						|
									// Mock object with known ETag and modification time
							 | 
						|
									testObject := &filer_pb.Entry{
							 | 
						|
										Name: "test-object",
							 | 
						|
										Extended: map[string][]byte{
							 | 
						|
											s3_constants.ExtETagKey: []byte("\"abc123\""),
							 | 
						|
										},
							 | 
						|
										Attributes: &filer_pb.FuseAttributes{
							 | 
						|
											Mtime:    time.Date(2024, 6, 15, 12, 0, 0, 0, time.UTC).Unix(), // June 15, 2024
							 | 
						|
											FileSize: 1024,                                                 // Add file size
							 | 
						|
										},
							 | 
						|
										Chunks: []*filer_pb.FileChunk{
							 | 
						|
											// Add a mock chunk to make calculateETagFromChunks work
							 | 
						|
											{
							 | 
						|
												FileId: "test-file-id",
							 | 
						|
												Offset: 0,
							 | 
						|
												Size:   1024,
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Test If-None-Match with existing object
							 | 
						|
									t.Run("IfNoneMatch_ObjectExists", func(t *testing.T) {
							 | 
						|
										// Test case 1: If-None-Match=* when object exists (should fail)
							 | 
						|
										t.Run("Asterisk_ShouldFail", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "*")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when object exists with If-None-Match=*, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 2: If-None-Match with matching ETag (should fail)
							 | 
						|
										t.Run("MatchingETag_ShouldFail", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "\"abc123\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when ETag matches, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 3: If-None-Match with non-matching ETag (should succeed)
							 | 
						|
										t.Run("NonMatchingETag_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "\"xyz789\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when ETag doesn't match, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 4: If-None-Match with multiple ETags, one matching (should fail)
							 | 
						|
										t.Run("MultipleETags_OneMatches_ShouldFail", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "\"xyz789\", \"abc123\", \"def456\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when one ETag matches, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 5: If-None-Match with multiple ETags, none matching (should succeed)
							 | 
						|
										t.Run("MultipleETags_NoneMatch_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "\"xyz789\", \"def456\", \"ghi123\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when no ETags match, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test If-Match with existing object
							 | 
						|
									t.Run("IfMatch_ObjectExists", func(t *testing.T) {
							 | 
						|
										// Test case 1: If-Match with matching ETag (should succeed)
							 | 
						|
										t.Run("MatchingETag_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfMatch, "\"abc123\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when ETag matches, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 2: If-Match with non-matching ETag (should fail)
							 | 
						|
										t.Run("NonMatchingETag_ShouldFail", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfMatch, "\"xyz789\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when ETag doesn't match, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 3: If-Match with multiple ETags, one matching (should succeed)
							 | 
						|
										t.Run("MultipleETags_OneMatches_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfMatch, "\"xyz789\", \"abc123\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when one ETag matches, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 4: If-Match with wildcard * (should succeed if object exists)
							 | 
						|
										t.Run("Wildcard_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfMatch, "*")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when If-Match=* and object exists, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test If-Modified-Since with existing object
							 | 
						|
									t.Run("IfModifiedSince_ObjectExists", func(t *testing.T) {
							 | 
						|
										// Test case 1: If-Modified-Since with date before object modification (should succeed)
							 | 
						|
										t.Run("DateBefore_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											dateBeforeModification := time.Date(2024, 6, 14, 12, 0, 0, 0, time.UTC)
							 | 
						|
											req.Header.Set(s3_constants.IfModifiedSince, dateBeforeModification.Format(time.RFC1123))
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when object was modified after date, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 2: If-Modified-Since with date after object modification (should fail)
							 | 
						|
										t.Run("DateAfter_ShouldFail", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											dateAfterModification := time.Date(2024, 6, 16, 12, 0, 0, 0, time.UTC)
							 | 
						|
											req.Header.Set(s3_constants.IfModifiedSince, dateAfterModification.Format(time.RFC1123))
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when object wasn't modified since date, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 3: If-Modified-Since with exact modification date (should fail - not after)
							 | 
						|
										t.Run("ExactDate_ShouldFail", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											exactDate := time.Date(2024, 6, 15, 12, 0, 0, 0, time.UTC)
							 | 
						|
											req.Header.Set(s3_constants.IfModifiedSince, exactDate.Format(time.RFC1123))
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when object modification time equals header date, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test If-Unmodified-Since with existing object
							 | 
						|
									t.Run("IfUnmodifiedSince_ObjectExists", func(t *testing.T) {
							 | 
						|
										// Test case 1: If-Unmodified-Since with date after object modification (should succeed)
							 | 
						|
										t.Run("DateAfter_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											dateAfterModification := time.Date(2024, 6, 16, 12, 0, 0, 0, time.UTC)
							 | 
						|
											req.Header.Set(s3_constants.IfUnmodifiedSince, dateAfterModification.Format(time.RFC1123))
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when object wasn't modified after date, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 2: If-Unmodified-Since with date before object modification (should fail)
							 | 
						|
										t.Run("DateBefore_ShouldFail", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(testObject)
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											dateBeforeModification := time.Date(2024, 6, 14, 12, 0, 0, 0, time.UTC)
							 | 
						|
											req.Header.Set(s3_constants.IfUnmodifiedSince, dateBeforeModification.Format(time.RFC1123))
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when object was modified after date, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									})
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestConditionalHeadersForReads tests conditional headers for read operations (GET, HEAD)
							 | 
						|
								// This implements AWS S3 conditional reads behavior where different conditions return different status codes
							 | 
						|
								// See: https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-reads.html
							 | 
						|
								func TestConditionalHeadersForReads(t *testing.T) {
							 | 
						|
									bucket := "test-bucket"
							 | 
						|
									object := "/test-read-object"
							 | 
						|
								
							 | 
						|
									// Mock existing object to test conditional headers against
							 | 
						|
									existingObject := &filer_pb.Entry{
							 | 
						|
										Name: "test-read-object",
							 | 
						|
										Extended: map[string][]byte{
							 | 
						|
											s3_constants.ExtETagKey: []byte("\"read123\""),
							 | 
						|
										},
							 | 
						|
										Attributes: &filer_pb.FuseAttributes{
							 | 
						|
											Mtime:    time.Date(2024, 6, 15, 12, 0, 0, 0, time.UTC).Unix(),
							 | 
						|
											FileSize: 1024,
							 | 
						|
										},
							 | 
						|
										Chunks: []*filer_pb.FileChunk{
							 | 
						|
											{
							 | 
						|
												FileId: "read-file-id",
							 | 
						|
												Offset: 0,
							 | 
						|
												Size:   1024,
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Test conditional reads with existing object
							 | 
						|
									t.Run("ConditionalReads_ObjectExists", func(t *testing.T) {
							 | 
						|
										// Test If-None-Match with existing object (should return 304 Not Modified)
							 | 
						|
										t.Run("IfNoneMatch_ObjectExists_ShouldReturn304", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "\"read123\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrNotModified {
							 | 
						|
												t.Errorf("Expected ErrNotModified when If-None-Match matches, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-None-Match=* with existing object (should return 304 Not Modified)
							 | 
						|
										t.Run("IfNoneMatchAsterisk_ObjectExists_ShouldReturn304", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "*")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrNotModified {
							 | 
						|
												t.Errorf("Expected ErrNotModified when If-None-Match=* with existing object, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-None-Match with non-matching ETag (should succeed)
							 | 
						|
										t.Run("IfNoneMatch_NonMatchingETag_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "\"different-etag\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when If-None-Match doesn't match, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-Match with matching ETag (should succeed)
							 | 
						|
										t.Run("IfMatch_MatchingETag_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfMatch, "\"read123\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when If-Match matches, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-Match with non-matching ETag (should return 412 Precondition Failed)
							 | 
						|
										t.Run("IfMatch_NonMatchingETag_ShouldReturn412", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfMatch, "\"different-etag\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when If-Match doesn't match, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-Match=* with existing object (should succeed)
							 | 
						|
										t.Run("IfMatchAsterisk_ObjectExists_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfMatch, "*")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when If-Match=* with existing object, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-Modified-Since (object modified after date - should succeed)
							 | 
						|
										t.Run("IfModifiedSince_ObjectModifiedAfter_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfModifiedSince, "Sat, 14 Jun 2024 12:00:00 GMT") // Before object mtime
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when object modified after If-Modified-Since date, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-Modified-Since (object not modified since date - should return 304)
							 | 
						|
										t.Run("IfModifiedSince_ObjectNotModified_ShouldReturn304", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfModifiedSince, "Sun, 16 Jun 2024 12:00:00 GMT") // After object mtime
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrNotModified {
							 | 
						|
												t.Errorf("Expected ErrNotModified when object not modified since If-Modified-Since date, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-Unmodified-Since (object not modified since date - should succeed)
							 | 
						|
										t.Run("IfUnmodifiedSince_ObjectNotModified_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfUnmodifiedSince, "Sun, 16 Jun 2024 12:00:00 GMT") // After object mtime
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when object not modified since If-Unmodified-Since date, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-Unmodified-Since (object modified since date - should return 412)
							 | 
						|
										t.Run("IfUnmodifiedSince_ObjectModified_ShouldReturn412", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfUnmodifiedSince, "Fri, 14 Jun 2024 12:00:00 GMT") // Before object mtime
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when object modified since If-Unmodified-Since date, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test conditional reads with non-existent object
							 | 
						|
									t.Run("ConditionalReads_ObjectNotExists", func(t *testing.T) {
							 | 
						|
										// Test If-None-Match with non-existent object (should succeed)
							 | 
						|
										t.Run("IfNoneMatch_ObjectNotExists_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "\"any-etag\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when object doesn't exist with If-None-Match, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-Match with non-existent object (should return 412)
							 | 
						|
										t.Run("IfMatch_ObjectNotExists_ShouldReturn412", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfMatch, "\"any-etag\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when object doesn't exist with If-Match, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-Modified-Since with non-existent object (should succeed)
							 | 
						|
										t.Run("IfModifiedSince_ObjectNotExists_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfModifiedSince, "Sat, 15 Jun 2024 12:00:00 GMT")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when object doesn't exist with If-Modified-Since, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test If-Unmodified-Since with non-existent object (should return 412)
							 | 
						|
										t.Run("IfUnmodifiedSince_ObjectNotExists_ShouldReturn412", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object
							 | 
						|
								
							 | 
						|
											req := createTestGetRequest(bucket, object)
							 | 
						|
											req.Header.Set(s3_constants.IfUnmodifiedSince, "Sat, 15 Jun 2024 12:00:00 GMT")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersForReadsWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode.ErrorCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when object doesn't exist with If-Unmodified-Since, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									})
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Helper function to create a GET request for testing
							 | 
						|
								func createTestGetRequest(bucket, object string) *http.Request {
							 | 
						|
									return &http.Request{
							 | 
						|
										Method: "GET",
							 | 
						|
										Header: make(http.Header),
							 | 
						|
										URL: &url.URL{
							 | 
						|
											Path: fmt.Sprintf("/%s%s", bucket, object),
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestConditionalHeadersWithNonExistentObjects tests the original scenarios (object doesn't exist)
							 | 
						|
								func TestConditionalHeadersWithNonExistentObjects(t *testing.T) {
							 | 
						|
									s3a := NewS3ApiServerForTest()
							 | 
						|
									if s3a == nil {
							 | 
						|
										t.Skip("S3ApiServer not available for testing")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									bucket := "test-bucket"
							 | 
						|
									object := "/test-object"
							 | 
						|
								
							 | 
						|
									// Test If-None-Match header when object doesn't exist
							 | 
						|
									t.Run("IfNoneMatch_ObjectDoesNotExist", func(t *testing.T) {
							 | 
						|
										// Test case 1: If-None-Match=* when object doesn't exist (should return ErrNone)
							 | 
						|
										t.Run("Asterisk_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object exists
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "*")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when object doesn't exist, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 2: If-None-Match with specific ETag when object doesn't exist
							 | 
						|
										t.Run("SpecificETag_ShouldSucceed", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object exists
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfNoneMatch, "\"some-etag\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone when object doesn't exist, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test If-Match header when object doesn't exist
							 | 
						|
									t.Run("IfMatch_ObjectDoesNotExist", func(t *testing.T) {
							 | 
						|
										// Test case 1: If-Match with specific ETag when object doesn't exist (should fail - critical bug fix)
							 | 
						|
										t.Run("SpecificETag_ShouldFail", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object exists
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfMatch, "\"some-etag\"")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when object doesn't exist with If-Match header, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 2: If-Match with wildcard * when object doesn't exist (should fail)
							 | 
						|
										t.Run("Wildcard_ShouldFail", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object exists
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfMatch, "*")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
												t.Errorf("Expected ErrPreconditionFailed when object doesn't exist with If-Match=*, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test date format validation (works regardless of object existence)
							 | 
						|
									t.Run("DateFormatValidation", func(t *testing.T) {
							 | 
						|
										// Test case 1: Valid If-Modified-Since date format
							 | 
						|
										t.Run("IfModifiedSince_ValidFormat", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object exists
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfModifiedSince, time.Now().Format(time.RFC1123))
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrNone {
							 | 
						|
												t.Errorf("Expected ErrNone with valid date format, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 2: Invalid If-Modified-Since date format
							 | 
						|
										t.Run("IfModifiedSince_InvalidFormat", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object exists
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfModifiedSince, "invalid-date")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrInvalidRequest {
							 | 
						|
												t.Errorf("Expected ErrInvalidRequest for invalid date format, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
								
							 | 
						|
										// Test case 3: Invalid If-Unmodified-Since date format
							 | 
						|
										t.Run("IfUnmodifiedSince_InvalidFormat", func(t *testing.T) {
							 | 
						|
											getter := createMockEntryGetter(nil) // No object exists
							 | 
						|
											req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
											req.Header.Set(s3_constants.IfUnmodifiedSince, "invalid-date")
							 | 
						|
								
							 | 
						|
											s3a := NewS3ApiServerForTest()
							 | 
						|
											errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
											if errCode != s3err.ErrInvalidRequest {
							 | 
						|
												t.Errorf("Expected ErrInvalidRequest for invalid date format, got %v", errCode)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test no conditional headers
							 | 
						|
									t.Run("NoConditionalHeaders", func(t *testing.T) {
							 | 
						|
										getter := createMockEntryGetter(nil) // No object exists
							 | 
						|
										req := createTestPutRequest(bucket, object, "test content")
							 | 
						|
										// Don't set any conditional headers
							 | 
						|
								
							 | 
						|
										s3a := NewS3ApiServerForTest()
							 | 
						|
										errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
										if errCode != s3err.ErrNone {
							 | 
						|
											t.Errorf("Expected ErrNone when no conditional headers, got %v", errCode)
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestETagMatching tests the etagMatches helper function
							 | 
						|
								func TestETagMatching(t *testing.T) {
							 | 
						|
									s3a := NewS3ApiServerForTest()
							 | 
						|
									if s3a == nil {
							 | 
						|
										t.Skip("S3ApiServer not available for testing")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									testCases := []struct {
							 | 
						|
										name        string
							 | 
						|
										headerValue string
							 | 
						|
										objectETag  string
							 | 
						|
										expected    bool
							 | 
						|
									}{
							 | 
						|
										{
							 | 
						|
											name:        "ExactMatch",
							 | 
						|
											headerValue: "\"abc123\"",
							 | 
						|
											objectETag:  "abc123",
							 | 
						|
											expected:    true,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:        "ExactMatchWithQuotes",
							 | 
						|
											headerValue: "\"abc123\"",
							 | 
						|
											objectETag:  "\"abc123\"",
							 | 
						|
											expected:    true,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:        "NoMatch",
							 | 
						|
											headerValue: "\"abc123\"",
							 | 
						|
											objectETag:  "def456",
							 | 
						|
											expected:    false,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:        "MultipleETags_FirstMatch",
							 | 
						|
											headerValue: "\"abc123\", \"def456\"",
							 | 
						|
											objectETag:  "abc123",
							 | 
						|
											expected:    true,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:        "MultipleETags_SecondMatch",
							 | 
						|
											headerValue: "\"abc123\", \"def456\"",
							 | 
						|
											objectETag:  "def456",
							 | 
						|
											expected:    true,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:        "MultipleETags_NoMatch",
							 | 
						|
											headerValue: "\"abc123\", \"def456\"",
							 | 
						|
											objectETag:  "ghi789",
							 | 
						|
											expected:    false,
							 | 
						|
										},
							 | 
						|
										{
							 | 
						|
											name:        "WithSpaces",
							 | 
						|
											headerValue: " \"abc123\" , \"def456\" ",
							 | 
						|
											objectETag:  "def456",
							 | 
						|
											expected:    true,
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									for _, tc := range testCases {
							 | 
						|
										t.Run(tc.name, func(t *testing.T) {
							 | 
						|
											result := s3a.etagMatches(tc.headerValue, tc.objectETag)
							 | 
						|
											if result != tc.expected {
							 | 
						|
												t.Errorf("Expected %v, got %v for headerValue='%s', objectETag='%s'",
							 | 
						|
													tc.expected, result, tc.headerValue, tc.objectETag)
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestConditionalHeadersIntegration tests conditional headers with full integration
							 | 
						|
								func TestConditionalHeadersIntegration(t *testing.T) {
							 | 
						|
									// This would be a full integration test that requires a running SeaweedFS instance
							 | 
						|
									t.Skip("Integration test - requires running SeaweedFS instance")
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// createTestPutRequest creates a test HTTP PUT request
							 | 
						|
								func createTestPutRequest(bucket, object, content string) *http.Request {
							 | 
						|
									req, _ := http.NewRequest("PUT", "/"+bucket+object, bytes.NewReader([]byte(content)))
							 | 
						|
									req.Header.Set("Content-Type", "application/octet-stream")
							 | 
						|
								
							 | 
						|
									// Set up mux vars to simulate the bucket and object extraction
							 | 
						|
									// In real tests, this would be handled by the gorilla mux router
							 | 
						|
									return req
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// NewS3ApiServerForTest creates a minimal S3ApiServer for testing
							 | 
						|
								// Note: This is a simplified version for unit testing conditional logic
							 | 
						|
								func NewS3ApiServerForTest() *S3ApiServer {
							 | 
						|
									// In a real test environment, this would set up a proper S3ApiServer
							 | 
						|
									// with filer connection, etc. For unit testing conditional header logic,
							 | 
						|
									// we create a minimal instance
							 | 
						|
									return &S3ApiServer{
							 | 
						|
										option: &S3ApiServerOption{
							 | 
						|
											BucketsPath: "/buckets",
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// MockEntryGetter implements the simplified EntryGetter interface for testing
							 | 
						|
								// Only mocks the data access dependency - tests use production getObjectETag and etagMatches
							 | 
						|
								type MockEntryGetter struct {
							 | 
						|
									mockEntry *filer_pb.Entry
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Implement only the simplified EntryGetter interface
							 | 
						|
								func (m *MockEntryGetter) getEntry(parentDirectoryPath, entryName string) (*filer_pb.Entry, error) {
							 | 
						|
									if m.mockEntry != nil {
							 | 
						|
										return m.mockEntry, nil
							 | 
						|
									}
							 | 
						|
									return nil, filer_pb.ErrNotFound
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// createMockEntryGetter creates a mock EntryGetter for testing
							 | 
						|
								func createMockEntryGetter(mockEntry *filer_pb.Entry) *MockEntryGetter {
							 | 
						|
									return &MockEntryGetter{
							 | 
						|
										mockEntry: mockEntry,
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// TestConditionalHeadersMultipartUpload tests conditional headers with multipart uploads
							 | 
						|
								// This verifies AWS S3 compatibility where conditional headers only apply to CompleteMultipartUpload
							 | 
						|
								func TestConditionalHeadersMultipartUpload(t *testing.T) {
							 | 
						|
									bucket := "test-bucket"
							 | 
						|
									object := "/test-multipart-object"
							 | 
						|
								
							 | 
						|
									// Mock existing object to test conditional headers against
							 | 
						|
									existingObject := &filer_pb.Entry{
							 | 
						|
										Name: "test-multipart-object",
							 | 
						|
										Extended: map[string][]byte{
							 | 
						|
											s3_constants.ExtETagKey: []byte("\"existing123\""),
							 | 
						|
										},
							 | 
						|
										Attributes: &filer_pb.FuseAttributes{
							 | 
						|
											Mtime:    time.Date(2024, 6, 15, 12, 0, 0, 0, time.UTC).Unix(),
							 | 
						|
											FileSize: 2048,
							 | 
						|
										},
							 | 
						|
										Chunks: []*filer_pb.FileChunk{
							 | 
						|
											{
							 | 
						|
												FileId: "existing-file-id",
							 | 
						|
												Offset: 0,
							 | 
						|
												Size:   2048,
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Test CompleteMultipartUpload with If-None-Match: * (should fail when object exists)
							 | 
						|
									t.Run("CompleteMultipartUpload_IfNoneMatchAsterisk_ObjectExists_ShouldFail", func(t *testing.T) {
							 | 
						|
										getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
										// Create a mock CompleteMultipartUpload request with If-None-Match: *
							 | 
						|
										req := &http.Request{
							 | 
						|
											Method: "POST",
							 | 
						|
											Header: make(http.Header),
							 | 
						|
											URL: &url.URL{
							 | 
						|
												RawQuery: "uploadId=test-upload-id",
							 | 
						|
											},
							 | 
						|
										}
							 | 
						|
										req.Header.Set(s3_constants.IfNoneMatch, "*")
							 | 
						|
								
							 | 
						|
										s3a := NewS3ApiServerForTest()
							 | 
						|
										errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
										if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
											t.Errorf("Expected ErrPreconditionFailed when object exists with If-None-Match=*, got %v", errCode)
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test CompleteMultipartUpload with If-None-Match: * (should succeed when object doesn't exist)
							 | 
						|
									t.Run("CompleteMultipartUpload_IfNoneMatchAsterisk_ObjectNotExists_ShouldSucceed", func(t *testing.T) {
							 | 
						|
										getter := createMockEntryGetter(nil) // No existing object
							 | 
						|
								
							 | 
						|
										req := &http.Request{
							 | 
						|
											Method: "POST",
							 | 
						|
											Header: make(http.Header),
							 | 
						|
											URL: &url.URL{
							 | 
						|
												RawQuery: "uploadId=test-upload-id",
							 | 
						|
											},
							 | 
						|
										}
							 | 
						|
										req.Header.Set(s3_constants.IfNoneMatch, "*")
							 | 
						|
								
							 | 
						|
										s3a := NewS3ApiServerForTest()
							 | 
						|
										errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
										if errCode != s3err.ErrNone {
							 | 
						|
											t.Errorf("Expected ErrNone when object doesn't exist with If-None-Match=*, got %v", errCode)
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test CompleteMultipartUpload with If-Match (should succeed when ETag matches)
							 | 
						|
									t.Run("CompleteMultipartUpload_IfMatch_ETagMatches_ShouldSucceed", func(t *testing.T) {
							 | 
						|
										getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
										req := &http.Request{
							 | 
						|
											Method: "POST",
							 | 
						|
											Header: make(http.Header),
							 | 
						|
											URL: &url.URL{
							 | 
						|
												RawQuery: "uploadId=test-upload-id",
							 | 
						|
											},
							 | 
						|
										}
							 | 
						|
										req.Header.Set(s3_constants.IfMatch, "\"existing123\"")
							 | 
						|
								
							 | 
						|
										s3a := NewS3ApiServerForTest()
							 | 
						|
										errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
										if errCode != s3err.ErrNone {
							 | 
						|
											t.Errorf("Expected ErrNone when ETag matches, got %v", errCode)
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test CompleteMultipartUpload with If-Match (should fail when object doesn't exist)
							 | 
						|
									t.Run("CompleteMultipartUpload_IfMatch_ObjectNotExists_ShouldFail", func(t *testing.T) {
							 | 
						|
										getter := createMockEntryGetter(nil) // No existing object
							 | 
						|
								
							 | 
						|
										req := &http.Request{
							 | 
						|
											Method: "POST",
							 | 
						|
											Header: make(http.Header),
							 | 
						|
											URL: &url.URL{
							 | 
						|
												RawQuery: "uploadId=test-upload-id",
							 | 
						|
											},
							 | 
						|
										}
							 | 
						|
										req.Header.Set(s3_constants.IfMatch, "\"any-etag\"")
							 | 
						|
								
							 | 
						|
										s3a := NewS3ApiServerForTest()
							 | 
						|
										errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
										if errCode != s3err.ErrPreconditionFailed {
							 | 
						|
											t.Errorf("Expected ErrPreconditionFailed when object doesn't exist with If-Match, got %v", errCode)
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									// Test CompleteMultipartUpload with If-Match wildcard (should succeed when object exists)
							 | 
						|
									t.Run("CompleteMultipartUpload_IfMatchWildcard_ObjectExists_ShouldSucceed", func(t *testing.T) {
							 | 
						|
										getter := createMockEntryGetter(existingObject)
							 | 
						|
								
							 | 
						|
										req := &http.Request{
							 | 
						|
											Method: "POST",
							 | 
						|
											Header: make(http.Header),
							 | 
						|
											URL: &url.URL{
							 | 
						|
												RawQuery: "uploadId=test-upload-id",
							 | 
						|
											},
							 | 
						|
										}
							 | 
						|
										req.Header.Set(s3_constants.IfMatch, "*")
							 | 
						|
								
							 | 
						|
										s3a := NewS3ApiServerForTest()
							 | 
						|
										errCode := s3a.checkConditionalHeadersWithGetter(getter, req, bucket, object)
							 | 
						|
										if errCode != s3err.ErrNone {
							 | 
						|
											t.Errorf("Expected ErrNone when object exists with If-Match=*, got %v", errCode)
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								}
							 |