A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at codeberg-source 361 lines 12 kB view raw
1package db 2 3import ( 4 "database/sql" 5 "testing" 6) 7 8func TestAnnotations_Placeholder(t *testing.T) { 9 // Placeholder test for annotations package 10 // GetRepositoryAnnotations returns map[string]string 11 annotations := make(map[string]string) 12 annotations["test"] = "value" 13 14 if annotations["test"] != "value" { 15 t.Error("Expected annotation value to be stored") 16 } 17} 18 19// Integration tests 20 21func setupAnnotationsTestDB(t *testing.T) *sql.DB { 22 t.Helper() 23 // Use file::memory: with cache=shared to ensure all connections share the same in-memory DB 24 db, err := InitDB("file::memory:?cache=shared", true) 25 if err != nil { 26 t.Fatalf("Failed to initialize test database: %v", err) 27 } 28 // Limit to single connection to avoid race conditions in tests 29 db.SetMaxOpenConns(1) 30 t.Cleanup(func() { db.Close() }) 31 return db 32} 33 34func createAnnotationTestUser(t *testing.T, db *sql.DB, did, handle string) { 35 t.Helper() 36 _, err := db.Exec(` 37 INSERT OR IGNORE INTO users (did, handle, pds_endpoint, last_seen) 38 VALUES (?, ?, ?, datetime('now')) 39 `, did, handle, "https://pds.example.com") 40 if err != nil { 41 t.Fatalf("Failed to create test user: %v", err) 42 } 43} 44 45// TestGetRepositoryAnnotations_Empty tests retrieving from empty repository 46func TestGetRepositoryAnnotations_Empty(t *testing.T) { 47 db := setupAnnotationsTestDB(t) 48 49 annotations, err := GetRepositoryAnnotations(db, "did:plc:alice123", "myapp") 50 if err != nil { 51 t.Fatalf("GetRepositoryAnnotations() error = %v", err) 52 } 53 54 if len(annotations) != 0 { 55 t.Errorf("Expected empty annotations, got %d entries", len(annotations)) 56 } 57} 58 59// TestGetRepositoryAnnotations_WithData tests retrieving existing annotations 60func TestGetRepositoryAnnotations_WithData(t *testing.T) { 61 db := setupAnnotationsTestDB(t) 62 createAnnotationTestUser(t, db, "did:plc:alice123", "alice.bsky.social") 63 64 // Insert test annotations 65 testAnnotations := map[string]string{ 66 "org.opencontainers.image.title": "My App", 67 "org.opencontainers.image.description": "A test application", 68 "org.opencontainers.image.version": "1.0.0", 69 } 70 71 err := UpsertRepositoryAnnotations(db, "did:plc:alice123", "myapp", testAnnotations) 72 if err != nil { 73 t.Fatalf("UpsertRepositoryAnnotations() error = %v", err) 74 } 75 76 // Retrieve annotations 77 annotations, err := GetRepositoryAnnotations(db, "did:plc:alice123", "myapp") 78 if err != nil { 79 t.Fatalf("GetRepositoryAnnotations() error = %v", err) 80 } 81 82 if len(annotations) != len(testAnnotations) { 83 t.Errorf("Expected %d annotations, got %d", len(testAnnotations), len(annotations)) 84 } 85 86 for key, expectedValue := range testAnnotations { 87 if actualValue, ok := annotations[key]; !ok { 88 t.Errorf("Missing annotation key: %s", key) 89 } else if actualValue != expectedValue { 90 t.Errorf("Annotation[%s] = %v, want %v", key, actualValue, expectedValue) 91 } 92 } 93} 94 95// TestUpsertRepositoryAnnotations_Insert tests inserting new annotations 96func TestUpsertRepositoryAnnotations_Insert(t *testing.T) { 97 db := setupAnnotationsTestDB(t) 98 createAnnotationTestUser(t, db, "did:plc:bob456", "bob.bsky.social") 99 100 annotations := map[string]string{ 101 "key1": "value1", 102 "key2": "value2", 103 } 104 105 err := UpsertRepositoryAnnotations(db, "did:plc:bob456", "testapp", annotations) 106 if err != nil { 107 t.Fatalf("UpsertRepositoryAnnotations() error = %v", err) 108 } 109 110 // Verify annotations were inserted 111 retrieved, err := GetRepositoryAnnotations(db, "did:plc:bob456", "testapp") 112 if err != nil { 113 t.Fatalf("GetRepositoryAnnotations() error = %v", err) 114 } 115 116 if len(retrieved) != len(annotations) { 117 t.Errorf("Expected %d annotations, got %d", len(annotations), len(retrieved)) 118 } 119 120 for key, expectedValue := range annotations { 121 if actualValue := retrieved[key]; actualValue != expectedValue { 122 t.Errorf("Annotation[%s] = %v, want %v", key, actualValue, expectedValue) 123 } 124 } 125} 126 127// TestUpsertRepositoryAnnotations_Update tests updating existing annotations 128func TestUpsertRepositoryAnnotations_Update(t *testing.T) { 129 db := setupAnnotationsTestDB(t) 130 createAnnotationTestUser(t, db, "did:plc:charlie789", "charlie.bsky.social") 131 132 // Insert initial annotations 133 initial := map[string]string{ 134 "key1": "oldvalue1", 135 "key2": "oldvalue2", 136 "key3": "oldvalue3", 137 } 138 139 err := UpsertRepositoryAnnotations(db, "did:plc:charlie789", "updateapp", initial) 140 if err != nil { 141 t.Fatalf("Initial UpsertRepositoryAnnotations() error = %v", err) 142 } 143 144 // Update with new annotations (completely replaces old ones) 145 updated := map[string]string{ 146 "key1": "newvalue1", // Updated 147 "key4": "newvalue4", // New key (key2 and key3 removed) 148 } 149 150 err = UpsertRepositoryAnnotations(db, "did:plc:charlie789", "updateapp", updated) 151 if err != nil { 152 t.Fatalf("Update UpsertRepositoryAnnotations() error = %v", err) 153 } 154 155 // Verify annotations were replaced 156 retrieved, err := GetRepositoryAnnotations(db, "did:plc:charlie789", "updateapp") 157 if err != nil { 158 t.Fatalf("GetRepositoryAnnotations() error = %v", err) 159 } 160 161 if len(retrieved) != len(updated) { 162 t.Errorf("Expected %d annotations, got %d", len(updated), len(retrieved)) 163 } 164 165 // Verify new values 166 if retrieved["key1"] != "newvalue1" { 167 t.Errorf("key1 = %v, want newvalue1", retrieved["key1"]) 168 } 169 if retrieved["key4"] != "newvalue4" { 170 t.Errorf("key4 = %v, want newvalue4", retrieved["key4"]) 171 } 172 173 // Verify old keys were removed 174 if _, exists := retrieved["key2"]; exists { 175 t.Error("key2 should have been removed") 176 } 177 if _, exists := retrieved["key3"]; exists { 178 t.Error("key3 should have been removed") 179 } 180} 181 182// TestUpsertRepositoryAnnotations_EmptyMap tests upserting with empty map 183func TestUpsertRepositoryAnnotations_EmptyMap(t *testing.T) { 184 db := setupAnnotationsTestDB(t) 185 createAnnotationTestUser(t, db, "did:plc:dave111", "dave.bsky.social") 186 187 // Insert initial annotations 188 initial := map[string]string{ 189 "key1": "value1", 190 "key2": "value2", 191 } 192 193 err := UpsertRepositoryAnnotations(db, "did:plc:dave111", "emptyapp", initial) 194 if err != nil { 195 t.Fatalf("Initial UpsertRepositoryAnnotations() error = %v", err) 196 } 197 198 // Upsert with empty map (should delete all) 199 empty := make(map[string]string) 200 201 err = UpsertRepositoryAnnotations(db, "did:plc:dave111", "emptyapp", empty) 202 if err != nil { 203 t.Fatalf("Empty UpsertRepositoryAnnotations() error = %v", err) 204 } 205 206 // Verify all annotations were deleted 207 retrieved, err := GetRepositoryAnnotations(db, "did:plc:dave111", "emptyapp") 208 if err != nil { 209 t.Fatalf("GetRepositoryAnnotations() error = %v", err) 210 } 211 212 if len(retrieved) != 0 { 213 t.Errorf("Expected 0 annotations after empty upsert, got %d", len(retrieved)) 214 } 215} 216 217// TestUpsertRepositoryAnnotations_MultipleRepos tests isolation between repositories 218func TestUpsertRepositoryAnnotations_MultipleRepos(t *testing.T) { 219 db := setupAnnotationsTestDB(t) 220 createAnnotationTestUser(t, db, "did:plc:eve222", "eve.bsky.social") 221 222 // Insert annotations for repo1 223 repo1Annotations := map[string]string{ 224 "repo": "repo1", 225 "key1": "value1", 226 } 227 err := UpsertRepositoryAnnotations(db, "did:plc:eve222", "repo1", repo1Annotations) 228 if err != nil { 229 t.Fatalf("UpsertRepositoryAnnotations(repo1) error = %v", err) 230 } 231 232 // Insert annotations for repo2 (same DID, different repo) 233 repo2Annotations := map[string]string{ 234 "repo": "repo2", 235 "key2": "value2", 236 } 237 err = UpsertRepositoryAnnotations(db, "did:plc:eve222", "repo2", repo2Annotations) 238 if err != nil { 239 t.Fatalf("UpsertRepositoryAnnotations(repo2) error = %v", err) 240 } 241 242 // Verify repo1 annotations unchanged 243 retrieved1, err := GetRepositoryAnnotations(db, "did:plc:eve222", "repo1") 244 if err != nil { 245 t.Fatalf("GetRepositoryAnnotations(repo1) error = %v", err) 246 } 247 if len(retrieved1) != len(repo1Annotations) { 248 t.Errorf("repo1: Expected %d annotations, got %d", len(repo1Annotations), len(retrieved1)) 249 } 250 if retrieved1["repo"] != "repo1" { 251 t.Errorf("repo1: Expected repo=repo1, got %v", retrieved1["repo"]) 252 } 253 254 // Verify repo2 annotations 255 retrieved2, err := GetRepositoryAnnotations(db, "did:plc:eve222", "repo2") 256 if err != nil { 257 t.Fatalf("GetRepositoryAnnotations(repo2) error = %v", err) 258 } 259 if len(retrieved2) != len(repo2Annotations) { 260 t.Errorf("repo2: Expected %d annotations, got %d", len(repo2Annotations), len(retrieved2)) 261 } 262 if retrieved2["repo"] != "repo2" { 263 t.Errorf("repo2: Expected repo=repo2, got %v", retrieved2["repo"]) 264 } 265} 266 267// TestDeleteRepositoryAnnotations tests deleting annotations 268func TestDeleteRepositoryAnnotations(t *testing.T) { 269 db := setupAnnotationsTestDB(t) 270 createAnnotationTestUser(t, db, "did:plc:frank333", "frank.bsky.social") 271 272 // Insert annotations 273 annotations := map[string]string{ 274 "key1": "value1", 275 "key2": "value2", 276 } 277 err := UpsertRepositoryAnnotations(db, "did:plc:frank333", "deleteapp", annotations) 278 if err != nil { 279 t.Fatalf("UpsertRepositoryAnnotations() error = %v", err) 280 } 281 282 // Verify annotations exist 283 retrieved, err := GetRepositoryAnnotations(db, "did:plc:frank333", "deleteapp") 284 if err != nil { 285 t.Fatalf("GetRepositoryAnnotations() error = %v", err) 286 } 287 if len(retrieved) != 2 { 288 t.Fatalf("Expected 2 annotations before delete, got %d", len(retrieved)) 289 } 290 291 // Delete annotations 292 err = DeleteRepositoryAnnotations(db, "did:plc:frank333", "deleteapp") 293 if err != nil { 294 t.Fatalf("DeleteRepositoryAnnotations() error = %v", err) 295 } 296 297 // Verify annotations were deleted 298 retrieved, err = GetRepositoryAnnotations(db, "did:plc:frank333", "deleteapp") 299 if err != nil { 300 t.Fatalf("GetRepositoryAnnotations() after delete error = %v", err) 301 } 302 if len(retrieved) != 0 { 303 t.Errorf("Expected 0 annotations after delete, got %d", len(retrieved)) 304 } 305} 306 307// TestDeleteRepositoryAnnotations_NonExistent tests deleting non-existent annotations 308func TestDeleteRepositoryAnnotations_NonExistent(t *testing.T) { 309 db := setupAnnotationsTestDB(t) 310 311 // Delete from non-existent repository (should not error) 312 err := DeleteRepositoryAnnotations(db, "did:plc:ghost999", "nonexistent") 313 if err != nil { 314 t.Errorf("DeleteRepositoryAnnotations() for non-existent repo should not error, got: %v", err) 315 } 316} 317 318// TestAnnotations_DifferentDIDs tests isolation between different DIDs 319func TestAnnotations_DifferentDIDs(t *testing.T) { 320 db := setupAnnotationsTestDB(t) 321 createAnnotationTestUser(t, db, "did:plc:alice123", "alice.bsky.social") 322 createAnnotationTestUser(t, db, "did:plc:bob456", "bob.bsky.social") 323 324 // Insert annotations for alice 325 aliceAnnotations := map[string]string{ 326 "owner": "alice", 327 "key1": "alice-value1", 328 } 329 err := UpsertRepositoryAnnotations(db, "did:plc:alice123", "sharedname", aliceAnnotations) 330 if err != nil { 331 t.Fatalf("UpsertRepositoryAnnotations(alice) error = %v", err) 332 } 333 334 // Insert annotations for bob (same repo name, different DID) 335 bobAnnotations := map[string]string{ 336 "owner": "bob", 337 "key1": "bob-value1", 338 } 339 err = UpsertRepositoryAnnotations(db, "did:plc:bob456", "sharedname", bobAnnotations) 340 if err != nil { 341 t.Fatalf("UpsertRepositoryAnnotations(bob) error = %v", err) 342 } 343 344 // Verify alice's annotations unchanged 345 aliceRetrieved, err := GetRepositoryAnnotations(db, "did:plc:alice123", "sharedname") 346 if err != nil { 347 t.Fatalf("GetRepositoryAnnotations(alice) error = %v", err) 348 } 349 if aliceRetrieved["owner"] != "alice" { 350 t.Errorf("alice: Expected owner=alice, got %v", aliceRetrieved["owner"]) 351 } 352 353 // Verify bob's annotations 354 bobRetrieved, err := GetRepositoryAnnotations(db, "did:plc:bob456", "sharedname") 355 if err != nil { 356 t.Fatalf("GetRepositoryAnnotations(bob) error = %v", err) 357 } 358 if bobRetrieved["owner"] != "bob" { 359 t.Errorf("bob: Expected owner=bob, got %v", bobRetrieved["owner"]) 360 } 361}