//nolint:testpackage // inspects the unexported database connection package database import ( "context" "testing" "time" ) // logSnapshotFileIDs logs every file_id present in snapshot_files. func logSnapshotFileIDs(t *testing.T, db *DB) { t.Helper() ctx := context.Background() rows, err := db.conn.QueryContext(ctx, "SELECT file_id FROM snapshot_files") if err != nil { t.Fatal(err) } defer func() { err := rows.Close() if err != nil { t.Logf("failed to close rows: %v", err) } }() t.Log("Files in snapshot_files:") for rows.Next() { var fileID string err := rows.Scan(&fileID) if err != nil { t.Fatal(err) } t.Logf(" - %s", fileID) } err = rows.Err() if err != nil { t.Fatal(err) } } // TestOrphanedFileCleanupDebug tests orphaned file cleanup with debug output // createOrphanDebugFixtures creates one orphaned file, one referenced // file, and the snapshot that will reference the latter. func createOrphanDebugFixtures( ctx context.Context, t *testing.T, repos *Repositories, ) (*File, *File, *Snapshot) { t.Helper() file1 := &File{ Path: "/orphaned.txt", MTime: time.Now().Truncate(time.Second), Size: 1024, Mode: 0644, UID: 1000, GID: 1000, } file2 := &File{ Path: "/referenced.txt", MTime: time.Now().Truncate(time.Second), Size: 2048, Mode: 0644, UID: 1000, GID: 1000, } err := repos.Files.Create(ctx, nil, file1) if err != nil { t.Fatalf("failed to create file1: %v", err) } t.Logf("Created file1 with ID: %s", file1.ID) err = repos.Files.Create(ctx, nil, file2) if err != nil { t.Fatalf("failed to create file2: %v", err) } t.Logf("Created file2 with ID: %s", file2.ID) // Create a snapshot and reference only file2 snapshot := &Snapshot{ ID: internalTestSnapshotID, Hostname: internalTestHost, StartedAt: time.Now(), } err = repos.Snapshots.Create(ctx, nil, snapshot) if err != nil { t.Fatalf("failed to create snapshot: %v", err) } t.Logf("Created snapshot: %s", snapshot.ID) return file1, file2, snapshot } func TestOrphanedFileCleanupDebug(t *testing.T) { t.Parallel() db, cleanup := setupTestDB(t) defer cleanup() ctx := context.Background() repos := NewRepositories(db) file1, file2, snapshot := createOrphanDebugFixtures(ctx, t, repos) // Check snapshot_files before adding count := countRow(t, db, "SELECT COUNT(*) FROM snapshot_files") t.Logf("snapshot_files count before add: %d", count) // Add file2 to snapshot err := repos.Snapshots.AddFileByID(ctx, nil, snapshot.ID.String(), file2.ID) if err != nil { t.Fatalf("failed to add file to snapshot: %v", err) } t.Logf("Added file2 to snapshot") // Check snapshot_files after adding count = countRow(t, db, "SELECT COUNT(*) FROM snapshot_files") t.Logf("snapshot_files count after add: %d", count) // Check which files are referenced logSnapshotFileIDs(t, db) // Check files before cleanup count = countRow(t, db, countFilesQuery) t.Logf("Files count before cleanup: %d", count) // Run orphaned cleanup err = repos.Files.DeleteOrphaned(ctx) if err != nil { t.Fatalf("failed to delete orphaned files: %v", err) } t.Log("Ran orphaned cleanup") // Check files after cleanup count = countRow(t, db, countFilesQuery) t.Logf("Files count after cleanup: %d", count) // List remaining files files, err := repos.Files.ListByPrefix(ctx, "/") if err != nil { t.Fatal(err) } t.Log("Remaining files:") for _, f := range files { t.Logf(" - ID: %s, Path: %s", f.ID, f.Path) } // Check that orphaned file is gone orphanedFile, err := repos.Files.GetByID(ctx, file1.ID) if err != nil { t.Fatalf("error getting file: %v", err) } if orphanedFile != nil { t.Error("orphaned file should have been deleted") // Let's check why it wasn't deleted stillReferenced := countRow(t, db, ` SELECT EXISTS( SELECT 1 FROM snapshot_files WHERE file_id = ? )`, file1.ID) t.Logf("File1 exists in snapshot_files: %v", stillReferenced != 0) } else { t.Log("Orphaned file was correctly deleted") } // Check that referenced file still exists referencedFile, err := repos.Files.GetByID(ctx, file2.ID) if err != nil { t.Fatalf("error getting file: %v", err) } if referencedFile == nil { t.Error("referenced file should not have been deleted") } else { t.Log("Referenced file correctly remains") } }