package s3 import ( "testing" "atcr.io/pkg/logging" ) func TestNewS3Service_MissingBucket(t *testing.T) { t.Cleanup(logging.SetupTestLogger()) params := map[string]any{ "region": "us-east-1", "accesskey": "test-key", "secretkey": "test-secret", // Missing bucket } _, err := NewS3Service(params) if err == nil { t.Error("Expected error when bucket is missing") } } func TestNewS3Service_Success(t *testing.T) { t.Cleanup(logging.SetupTestLogger()) params := map[string]any{ "bucket": "test-bucket", "region": "us-west-2", "accesskey": "test-access-key", "secretkey": "test-secret-key", } service, err := NewS3Service(params) if err != nil { t.Fatalf("Expected success, got error: %v", err) } if service.Client == nil { t.Error("Expected Client to be initialized") } if service.Bucket != "test-bucket" { t.Errorf("Expected Bucket=test-bucket, got %s", service.Bucket) } if service.PathPrefix != "" { t.Errorf("Expected empty PathPrefix, got %s", service.PathPrefix) } } func TestNewS3Service_WithEndpoint(t *testing.T) { t.Cleanup(logging.SetupTestLogger()) params := map[string]any{ "bucket": "test-bucket", "region": "us-east-1", "accesskey": "test-key", "secretkey": "test-secret", "regionendpoint": "https://s3.storj.io", } service, err := NewS3Service(params) if err != nil { t.Fatalf("Expected success with custom endpoint, got error: %v", err) } if service.Client == nil { t.Error("Expected Client to be initialized") } if service.Bucket != "test-bucket" { t.Errorf("Expected Bucket=test-bucket, got %s", service.Bucket) } } func TestNewS3Service_DefaultRegion(t *testing.T) { t.Cleanup(logging.SetupTestLogger()) params := map[string]any{ "bucket": "test-bucket", "accesskey": "test-key", "secretkey": "test-secret", // No region specified - should use default } service, err := NewS3Service(params) if err != nil { t.Fatalf("Expected success with default region, got error: %v", err) } if service.Client == nil { t.Error("Expected Client to be initialized") } // Note: We can't easily verify the region without accessing private fields // but the fact that it didn't error means it used the default } func TestNewS3Service_WithPathPrefix(t *testing.T) { t.Cleanup(logging.SetupTestLogger()) params := map[string]any{ "bucket": "test-bucket", "region": "us-east-1", "accesskey": "test-key", "secretkey": "test-secret", "rootdirectory": "/my/prefix/path", } service, err := NewS3Service(params) if err != nil { t.Fatalf("Expected success, got error: %v", err) } if service.PathPrefix != "my/prefix/path" { t.Errorf("Expected PathPrefix=my/prefix/path (leading slash stripped), got %s", service.PathPrefix) } } func TestNewS3Service_NoCredentials(t *testing.T) { t.Cleanup(logging.SetupTestLogger()) params := map[string]any{ "bucket": "test-bucket", "region": "us-east-1", // No credentials - should allow IAM role auth } service, err := NewS3Service(params) if err != nil { t.Fatalf("Expected success without credentials (IAM role), got error: %v", err) } if service.Client == nil { t.Error("Expected Client to be initialized") } } func TestBlobPath_SHA256(t *testing.T) { tests := []struct { name string digest string expected string }{ { name: "standard sha256 digest", digest: "sha256:abc123def456", expected: "/docker/registry/v2/blobs/sha256/ab/abc123def456/data", }, { name: "short hash (less than 2 chars)", digest: "sha256:a", expected: "/docker/registry/v2/blobs/sha256/a/data", }, { name: "exactly 2 char hash", digest: "sha256:ab", expected: "/docker/registry/v2/blobs/sha256/ab/ab/data", }, { name: "long sha256", digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", expected: "/docker/registry/v2/blobs/sha256/e3/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/data", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := BlobPath(tt.digest) if result != tt.expected { t.Errorf("Expected %s, got %s", tt.expected, result) } }) } } func TestBlobPath_TempUpload(t *testing.T) { tests := []struct { name string digest string expected string }{ { name: "temp upload path", digest: "uploads/temp-uuid-123", expected: "/docker/registry/v2/uploads/temp-uuid-123/data", }, { name: "temp upload with different uuid", digest: "uploads/temp-abc-def-456", expected: "/docker/registry/v2/uploads/temp-abc-def-456/data", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := BlobPath(tt.digest) if result != tt.expected { t.Errorf("Expected %s, got %s", tt.expected, result) } }) } } func TestBlobPath_MalformedDigest(t *testing.T) { tests := []struct { name string digest string expected string }{ { name: "no colon in digest", digest: "malformed-digest", expected: "/docker/registry/v2/blobs/malformed-digest/data", }, { name: "empty digest", digest: "", expected: "/docker/registry/v2/blobs//data", }, { name: "only algorithm", digest: "sha256:", expected: "/docker/registry/v2/blobs/sha256//data", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := BlobPath(tt.digest) if result != tt.expected { t.Errorf("Expected %s, got %s", tt.expected, result) } }) } }