package db import ( "database/sql" "fmt" "strings" "testing" ) func TestAnnotations_Placeholder(t *testing.T) { // Placeholder test for annotations package // GetRepositoryAnnotations returns map[string]string annotations := make(map[string]string) annotations["test"] = "value" if annotations["test"] != "value" { t.Error("Expected annotation value to be stored") } } // Integration tests func setupAnnotationsTestDB(t *testing.T) *sql.DB { t.Helper() // Use a named in-memory DB unique to this test to ensure isolation between tests safeName := strings.ReplaceAll(t.Name(), "/", "_") db, err := InitDB(fmt.Sprintf("file:%s?mode=memory&cache=shared", safeName), LibsqlConfig{}) if err != nil { t.Fatalf("Failed to initialize test database: %v", err) } // Limit to single connection to avoid race conditions in tests db.SetMaxOpenConns(1) t.Cleanup(func() { db.Close() }) return db } func createAnnotationTestUser(t *testing.T, db *sql.DB, did, handle string) { t.Helper() _, err := db.Exec(` INSERT OR IGNORE INTO users (did, handle, pds_endpoint, last_seen) VALUES (?, ?, ?, datetime('now')) `, did, handle, "https://pds.example.com") if err != nil { t.Fatalf("Failed to create test user: %v", err) } } // TestGetRepositoryAnnotations_Empty tests retrieving from empty repository func TestGetRepositoryAnnotations_Empty(t *testing.T) { db := setupAnnotationsTestDB(t) annotations, err := GetRepositoryAnnotations(db, "did:plc:alice123", "myapp") if err != nil { t.Fatalf("GetRepositoryAnnotations() error = %v", err) } if len(annotations) != 0 { t.Errorf("Expected empty annotations, got %d entries", len(annotations)) } } // TestGetRepositoryAnnotations_WithData tests retrieving existing annotations func TestGetRepositoryAnnotations_WithData(t *testing.T) { db := setupAnnotationsTestDB(t) createAnnotationTestUser(t, db, "did:plc:alice123", "alice.bsky.social") // Insert test annotations testAnnotations := map[string]string{ "org.opencontainers.image.title": "My App", "org.opencontainers.image.description": "A test application", "org.opencontainers.image.version": "1.0.0", } err := UpsertRepositoryAnnotations(db, "did:plc:alice123", "myapp", testAnnotations) if err != nil { t.Fatalf("UpsertRepositoryAnnotations() error = %v", err) } // Retrieve annotations annotations, err := GetRepositoryAnnotations(db, "did:plc:alice123", "myapp") if err != nil { t.Fatalf("GetRepositoryAnnotations() error = %v", err) } if len(annotations) != len(testAnnotations) { t.Errorf("Expected %d annotations, got %d", len(testAnnotations), len(annotations)) } for key, expectedValue := range testAnnotations { if actualValue, ok := annotations[key]; !ok { t.Errorf("Missing annotation key: %s", key) } else if actualValue != expectedValue { t.Errorf("Annotation[%s] = %v, want %v", key, actualValue, expectedValue) } } } // TestUpsertRepositoryAnnotations_Insert tests inserting new annotations func TestUpsertRepositoryAnnotations_Insert(t *testing.T) { db := setupAnnotationsTestDB(t) createAnnotationTestUser(t, db, "did:plc:bob456", "bob.bsky.social") annotations := map[string]string{ "key1": "value1", "key2": "value2", } err := UpsertRepositoryAnnotations(db, "did:plc:bob456", "testapp", annotations) if err != nil { t.Fatalf("UpsertRepositoryAnnotations() error = %v", err) } // Verify annotations were inserted retrieved, err := GetRepositoryAnnotations(db, "did:plc:bob456", "testapp") if err != nil { t.Fatalf("GetRepositoryAnnotations() error = %v", err) } if len(retrieved) != len(annotations) { t.Errorf("Expected %d annotations, got %d", len(annotations), len(retrieved)) } for key, expectedValue := range annotations { if actualValue := retrieved[key]; actualValue != expectedValue { t.Errorf("Annotation[%s] = %v, want %v", key, actualValue, expectedValue) } } } // TestUpsertRepositoryAnnotations_Update tests updating existing annotations func TestUpsertRepositoryAnnotations_Update(t *testing.T) { db := setupAnnotationsTestDB(t) createAnnotationTestUser(t, db, "did:plc:charlie789", "charlie.bsky.social") // Insert initial annotations initial := map[string]string{ "key1": "oldvalue1", "key2": "oldvalue2", "key3": "oldvalue3", } err := UpsertRepositoryAnnotations(db, "did:plc:charlie789", "updateapp", initial) if err != nil { t.Fatalf("Initial UpsertRepositoryAnnotations() error = %v", err) } // Update with new annotations (completely replaces old ones) updated := map[string]string{ "key1": "newvalue1", // Updated "key4": "newvalue4", // New key (key2 and key3 removed) } err = UpsertRepositoryAnnotations(db, "did:plc:charlie789", "updateapp", updated) if err != nil { t.Fatalf("Update UpsertRepositoryAnnotations() error = %v", err) } // Verify annotations were replaced retrieved, err := GetRepositoryAnnotations(db, "did:plc:charlie789", "updateapp") if err != nil { t.Fatalf("GetRepositoryAnnotations() error = %v", err) } if len(retrieved) != len(updated) { t.Errorf("Expected %d annotations, got %d", len(updated), len(retrieved)) } // Verify new values if retrieved["key1"] != "newvalue1" { t.Errorf("key1 = %v, want newvalue1", retrieved["key1"]) } if retrieved["key4"] != "newvalue4" { t.Errorf("key4 = %v, want newvalue4", retrieved["key4"]) } // Verify old keys were removed if _, exists := retrieved["key2"]; exists { t.Error("key2 should have been removed") } if _, exists := retrieved["key3"]; exists { t.Error("key3 should have been removed") } } // TestUpsertRepositoryAnnotations_EmptyMap tests upserting with empty map func TestUpsertRepositoryAnnotations_EmptyMap(t *testing.T) { db := setupAnnotationsTestDB(t) createAnnotationTestUser(t, db, "did:plc:dave111", "dave.bsky.social") // Insert initial annotations initial := map[string]string{ "key1": "value1", "key2": "value2", } err := UpsertRepositoryAnnotations(db, "did:plc:dave111", "emptyapp", initial) if err != nil { t.Fatalf("Initial UpsertRepositoryAnnotations() error = %v", err) } // Upsert with empty map (should delete all) empty := make(map[string]string) err = UpsertRepositoryAnnotations(db, "did:plc:dave111", "emptyapp", empty) if err != nil { t.Fatalf("Empty UpsertRepositoryAnnotations() error = %v", err) } // Verify all annotations were deleted retrieved, err := GetRepositoryAnnotations(db, "did:plc:dave111", "emptyapp") if err != nil { t.Fatalf("GetRepositoryAnnotations() error = %v", err) } if len(retrieved) != 0 { t.Errorf("Expected 0 annotations after empty upsert, got %d", len(retrieved)) } } // TestUpsertRepositoryAnnotations_MultipleRepos tests isolation between repositories func TestUpsertRepositoryAnnotations_MultipleRepos(t *testing.T) { db := setupAnnotationsTestDB(t) createAnnotationTestUser(t, db, "did:plc:eve222", "eve.bsky.social") // Insert annotations for repo1 repo1Annotations := map[string]string{ "repo": "repo1", "key1": "value1", } err := UpsertRepositoryAnnotations(db, "did:plc:eve222", "repo1", repo1Annotations) if err != nil { t.Fatalf("UpsertRepositoryAnnotations(repo1) error = %v", err) } // Insert annotations for repo2 (same DID, different repo) repo2Annotations := map[string]string{ "repo": "repo2", "key2": "value2", } err = UpsertRepositoryAnnotations(db, "did:plc:eve222", "repo2", repo2Annotations) if err != nil { t.Fatalf("UpsertRepositoryAnnotations(repo2) error = %v", err) } // Verify repo1 annotations unchanged retrieved1, err := GetRepositoryAnnotations(db, "did:plc:eve222", "repo1") if err != nil { t.Fatalf("GetRepositoryAnnotations(repo1) error = %v", err) } if len(retrieved1) != len(repo1Annotations) { t.Errorf("repo1: Expected %d annotations, got %d", len(repo1Annotations), len(retrieved1)) } if retrieved1["repo"] != "repo1" { t.Errorf("repo1: Expected repo=repo1, got %v", retrieved1["repo"]) } // Verify repo2 annotations retrieved2, err := GetRepositoryAnnotations(db, "did:plc:eve222", "repo2") if err != nil { t.Fatalf("GetRepositoryAnnotations(repo2) error = %v", err) } if len(retrieved2) != len(repo2Annotations) { t.Errorf("repo2: Expected %d annotations, got %d", len(repo2Annotations), len(retrieved2)) } if retrieved2["repo"] != "repo2" { t.Errorf("repo2: Expected repo=repo2, got %v", retrieved2["repo"]) } } // TestDeleteRepositoryAnnotations tests deleting annotations func TestDeleteRepositoryAnnotations(t *testing.T) { db := setupAnnotationsTestDB(t) createAnnotationTestUser(t, db, "did:plc:frank333", "frank.bsky.social") // Insert annotations annotations := map[string]string{ "key1": "value1", "key2": "value2", } err := UpsertRepositoryAnnotations(db, "did:plc:frank333", "deleteapp", annotations) if err != nil { t.Fatalf("UpsertRepositoryAnnotations() error = %v", err) } // Verify annotations exist retrieved, err := GetRepositoryAnnotations(db, "did:plc:frank333", "deleteapp") if err != nil { t.Fatalf("GetRepositoryAnnotations() error = %v", err) } if len(retrieved) != 2 { t.Fatalf("Expected 2 annotations before delete, got %d", len(retrieved)) } // Delete annotations err = DeleteRepositoryAnnotations(db, "did:plc:frank333", "deleteapp") if err != nil { t.Fatalf("DeleteRepositoryAnnotations() error = %v", err) } // Verify annotations were deleted retrieved, err = GetRepositoryAnnotations(db, "did:plc:frank333", "deleteapp") if err != nil { t.Fatalf("GetRepositoryAnnotations() after delete error = %v", err) } if len(retrieved) != 0 { t.Errorf("Expected 0 annotations after delete, got %d", len(retrieved)) } } // TestDeleteRepositoryAnnotations_NonExistent tests deleting non-existent annotations func TestDeleteRepositoryAnnotations_NonExistent(t *testing.T) { db := setupAnnotationsTestDB(t) // Delete from non-existent repository (should not error) err := DeleteRepositoryAnnotations(db, "did:plc:ghost999", "nonexistent") if err != nil { t.Errorf("DeleteRepositoryAnnotations() for non-existent repo should not error, got: %v", err) } } // TestAnnotations_DifferentDIDs tests isolation between different DIDs func TestAnnotations_DifferentDIDs(t *testing.T) { db := setupAnnotationsTestDB(t) createAnnotationTestUser(t, db, "did:plc:alice123", "alice.bsky.social") createAnnotationTestUser(t, db, "did:plc:bob456", "bob.bsky.social") // Insert annotations for alice aliceAnnotations := map[string]string{ "owner": "alice", "key1": "alice-value1", } err := UpsertRepositoryAnnotations(db, "did:plc:alice123", "sharedname", aliceAnnotations) if err != nil { t.Fatalf("UpsertRepositoryAnnotations(alice) error = %v", err) } // Insert annotations for bob (same repo name, different DID) bobAnnotations := map[string]string{ "owner": "bob", "key1": "bob-value1", } err = UpsertRepositoryAnnotations(db, "did:plc:bob456", "sharedname", bobAnnotations) if err != nil { t.Fatalf("UpsertRepositoryAnnotations(bob) error = %v", err) } // Verify alice's annotations unchanged aliceRetrieved, err := GetRepositoryAnnotations(db, "did:plc:alice123", "sharedname") if err != nil { t.Fatalf("GetRepositoryAnnotations(alice) error = %v", err) } if aliceRetrieved["owner"] != "alice" { t.Errorf("alice: Expected owner=alice, got %v", aliceRetrieved["owner"]) } // Verify bob's annotations bobRetrieved, err := GetRepositoryAnnotations(db, "did:plc:bob456", "sharedname") if err != nil { t.Fatalf("GetRepositoryAnnotations(bob) error = %v", err) } if bobRetrieved["owner"] != "bob" { t.Errorf("bob: Expected owner=bob, got %v", bobRetrieved["owner"]) } }