mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-31 13:17:09 +00:00
590 lines
15 KiB
Go
590 lines
15 KiB
Go
package pds
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
|
|
"atcr.io/pkg/atproto"
|
|
)
|
|
|
|
// TestAddCrewMember tests adding a single crew member
|
|
func TestAddCrewMember(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// Add crew member
|
|
memberDID := "did:plc:alice123"
|
|
role := "writer"
|
|
permissions := []string{"blob:read", "blob:write"}
|
|
|
|
recordCID, err := pds.AddCrewMember(ctx, memberDID, role, permissions)
|
|
if err != nil {
|
|
t.Fatalf("AddCrewMember failed: %v", err)
|
|
}
|
|
|
|
// Verify CID is defined
|
|
if !recordCID.Defined() {
|
|
t.Error("Expected defined CID")
|
|
}
|
|
|
|
// List crew members to verify
|
|
crewMembers, err := pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) != 1 {
|
|
t.Fatalf("Expected 1 crew member, got %d", len(crewMembers))
|
|
}
|
|
|
|
crew := crewMembers[0]
|
|
if crew.Record.Member != memberDID {
|
|
t.Errorf("Expected member %s, got %s", memberDID, crew.Record.Member)
|
|
}
|
|
if crew.Record.Role != role {
|
|
t.Errorf("Expected role %s, got %s", role, crew.Record.Role)
|
|
}
|
|
if len(crew.Record.Permissions) != len(permissions) {
|
|
t.Fatalf("Expected %d permissions, got %d", len(permissions), len(crew.Record.Permissions))
|
|
}
|
|
for i, perm := range permissions {
|
|
if crew.Record.Permissions[i] != perm {
|
|
t.Errorf("Expected permission[%d]=%s, got %s", i, perm, crew.Record.Permissions[i])
|
|
}
|
|
}
|
|
if crew.Record.Type != atproto.CrewCollection {
|
|
t.Errorf("Expected type %s, got %s", atproto.CrewCollection, crew.Record.Type)
|
|
}
|
|
if crew.Record.AddedAt == "" {
|
|
t.Error("Expected addedAt to be set")
|
|
}
|
|
}
|
|
|
|
// TestGetCrewMember tests retrieving a crew member by rkey
|
|
func TestGetCrewMember(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// Add crew member
|
|
memberDID := "did:plc:bob456"
|
|
role := "reader"
|
|
permissions := []string{"blob:read"}
|
|
|
|
_, err := pds.AddCrewMember(ctx, memberDID, role, permissions)
|
|
if err != nil {
|
|
t.Fatalf("AddCrewMember failed: %v", err)
|
|
}
|
|
|
|
// List to get the rkey
|
|
crewMembers, err := pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) == 0 {
|
|
t.Fatal("Expected at least one crew member")
|
|
}
|
|
|
|
rkey := crewMembers[0].Rkey
|
|
|
|
// Get crew member by rkey
|
|
retrievedCID, crew, err := pds.GetCrewMember(ctx, rkey)
|
|
if err != nil {
|
|
t.Fatalf("GetCrewMember failed: %v", err)
|
|
}
|
|
|
|
// Verify CID matches
|
|
if !crewMembers[0].Cid.Equals(retrievedCID) {
|
|
t.Error("Expected retrieved CID to match")
|
|
}
|
|
|
|
// Verify crew data
|
|
if crew.Member != memberDID {
|
|
t.Errorf("Expected member %s, got %s", memberDID, crew.Member)
|
|
}
|
|
if crew.Role != role {
|
|
t.Errorf("Expected role %s, got %s", role, crew.Role)
|
|
}
|
|
if len(crew.Permissions) != len(permissions) {
|
|
t.Fatalf("Expected %d permissions, got %d", len(permissions), len(crew.Permissions))
|
|
}
|
|
}
|
|
|
|
// TestGetCrewMember_NotFound tests error handling for missing crew
|
|
func TestGetCrewMember_NotFound(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// Try to get non-existent crew member
|
|
_, _, err := pds.GetCrewMember(ctx, "nonexistent-rkey")
|
|
if err == nil {
|
|
t.Fatal("Expected error when getting non-existent crew member")
|
|
}
|
|
|
|
// Verify error message
|
|
errMsg := err.Error()
|
|
if !strings.Contains(errMsg, "failed to get crew record") {
|
|
t.Errorf("Expected 'failed to get crew record' in error, got: %s", errMsg)
|
|
}
|
|
}
|
|
|
|
// TestListCrewMembers_Empty tests listing when no crew exists
|
|
func TestListCrewMembers_Empty(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// List crew members (should be empty)
|
|
crewMembers, err := pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) != 0 {
|
|
t.Errorf("Expected 0 crew members, got %d", len(crewMembers))
|
|
}
|
|
}
|
|
|
|
// TestListCrewMembers_Multiple tests listing with multiple crew members
|
|
func TestListCrewMembers_Multiple(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// Add multiple crew members
|
|
members := []struct {
|
|
did string
|
|
role string
|
|
permissions []string
|
|
}{
|
|
{
|
|
did: "did:plc:alice123",
|
|
role: "admin",
|
|
permissions: []string{"blob:read", "blob:write", "crew:admin"},
|
|
},
|
|
{
|
|
did: "did:plc:bob456",
|
|
role: "writer",
|
|
permissions: []string{"blob:read", "blob:write"},
|
|
},
|
|
{
|
|
did: "did:plc:charlie789",
|
|
role: "reader",
|
|
permissions: []string{"blob:read"},
|
|
},
|
|
}
|
|
|
|
for _, m := range members {
|
|
_, err := pds.AddCrewMember(ctx, m.did, m.role, m.permissions)
|
|
if err != nil {
|
|
t.Fatalf("AddCrewMember failed for %s: %v", m.did, err)
|
|
}
|
|
}
|
|
|
|
// List all crew members
|
|
crewMembers, err := pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) != len(members) {
|
|
t.Fatalf("Expected %d crew members, got %d", len(members), len(crewMembers))
|
|
}
|
|
|
|
// Verify each crew member (order may vary, so check by DID)
|
|
foundMembers := make(map[string]*CrewMemberWithKey)
|
|
for _, cm := range crewMembers {
|
|
foundMembers[cm.Record.Member] = cm
|
|
}
|
|
|
|
for _, m := range members {
|
|
crew, found := foundMembers[m.did]
|
|
if !found {
|
|
t.Errorf("Expected to find crew member %s", m.did)
|
|
continue
|
|
}
|
|
|
|
if crew.Record.Role != m.role {
|
|
t.Errorf("Expected role %s for %s, got %s", m.role, m.did, crew.Record.Role)
|
|
}
|
|
|
|
if len(crew.Record.Permissions) != len(m.permissions) {
|
|
t.Errorf("Expected %d permissions for %s, got %d", len(m.permissions), m.did, len(crew.Record.Permissions))
|
|
}
|
|
|
|
// Verify rkey is set
|
|
if crew.Rkey == "" {
|
|
t.Errorf("Expected non-empty rkey for %s", m.did)
|
|
}
|
|
|
|
// Verify CID is defined
|
|
if !crew.Cid.Defined() {
|
|
t.Errorf("Expected defined CID for %s", m.did)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestRemoveCrewMember tests deleting a crew member
|
|
func TestRemoveCrewMember(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// Add crew member
|
|
memberDID := "did:plc:alice123"
|
|
_, err := pds.AddCrewMember(ctx, memberDID, "writer", []string{"blob:read", "blob:write"})
|
|
if err != nil {
|
|
t.Fatalf("AddCrewMember failed: %v", err)
|
|
}
|
|
|
|
// List to get the rkey
|
|
crewMembers, err := pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) != 1 {
|
|
t.Fatalf("Expected 1 crew member, got %d", len(crewMembers))
|
|
}
|
|
|
|
rkey := crewMembers[0].Rkey
|
|
|
|
// Remove crew member
|
|
err = pds.RemoveCrewMember(ctx, rkey)
|
|
if err != nil {
|
|
t.Fatalf("RemoveCrewMember failed: %v", err)
|
|
}
|
|
|
|
// Verify crew member is gone
|
|
crewMembers, err = pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed after removal: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) != 0 {
|
|
t.Errorf("Expected 0 crew members after removal, got %d", len(crewMembers))
|
|
}
|
|
|
|
// Try to get removed crew member (should fail)
|
|
_, _, err = pds.GetCrewMember(ctx, rkey)
|
|
if err == nil {
|
|
t.Error("Expected error when getting removed crew member")
|
|
}
|
|
}
|
|
|
|
// TestRemoveCrewMember_NotFound tests removing non-existent crew member
|
|
func TestRemoveCrewMember_NotFound(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// Try to remove non-existent crew member
|
|
err := pds.RemoveCrewMember(ctx, "nonexistent-rkey")
|
|
if err == nil {
|
|
t.Fatal("Expected error when removing non-existent crew member")
|
|
}
|
|
|
|
// Verify error message
|
|
errMsg := err.Error()
|
|
if !strings.Contains(errMsg, "failed to delete crew record") {
|
|
t.Errorf("Expected 'failed to delete crew record' in error, got: %s", errMsg)
|
|
}
|
|
}
|
|
|
|
// TestRemoveCrewMember_Multiple tests removing one crew member from many
|
|
func TestRemoveCrewMember_Multiple(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// Add multiple crew members
|
|
dids := []string{
|
|
"did:plc:alice123",
|
|
"did:plc:bob456",
|
|
"did:plc:charlie789",
|
|
}
|
|
|
|
for _, did := range dids {
|
|
_, err := pds.AddCrewMember(ctx, did, "writer", []string{"blob:read"})
|
|
if err != nil {
|
|
t.Fatalf("AddCrewMember failed for %s: %v", did, err)
|
|
}
|
|
}
|
|
|
|
// List crew members
|
|
crewMembers, err := pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) != 3 {
|
|
t.Fatalf("Expected 3 crew members, got %d", len(crewMembers))
|
|
}
|
|
|
|
// Remove middle member
|
|
middleRkey := crewMembers[1].Rkey
|
|
middleDID := crewMembers[1].Record.Member
|
|
|
|
err = pds.RemoveCrewMember(ctx, middleRkey)
|
|
if err != nil {
|
|
t.Fatalf("RemoveCrewMember failed: %v", err)
|
|
}
|
|
|
|
// Verify only 2 remain
|
|
crewMembers, err = pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed after removal: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) != 2 {
|
|
t.Fatalf("Expected 2 crew members after removal, got %d", len(crewMembers))
|
|
}
|
|
|
|
// Verify removed member is not in list
|
|
for _, cm := range crewMembers {
|
|
if cm.Record.Member == middleDID {
|
|
t.Errorf("Expected %s to be removed, but still found in list", middleDID)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestCrewRecord_CBORRoundtrip tests CBOR marshal/unmarshal integrity
|
|
func TestCrewRecord_CBORRoundtrip(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
record *atproto.CrewRecord
|
|
}{
|
|
{
|
|
name: "Basic crew member",
|
|
record: &atproto.CrewRecord{
|
|
Type: atproto.CrewCollection,
|
|
Member: "did:plc:alice123",
|
|
Role: "writer",
|
|
Permissions: []string{"blob:read", "blob:write"},
|
|
AddedAt: "2025-10-16T12:00:00Z",
|
|
},
|
|
},
|
|
{
|
|
name: "Admin crew member",
|
|
record: &atproto.CrewRecord{
|
|
Type: atproto.CrewCollection,
|
|
Member: "did:plc:bob456",
|
|
Role: "admin",
|
|
Permissions: []string{"blob:read", "blob:write", "crew:admin"},
|
|
AddedAt: "2025-10-16T13:00:00Z",
|
|
},
|
|
},
|
|
{
|
|
name: "Reader crew member",
|
|
record: &atproto.CrewRecord{
|
|
Type: atproto.CrewCollection,
|
|
Member: "did:plc:charlie789",
|
|
Role: "reader",
|
|
Permissions: []string{"blob:read"},
|
|
AddedAt: "2025-10-16T14:00:00Z",
|
|
},
|
|
},
|
|
{
|
|
name: "Crew member with empty permissions",
|
|
record: &atproto.CrewRecord{
|
|
Type: atproto.CrewCollection,
|
|
Member: "did:plc:dave012",
|
|
Role: "none",
|
|
Permissions: []string{},
|
|
AddedAt: "2025-10-16T15:00:00Z",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Marshal to CBOR
|
|
var buf bytes.Buffer
|
|
err := tt.record.MarshalCBOR(&buf)
|
|
if err != nil {
|
|
t.Fatalf("MarshalCBOR failed: %v", err)
|
|
}
|
|
|
|
cborBytes := buf.Bytes()
|
|
if len(cborBytes) == 0 {
|
|
t.Fatal("Expected non-empty CBOR bytes")
|
|
}
|
|
|
|
// Unmarshal from CBOR
|
|
var decoded atproto.CrewRecord
|
|
err = decoded.UnmarshalCBOR(bytes.NewReader(cborBytes))
|
|
if err != nil {
|
|
t.Fatalf("UnmarshalCBOR failed: %v", err)
|
|
}
|
|
|
|
// Verify all fields match
|
|
if decoded.Type != tt.record.Type {
|
|
t.Errorf("Type mismatch: expected %s, got %s", tt.record.Type, decoded.Type)
|
|
}
|
|
if decoded.Member != tt.record.Member {
|
|
t.Errorf("Member mismatch: expected %s, got %s", tt.record.Member, decoded.Member)
|
|
}
|
|
if decoded.Role != tt.record.Role {
|
|
t.Errorf("Role mismatch: expected %s, got %s", tt.record.Role, decoded.Role)
|
|
}
|
|
if decoded.AddedAt != tt.record.AddedAt {
|
|
t.Errorf("AddedAt mismatch: expected %s, got %s", tt.record.AddedAt, decoded.AddedAt)
|
|
}
|
|
|
|
// Verify permissions
|
|
if len(decoded.Permissions) != len(tt.record.Permissions) {
|
|
t.Fatalf("Permissions length mismatch: expected %d, got %d", len(tt.record.Permissions), len(decoded.Permissions))
|
|
}
|
|
for i, perm := range tt.record.Permissions {
|
|
if decoded.Permissions[i] != perm {
|
|
t.Errorf("Permission[%d] mismatch: expected %s, got %s", i, perm, decoded.Permissions[i])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestCrewMemberWithKey_Structure tests the CrewMemberWithKey struct
|
|
func TestCrewMemberWithKey_Structure(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// Add crew member
|
|
memberDID := "did:plc:alice123"
|
|
_, err := pds.AddCrewMember(ctx, memberDID, "writer", []string{"blob:read"})
|
|
if err != nil {
|
|
t.Fatalf("AddCrewMember failed: %v", err)
|
|
}
|
|
|
|
// List crew members
|
|
crewMembers, err := pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) != 1 {
|
|
t.Fatalf("Expected 1 crew member, got %d", len(crewMembers))
|
|
}
|
|
|
|
cm := crewMembers[0]
|
|
|
|
// Verify CrewMemberWithKey structure
|
|
if cm.Rkey == "" {
|
|
t.Error("Expected non-empty Rkey")
|
|
}
|
|
if !cm.Cid.Defined() {
|
|
t.Error("Expected defined Cid")
|
|
}
|
|
if cm.Record == nil {
|
|
t.Fatal("Expected non-nil Record")
|
|
}
|
|
if cm.Record.Member != memberDID {
|
|
t.Errorf("Expected member %s, got %s", memberDID, cm.Record.Member)
|
|
}
|
|
}
|
|
|
|
// TestAddCrewMember_DidWeb tests adding crew members with did:web DIDs
|
|
func TestAddCrewMember_DidWeb(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// Add crew member with did:web
|
|
memberDID := "did:web:alice.example.com"
|
|
role := "writer"
|
|
permissions := []string{"blob:read", "blob:write"}
|
|
|
|
recordCID, err := pds.AddCrewMember(ctx, memberDID, role, permissions)
|
|
if err != nil {
|
|
t.Fatalf("AddCrewMember failed with did:web: %v", err)
|
|
}
|
|
|
|
if !recordCID.Defined() {
|
|
t.Error("Expected defined CID")
|
|
}
|
|
|
|
// Verify crew member was added
|
|
crewMembers, err := pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) != 1 {
|
|
t.Fatalf("Expected 1 crew member, got %d", len(crewMembers))
|
|
}
|
|
|
|
crew := crewMembers[0]
|
|
if crew.Record.Member != memberDID {
|
|
t.Errorf("Expected member %s, got %s", memberDID, crew.Record.Member)
|
|
}
|
|
|
|
// Verify we can get it by rkey
|
|
_, retrievedCrew, err := pds.GetCrewMember(ctx, crew.Rkey)
|
|
if err != nil {
|
|
t.Fatalf("GetCrewMember failed for did:web: %v", err)
|
|
}
|
|
|
|
if retrievedCrew.Member != memberDID {
|
|
t.Errorf("Expected member %s, got %s", memberDID, retrievedCrew.Member)
|
|
}
|
|
}
|
|
|
|
// TestListCrewMembers_MixedDIDs tests listing crew members with mixed DID types
|
|
func TestListCrewMembers_MixedDIDs(t *testing.T) {
|
|
pds, ctx := setupTestPDS(t)
|
|
defer pds.Close()
|
|
|
|
// Add crew members with different DID types
|
|
members := []struct {
|
|
did string
|
|
role string
|
|
permissions []string
|
|
}{
|
|
{
|
|
did: "did:plc:alice123",
|
|
role: "admin",
|
|
permissions: []string{"blob:read", "blob:write", "crew:admin"},
|
|
},
|
|
{
|
|
did: "did:web:bob.example.com",
|
|
role: "writer",
|
|
permissions: []string{"blob:read", "blob:write"},
|
|
},
|
|
{
|
|
did: "did:web:charlie.example.org",
|
|
role: "reader",
|
|
permissions: []string{"blob:read"},
|
|
},
|
|
}
|
|
|
|
for _, m := range members {
|
|
_, err := pds.AddCrewMember(ctx, m.did, m.role, m.permissions)
|
|
if err != nil {
|
|
t.Fatalf("AddCrewMember failed for %s: %v", m.did, err)
|
|
}
|
|
}
|
|
|
|
// List all crew members
|
|
crewMembers, err := pds.ListCrewMembers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ListCrewMembers failed: %v", err)
|
|
}
|
|
|
|
if len(crewMembers) != len(members) {
|
|
t.Fatalf("Expected %d crew members, got %d", len(members), len(crewMembers))
|
|
}
|
|
|
|
// Verify each crew member exists (order may vary)
|
|
foundMembers := make(map[string]*CrewMemberWithKey)
|
|
for _, cm := range crewMembers {
|
|
foundMembers[cm.Record.Member] = cm
|
|
}
|
|
|
|
for _, m := range members {
|
|
crew, found := foundMembers[m.did]
|
|
if !found {
|
|
t.Errorf("Expected to find crew member %s", m.did)
|
|
continue
|
|
}
|
|
|
|
if crew.Record.Role != m.role {
|
|
t.Errorf("Expected role %s for %s, got %s", m.role, m.did, crew.Record.Role)
|
|
}
|
|
}
|
|
}
|