- Changed blob table to use ID (UUID) as primary key instead of hash - Blob records are now created at packing start, enabling immediate chunk associations - Implemented streaming chunking to process large files without memory exhaustion - Fixed blob manifest generation to include all referenced blobs - Updated all foreign key references from blob_hash to blob_id - Added progress reporting and improved error handling - Enforced encryption requirement for all blob packing - Updated tests to use test encryption keys - Added Cyrillic transliteration to README
148 lines
3.6 KiB
Go
148 lines
3.6 KiB
Go
package backup
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"git.eeqj.de/sneak/vaultik/internal/database"
|
|
"git.eeqj.de/sneak/vaultik/internal/log"
|
|
)
|
|
|
|
func TestCleanSnapshotDBEmptySnapshot(t *testing.T) {
|
|
// Initialize logger
|
|
log.Initialize(log.Config{})
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create a test database
|
|
tempDir := t.TempDir()
|
|
dbPath := filepath.Join(tempDir, "test.db")
|
|
db, err := database.New(ctx, dbPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to create database: %v", err)
|
|
}
|
|
|
|
repos := database.NewRepositories(db)
|
|
|
|
// Create an empty snapshot
|
|
snapshot := &database.Snapshot{
|
|
ID: "empty-snapshot",
|
|
Hostname: "test-host",
|
|
}
|
|
|
|
err = repos.WithTx(ctx, func(ctx context.Context, tx *sql.Tx) error {
|
|
return repos.Snapshots.Create(ctx, tx, snapshot)
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to create snapshot: %v", err)
|
|
}
|
|
|
|
// Create some files and chunks not associated with any snapshot
|
|
file := &database.File{Path: "/orphan/file.txt", Size: 1000}
|
|
chunk := &database.Chunk{ChunkHash: "orphan-chunk", SHA256: "orphan-chunk", Size: 500}
|
|
|
|
err = repos.WithTx(ctx, func(ctx context.Context, tx *sql.Tx) error {
|
|
if err := repos.Files.Create(ctx, tx, file); err != nil {
|
|
return err
|
|
}
|
|
return repos.Chunks.Create(ctx, tx, chunk)
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to create orphan data: %v", err)
|
|
}
|
|
|
|
// Close the database
|
|
if err := db.Close(); err != nil {
|
|
t.Fatalf("failed to close database: %v", err)
|
|
}
|
|
|
|
// Copy database
|
|
tempDBPath := filepath.Join(tempDir, "temp.db")
|
|
if err := copyFile(dbPath, tempDBPath); err != nil {
|
|
t.Fatalf("failed to copy database: %v", err)
|
|
}
|
|
|
|
// Clean the database
|
|
sm := &SnapshotManager{}
|
|
if err := sm.cleanSnapshotDB(ctx, tempDBPath, snapshot.ID); err != nil {
|
|
t.Fatalf("failed to clean snapshot database: %v", err)
|
|
}
|
|
|
|
// Verify the cleaned database
|
|
cleanedDB, err := database.New(ctx, tempDBPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to open cleaned database: %v", err)
|
|
}
|
|
defer func() {
|
|
if err := cleanedDB.Close(); err != nil {
|
|
t.Errorf("failed to close database: %v", err)
|
|
}
|
|
}()
|
|
|
|
cleanedRepos := database.NewRepositories(cleanedDB)
|
|
|
|
// Verify snapshot exists
|
|
verifySnapshot, err := cleanedRepos.Snapshots.GetByID(ctx, snapshot.ID)
|
|
if err != nil {
|
|
t.Fatalf("failed to get snapshot: %v", err)
|
|
}
|
|
if verifySnapshot == nil {
|
|
t.Error("snapshot should exist")
|
|
}
|
|
|
|
// Verify orphan file is gone
|
|
f, err := cleanedRepos.Files.GetByPath(ctx, file.Path)
|
|
if err != nil {
|
|
t.Fatalf("failed to check file: %v", err)
|
|
}
|
|
if f != nil {
|
|
t.Error("orphan file should not exist")
|
|
}
|
|
|
|
// Verify orphan chunk is gone
|
|
c, err := cleanedRepos.Chunks.GetByHash(ctx, chunk.ChunkHash)
|
|
if err != nil {
|
|
t.Fatalf("failed to check chunk: %v", err)
|
|
}
|
|
if c != nil {
|
|
t.Error("orphan chunk should not exist")
|
|
}
|
|
}
|
|
|
|
func TestCleanSnapshotDBNonExistentSnapshot(t *testing.T) {
|
|
// Initialize logger
|
|
log.Initialize(log.Config{})
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create a test database
|
|
tempDir := t.TempDir()
|
|
dbPath := filepath.Join(tempDir, "test.db")
|
|
db, err := database.New(ctx, dbPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to create database: %v", err)
|
|
}
|
|
|
|
// Close immediately
|
|
if err := db.Close(); err != nil {
|
|
t.Fatalf("failed to close database: %v", err)
|
|
}
|
|
|
|
// Copy database
|
|
tempDBPath := filepath.Join(tempDir, "temp.db")
|
|
if err := copyFile(dbPath, tempDBPath); err != nil {
|
|
t.Fatalf("failed to copy database: %v", err)
|
|
}
|
|
|
|
// Try to clean with non-existent snapshot
|
|
sm := &SnapshotManager{}
|
|
err = sm.cleanSnapshotDB(ctx, tempDBPath, "non-existent-snapshot")
|
|
|
|
// Should not error - it will just delete everything
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|