The connection settings were passed as `_journal_mode=`-style parameters, which the SQLite driver drops without an error, so the index ran in rollback-journal mode with no busy timeout. `snapshot list` or `info` reading during a backup could make the backup's next write fail with "database is locked". Both open paths now pass `_pragma=` parameters; foreign keys moved there too. With WAL on, rows committed to the open index can still be in the -wal file, which a copy of the main file misses. The metadata export now copies the index with VACUUM INTO, into an empty 0600 file. The retry after a failed open no longer claims a TRUNCATE recovery; it retries with the same settings. Model: opus-5-5
58 lines
1.3 KiB
Go
58 lines
1.3 KiB
Go
//nolint:testpackage // exercises the unexported copyDatabase helper
|
|
package snapshot
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
"testing"
|
|
|
|
"github.com/spf13/afero"
|
|
"sneak.berlin/go/vaultik/internal/database"
|
|
)
|
|
|
|
// TestCopyDatabaseExportCopyMode verifies that the exported snapshot
|
|
// database copy is created owner-only (0600), even under a lenient 022
|
|
// umask that would otherwise leave a fresh file world-readable.
|
|
//
|
|
//nolint:paralleltest // syscall.Umask is process-global; parallel tests would clash
|
|
func TestCopyDatabaseExportCopyMode(t *testing.T) {
|
|
restore := syscall.Umask(0o022)
|
|
defer syscall.Umask(restore)
|
|
|
|
ctx := context.Background()
|
|
dir := t.TempDir()
|
|
|
|
src := filepath.Join(dir, "index.sqlite")
|
|
|
|
db, err := database.New(ctx, src)
|
|
if err != nil {
|
|
t.Fatalf("creating source index: %v", err)
|
|
}
|
|
|
|
err = db.Close()
|
|
if err != nil {
|
|
t.Fatalf("closing source index: %v", err)
|
|
}
|
|
|
|
dst := filepath.Join(dir, "snapshot.db")
|
|
|
|
sm := &SnapshotManager{fs: afero.NewOsFs()}
|
|
|
|
err = sm.copyDatabase(ctx, src, dst)
|
|
if err != nil {
|
|
t.Fatalf("copyDatabase: %v", err)
|
|
}
|
|
|
|
info, err := os.Stat(dst)
|
|
if err != nil {
|
|
t.Fatalf("stat export copy: %v", err)
|
|
}
|
|
|
|
got := info.Mode().Perm()
|
|
if got != 0o600 {
|
|
t.Fatalf("export copy permissions = %#o, want %#o", got, 0o600)
|
|
}
|
|
}
|