···
1
-
package repository_test
10
-
"Coves/internal/atproto/carstore"
11
-
"Coves/internal/core/repository"
12
-
"Coves/internal/db/postgres"
14
-
"github.com/ipfs/go-cid"
15
-
_ "github.com/lib/pq"
16
-
"github.com/pressly/goose/v3"
17
-
postgresDriver "gorm.io/driver/postgres"
21
-
// Mock signing key for testing
22
-
type mockSigningKey struct{}
24
-
// Test database connection
25
-
func setupTestDB(t *testing.T) (*sql.DB, *gorm.DB, func()) {
26
-
// Use test database URL from environment or default
27
-
dbURL := os.Getenv("TEST_DATABASE_URL")
29
-
// Skip test if no database configured
30
-
t.Skip("TEST_DATABASE_URL not set, skipping database tests")
33
-
// Connect with sql.DB for migrations
34
-
sqlDB, err := sql.Open("postgres", dbURL)
36
-
t.Fatalf("Failed to connect to test database: %v", err)
40
-
if err := goose.Up(sqlDB, "../../db/migrations"); err != nil {
41
-
t.Fatalf("Failed to run migrations: %v", err)
44
-
// Connect with GORM using a fresh connection
45
-
gormDB, err := gorm.Open(postgresDriver.Open(dbURL), &gorm.Config{
46
-
DisableForeignKeyConstraintWhenMigrating: true,
50
-
t.Fatalf("Failed to create GORM connection: %v", err)
55
-
// Clean up test data
56
-
gormDB.Exec("DELETE FROM repositories")
57
-
gormDB.Exec("DELETE FROM commits")
58
-
gormDB.Exec("DELETE FROM records")
59
-
gormDB.Exec("DELETE FROM user_maps")
60
-
gormDB.Exec("DELETE FROM car_shards")
61
-
gormDB.Exec("DELETE FROM block_refs")
63
-
// Close GORM connection
64
-
if sqlGormDB, err := gormDB.DB(); err == nil {
68
-
// Close original SQL connection
72
-
return sqlDB, gormDB, cleanup
75
-
func TestRepositoryService_CreateRepository(t *testing.T) {
76
-
sqlDB, gormDB, cleanup := setupTestDB(t)
79
-
// Create temporary directory for carstore
80
-
tempDir, err := os.MkdirTemp("", "carstore_test")
82
-
t.Fatalf("Failed to create temp dir: %v", err)
84
-
defer os.RemoveAll(tempDir)
86
-
// Initialize carstore
87
-
carDirs := []string{tempDir}
88
-
repoStore, err := carstore.NewRepoStore(gormDB, carDirs)
90
-
t.Fatalf("Failed to create repo store: %v", err)
93
-
// Initialize repository service
94
-
repoRepo := postgres.NewRepositoryRepo(sqlDB)
95
-
service := repository.NewService(repoRepo, repoStore)
98
-
testDID := "did:plc:testuser123"
101
-
service.SetSigningKey(testDID, &mockSigningKey{})
103
-
// Create repository
104
-
repo, err := service.CreateRepository(testDID)
106
-
t.Fatalf("Failed to create repository: %v", err)
109
-
// Verify repository was created
110
-
if repo.DID != testDID {
111
-
t.Errorf("Expected DID %s, got %s", testDID, repo.DID)
113
-
if !repo.HeadCID.Defined() {
114
-
t.Error("Expected HeadCID to be defined")
116
-
if repo.RecordCount != 0 {
117
-
t.Errorf("Expected RecordCount 0, got %d", repo.RecordCount)
120
-
// Verify repository exists in database
121
-
fetchedRepo, err := service.GetRepository(testDID)
123
-
t.Fatalf("Failed to get repository: %v", err)
125
-
if fetchedRepo.DID != testDID {
126
-
t.Errorf("Expected fetched DID %s, got %s", testDID, fetchedRepo.DID)
129
-
// Test duplicate creation should fail
130
-
_, err = service.CreateRepository(testDID)
132
-
t.Error("Expected error creating duplicate repository")
136
-
func TestRepositoryService_ImportExport(t *testing.T) {
137
-
sqlDB, gormDB, cleanup := setupTestDB(t)
140
-
// Create temporary directory for carstore
141
-
tempDir, err := os.MkdirTemp("", "carstore_test")
143
-
t.Fatalf("Failed to create temp dir: %v", err)
145
-
defer os.RemoveAll(tempDir)
147
-
// Log the temp directory for debugging
148
-
t.Logf("Using carstore directory: %s", tempDir)
150
-
// Initialize carstore
151
-
carDirs := []string{tempDir}
152
-
repoStore, err := carstore.NewRepoStore(gormDB, carDirs)
154
-
t.Fatalf("Failed to create repo store: %v", err)
157
-
// Initialize repository service
158
-
repoRepo := postgres.NewRepositoryRepo(sqlDB)
159
-
service := repository.NewService(repoRepo, repoStore)
161
-
// Create first repository
162
-
did1 := "did:plc:user1"
163
-
service.SetSigningKey(did1, &mockSigningKey{})
164
-
repo1, err := service.CreateRepository(did1)
166
-
t.Fatalf("Failed to create repository 1: %v", err)
168
-
t.Logf("Created repository with HeadCID: %s", repo1.HeadCID)
170
-
// Check what's in the database
171
-
var userMapCount int
172
-
gormDB.Raw("SELECT COUNT(*) FROM user_maps").Scan(&userMapCount)
173
-
t.Logf("User maps count: %d", userMapCount)
175
-
var carShardCount int
176
-
gormDB.Raw("SELECT COUNT(*) FROM car_shards").Scan(&carShardCount)
177
-
t.Logf("Car shards count: %d", carShardCount)
179
-
// Check block_refs too
180
-
var blockRefCount int
181
-
gormDB.Raw("SELECT COUNT(*) FROM block_refs").Scan(&blockRefCount)
182
-
t.Logf("Block refs count: %d", blockRefCount)
184
-
// Export repository
185
-
carData, err := service.ExportRepository(did1)
187
-
t.Fatalf("Failed to export repository: %v", err)
189
-
// For now, empty repositories return empty CAR data
190
-
t.Logf("Exported CAR data size: %d bytes", len(carData))
192
-
// Import to new DID
193
-
did2 := "did:plc:user2"
194
-
err = service.ImportRepository(did2, carData)
196
-
t.Fatalf("Failed to import repository: %v", err)
199
-
// Verify imported repository
200
-
repo2, err := service.GetRepository(did2)
202
-
t.Fatalf("Failed to get imported repository: %v", err)
204
-
if repo2.DID != did2 {
205
-
t.Errorf("Expected DID %s, got %s", did2, repo2.DID)
207
-
// Note: HeadCID might differ due to new import
210
-
func TestRepositoryService_DeleteRepository(t *testing.T) {
211
-
sqlDB, gormDB, cleanup := setupTestDB(t)
214
-
// Create temporary directory for carstore
215
-
tempDir, err := os.MkdirTemp("", "carstore_test")
217
-
t.Fatalf("Failed to create temp dir: %v", err)
219
-
defer os.RemoveAll(tempDir)
221
-
// Initialize carstore
222
-
carDirs := []string{tempDir}
223
-
repoStore, err := carstore.NewRepoStore(gormDB, carDirs)
225
-
t.Fatalf("Failed to create repo store: %v", err)
228
-
// Initialize repository service
229
-
repoRepo := postgres.NewRepositoryRepo(sqlDB)
230
-
service := repository.NewService(repoRepo, repoStore)
232
-
// Create repository
233
-
testDID := "did:plc:deletetest"
234
-
service.SetSigningKey(testDID, &mockSigningKey{})
235
-
_, err = service.CreateRepository(testDID)
237
-
t.Fatalf("Failed to create repository: %v", err)
240
-
// Delete repository
241
-
err = service.DeleteRepository(testDID)
243
-
t.Fatalf("Failed to delete repository: %v", err)
246
-
// Verify repository is deleted
247
-
_, err = service.GetRepository(testDID)
249
-
t.Error("Expected error getting deleted repository")
253
-
func TestRepositoryService_CompactRepository(t *testing.T) {
254
-
sqlDB, gormDB, cleanup := setupTestDB(t)
257
-
// Create temporary directory for carstore
258
-
tempDir, err := os.MkdirTemp("", "carstore_test")
260
-
t.Fatalf("Failed to create temp dir: %v", err)
262
-
defer os.RemoveAll(tempDir)
264
-
// Initialize carstore
265
-
carDirs := []string{tempDir}
266
-
repoStore, err := carstore.NewRepoStore(gormDB, carDirs)
268
-
t.Fatalf("Failed to create repo store: %v", err)
271
-
// Initialize repository service
272
-
repoRepo := postgres.NewRepositoryRepo(sqlDB)
273
-
service := repository.NewService(repoRepo, repoStore)
275
-
// Create repository
276
-
testDID := "did:plc:compacttest"
277
-
service.SetSigningKey(testDID, &mockSigningKey{})
278
-
_, err = service.CreateRepository(testDID)
280
-
t.Fatalf("Failed to create repository: %v", err)
283
-
// Run compaction (should not error even with minimal data)
284
-
err = service.CompactRepository(testDID)
286
-
t.Errorf("Failed to compact repository: %v", err)
290
-
// Test UserMapping functionality
291
-
func TestUserMapping(t *testing.T) {
292
-
_, gormDB, cleanup := setupTestDB(t)
295
-
// Create user mapping
296
-
mapping, err := carstore.NewUserMapping(gormDB)
298
-
t.Fatalf("Failed to create user mapping: %v", err)
301
-
// Test creating new mapping
302
-
did1 := "did:plc:mapping1"
303
-
uid1, err := mapping.GetOrCreateUID(context.Background(), did1)
305
-
t.Fatalf("Failed to create UID for %s: %v", did1, err)
308
-
t.Error("Expected non-zero UID")
311
-
// Test getting existing mapping
312
-
uid1Again, err := mapping.GetOrCreateUID(context.Background(), did1)
314
-
t.Fatalf("Failed to get UID for %s: %v", did1, err)
316
-
if uid1 != uid1Again {
317
-
t.Errorf("Expected same UID, got %d and %d", uid1, uid1Again)
320
-
// Test reverse lookup
321
-
didLookup, err := mapping.GetDID(uid1)
323
-
t.Fatalf("Failed to get DID for UID %d: %v", uid1, err)
325
-
if didLookup != did1 {
326
-
t.Errorf("Expected DID %s, got %s", did1, didLookup)
329
-
// Test second user gets different UID
330
-
did2 := "did:plc:mapping2"
331
-
uid2, err := mapping.GetOrCreateUID(context.Background(), did2)
333
-
t.Fatalf("Failed to create UID for %s: %v", did2, err)
336
-
t.Error("Expected different UIDs for different DIDs")
340
-
// Test with mock repository and carstore
341
-
func TestRepositoryService_MockedComponents(t *testing.T) {
342
-
// Use the existing mock repository from the old test file
343
-
_ = NewMockRepositoryRepository()
345
-
// For unit testing without real carstore, we would need to mock RepoStore
346
-
// For now, this demonstrates the structure
347
-
t.Skip("Mocked carstore tests would require creating mock RepoStore interface")
350
-
// Benchmark repository creation
351
-
func BenchmarkRepositoryCreation(b *testing.B) {
352
-
sqlDB, gormDB, cleanup := setupTestDB(&testing.T{})
355
-
tempDir, _ := os.MkdirTemp("", "carstore_bench")
356
-
defer os.RemoveAll(tempDir)
358
-
carDirs := []string{tempDir}
359
-
repoStore, _ := carstore.NewRepoStore(gormDB, carDirs)
360
-
repoRepo := postgres.NewRepositoryRepo(sqlDB)
361
-
service := repository.NewService(repoRepo, repoStore)
364
-
for i := 0; i < b.N; i++ {
365
-
did := fmt.Sprintf("did:plc:bench%d", i)
366
-
service.SetSigningKey(did, &mockSigningKey{})
367
-
_, _ = service.CreateRepository(did)
371
-
// MockRepositoryRepository is a mock implementation of repository.RepositoryRepository
372
-
type MockRepositoryRepository struct {
373
-
repositories map[string]*repository.Repository
374
-
commits map[string][]*repository.Commit
375
-
records map[string]*repository.Record
378
-
func NewMockRepositoryRepository() *MockRepositoryRepository {
379
-
return &MockRepositoryRepository{
380
-
repositories: make(map[string]*repository.Repository),
381
-
commits: make(map[string][]*repository.Commit),
382
-
records: make(map[string]*repository.Record),
386
-
// Repository operations
387
-
func (m *MockRepositoryRepository) Create(repo *repository.Repository) error {
388
-
m.repositories[repo.DID] = repo
392
-
func (m *MockRepositoryRepository) GetByDID(did string) (*repository.Repository, error) {
393
-
repo, exists := m.repositories[did]
400
-
func (m *MockRepositoryRepository) Update(repo *repository.Repository) error {
401
-
if _, exists := m.repositories[repo.DID]; !exists {
404
-
m.repositories[repo.DID] = repo
408
-
func (m *MockRepositoryRepository) Delete(did string) error {
409
-
delete(m.repositories, did)
413
-
// Commit operations
414
-
func (m *MockRepositoryRepository) CreateCommit(commit *repository.Commit) error {
415
-
m.commits[commit.DID] = append(m.commits[commit.DID], commit)
419
-
func (m *MockRepositoryRepository) GetCommit(did string, commitCID cid.Cid) (*repository.Commit, error) {
420
-
commits, exists := m.commits[did]
425
-
for _, c := range commits {
426
-
if c.CID.Equals(commitCID) {
433
-
func (m *MockRepositoryRepository) GetLatestCommit(did string) (*repository.Commit, error) {
434
-
commits, exists := m.commits[did]
435
-
if !exists || len(commits) == 0 {
438
-
return commits[len(commits)-1], nil
441
-
func (m *MockRepositoryRepository) ListCommits(did string, limit int, offset int) ([]*repository.Commit, error) {
442
-
commits, exists := m.commits[did]
444
-
return []*repository.Commit{}, nil
448
-
if start >= len(commits) {
449
-
return []*repository.Commit{}, nil
452
-
end := start + limit
453
-
if end > len(commits) {
457
-
return commits[start:end], nil
460
-
// Record operations
461
-
func (m *MockRepositoryRepository) CreateRecord(record *repository.Record) error {
463
-
m.records[key] = record
467
-
func (m *MockRepositoryRepository) GetRecord(did string, collection string, recordKey string) (*repository.Record, error) {
468
-
uri := "at://" + did + "/" + collection + "/" + recordKey
469
-
record, exists := m.records[uri]
476
-
func (m *MockRepositoryRepository) UpdateRecord(record *repository.Record) error {
478
-
if _, exists := m.records[key]; !exists {
481
-
m.records[key] = record
485
-
func (m *MockRepositoryRepository) DeleteRecord(did string, collection string, recordKey string) error {
486
-
uri := "at://" + did + "/" + collection + "/" + recordKey
487
-
delete(m.records, uri)
491
-
func (m *MockRepositoryRepository) ListRecords(did string, collection string, limit int, offset int) ([]*repository.Record, error) {
492
-
var records []*repository.Record
493
-
prefix := "at://" + did + "/" + collection + "/"
495
-
for uri, record := range m.records {
496
-
if len(uri) > len(prefix) && uri[:len(prefix)] == prefix {
497
-
records = append(records, record)
501
-
// Simple pagination
503
-
if start >= len(records) {
504
-
return []*repository.Record{}, nil
507
-
end := start + limit
508
-
if end > len(records) {
512
-
return records[start:end], nil