package pds import ( "testing" "atcr.io/pkg/atproto" ) func TestCreateLayerRecord(t *testing.T) { // Setup test PDS pds, ctx := setupTestPDS(t) tests := []struct { name string record *atproto.LayerRecord wantErr bool errSubstr string }{ { name: "valid layer record", record: atproto.NewLayerRecord( "sha256:abc123def456", 1048576, // 1 MB "application/vnd.oci.image.layer.v1.tar+gzip", "myapp", "did:plc:alice123", "alice.bsky.social", ), wantErr: false, }, { name: "valid layer record with large size", record: atproto.NewLayerRecord( "sha256:fedcba987654", 1073741824, // 1 GB "application/vnd.docker.image.rootfs.diff.tar.gzip", "debian", "did:plc:bob456", "bob.example.com", ), wantErr: false, }, { name: "invalid record type", record: &atproto.LayerRecord{ Type: "wrong.type", Digest: "sha256:abc123", Size: 1024, MediaType: "application/vnd.oci.image.layer.v1.tar", Repository: "test", UserDID: "did:plc:test", UserHandle: "test.example.com", }, wantErr: true, errSubstr: "invalid record type", }, { name: "missing digest", record: &atproto.LayerRecord{ Type: atproto.LayerCollection, Digest: "", Size: 1024, MediaType: "application/vnd.oci.image.layer.v1.tar", Repository: "test", UserDID: "did:plc:test", UserHandle: "test.example.com", }, wantErr: true, errSubstr: "digest is required", }, { name: "zero size", record: &atproto.LayerRecord{ Type: atproto.LayerCollection, Digest: "sha256:abc123", Size: 0, MediaType: "application/vnd.oci.image.layer.v1.tar", Repository: "test", UserDID: "did:plc:test", UserHandle: "test.example.com", }, wantErr: true, errSubstr: "size must be positive", }, { name: "negative size", record: &atproto.LayerRecord{ Type: atproto.LayerCollection, Digest: "sha256:abc123", Size: -1, MediaType: "application/vnd.oci.image.layer.v1.tar", Repository: "test", UserDID: "did:plc:test", UserHandle: "test.example.com", }, wantErr: true, errSubstr: "size must be positive", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rkey, cid, err := pds.CreateLayerRecord(ctx, tt.record) if tt.wantErr { if err == nil { t.Errorf("CreateLayerRecord() expected error containing %q, got nil", tt.errSubstr) return } if tt.errSubstr != "" && !contains(err.Error(), tt.errSubstr) { t.Errorf("CreateLayerRecord() error = %v, want error containing %q", err, tt.errSubstr) } return } if err != nil { t.Errorf("CreateLayerRecord() unexpected error: %v", err) return } if rkey == "" { t.Error("CreateLayerRecord() returned empty rkey") } if cid == "" { t.Error("CreateLayerRecord() returned empty CID") } t.Logf("Created layer record: rkey=%s, cid=%s", rkey, cid) }) } } func TestCreateLayerRecord_MultipleRecords(t *testing.T) { // Test creating multiple layer records for the same manifest pds, ctx := setupTestPDS(t) layers := []struct { digest string size int64 }{ {"sha256:layer1abc123", 1024}, {"sha256:layer2def456", 2048}, {"sha256:layer3ghi789", 4096}, } createdRKeys := make(map[string]bool) for i, layer := range layers { record := atproto.NewLayerRecord( layer.digest, layer.size, "application/vnd.oci.image.layer.v1.tar+gzip", "multi-layer-app", "did:plc:test123", "test.example.com", ) rkey, cid, err := pds.CreateLayerRecord(ctx, record) if err != nil { t.Fatalf("CreateLayerRecord() for layer %d failed: %v", i, err) } // Ensure unique rkeys if createdRKeys[rkey] { t.Errorf("CreateLayerRecord() returned duplicate rkey: %s", rkey) } createdRKeys[rkey] = true t.Logf("Layer %d: rkey=%s, cid=%s", i, rkey, cid) } if len(createdRKeys) != len(layers) { t.Errorf("Created %d unique rkeys, want %d", len(createdRKeys), len(layers)) } } func TestNewLayerRecord(t *testing.T) { // Test the layer record constructor digest := "sha256:abc123def456" size := int64(1048576) mediaType := "application/vnd.oci.image.layer.v1.tar+gzip" repository := "myapp" userDID := "did:plc:alice123" userHandle := "alice.bsky.social" record := atproto.NewLayerRecord(digest, size, mediaType, repository, userDID, userHandle) if record == nil { t.Fatal("NewLayerRecord() returned nil") } // Verify all fields are set correctly if record.Type != atproto.LayerCollection { t.Errorf("Type = %q, want %q", record.Type, atproto.LayerCollection) } if record.Digest != digest { t.Errorf("Digest = %q, want %q", record.Digest, digest) } if record.Size != size { t.Errorf("Size = %d, want %d", record.Size, size) } if record.MediaType != mediaType { t.Errorf("MediaType = %q, want %q", record.MediaType, mediaType) } if record.Repository != repository { t.Errorf("Repository = %q, want %q", record.Repository, repository) } if record.UserDID != userDID { t.Errorf("UserDID = %q, want %q", record.UserDID, userDID) } if record.UserHandle != userHandle { t.Errorf("UserHandle = %q, want %q", record.UserHandle, userHandle) } if record.CreatedAt == "" { t.Error("CreatedAt is empty") } t.Logf("Created layer record: %+v", record) } func TestLayerRecord_FieldValidation(t *testing.T) { // Test various field values tests := []struct { name string digest string size int64 mediaType string repository string userDID string userHandle string }{ { name: "typical OCI layer", digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f", size: 12582912, // 12 MB mediaType: "application/vnd.oci.image.layer.v1.tar+gzip", repository: "hsm-secrets-operator", userDID: "did:plc:evan123", userHandle: "evan.jarrett.net", }, { name: "Docker layer format", digest: "sha256:abc123", size: 1024, mediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", repository: "nginx", userDID: "did:plc:user456", userHandle: "user.example.com", }, { name: "uncompressed layer", digest: "sha256:def456", size: 2048, mediaType: "application/vnd.oci.image.layer.v1.tar", repository: "alpine", userDID: "did:plc:user789", userHandle: "user.bsky.social", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { record := atproto.NewLayerRecord( tt.digest, tt.size, tt.mediaType, tt.repository, tt.userDID, tt.userHandle, ) if record == nil { t.Fatal("NewLayerRecord() returned nil") } // Verify the record can be created if record.Type != atproto.LayerCollection { t.Errorf("Type = %q, want %q", record.Type, atproto.LayerCollection) } if record.Digest != tt.digest { t.Errorf("Digest = %q, want %q", record.Digest, tt.digest) } }) } }