check / check (push) Waiting to run
Under --skip-errors, an error recording a directory or symlink in the local index was skipped like an unreadable file. The snapshot completed without the entry, and a restore did not recreate it. processFileWithErrorHandling now records a directory or symlink itself and returns any error, which stops the backup with or without the flag. The skip sees only a regular file's errors. The test injects the error with a SQLite trigger that refuses the directory's files row. Model: opus-5-5
76 lines
2.3 KiB
Go
76 lines
2.3 KiB
Go
package vaultik_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/spf13/afero"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"sneak.berlin/go/vaultik/internal/config"
|
|
"sneak.berlin/go/vaultik/internal/database"
|
|
"sneak.berlin/go/vaultik/internal/log"
|
|
"sneak.berlin/go/vaultik/internal/storage"
|
|
"sneak.berlin/go/vaultik/internal/vaultik"
|
|
)
|
|
|
|
// --skip-errors skips only a file that cannot be opened or read. A local
|
|
// index error while a directory is recorded stops the backup, and the
|
|
// snapshot is not recorded as complete. See
|
|
// https://git.eeqj.de/sneak/vaultik/issues/284.
|
|
func TestSkipErrorsBackupStopsOnIndexErrorRecordingDirectory(t *testing.T) {
|
|
log.Initialize(log.Config{})
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
|
|
// The scan walks the source path with symlinks resolved, so dirPath
|
|
// must be spelled the same way to match the row the scan inserts.
|
|
tempDir, err := filepath.EvalSymlinks(t.TempDir())
|
|
require.NoError(t, err)
|
|
|
|
srcDir := filepath.Join(tempDir, "src")
|
|
dirPath := filepath.Join(srcDir, "dir")
|
|
|
|
fs := afero.NewOsFs()
|
|
require.NoError(t, fs.MkdirAll(dirPath, 0o755))
|
|
require.NoError(t, afero.WriteFile(fs,
|
|
filepath.Join(dirPath, "file.txt"), []byte("file content"), 0o644))
|
|
|
|
cfg := faultTestConfig()
|
|
cfg.IndexPath = filepath.Join(tempDir, "index.sqlite")
|
|
cfg.Snapshots = map[string]config.SnapshotConfig{
|
|
"tree": {Paths: []string{srcDir}},
|
|
}
|
|
|
|
store, err := storage.NewFileStorer(filepath.Join(tempDir, "remote"))
|
|
require.NoError(t, err)
|
|
|
|
db, err := database.New(ctx, cfg.IndexPath)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { _ = db.Close() })
|
|
|
|
// The local index refuses the directory's files row.
|
|
_, err = db.Conn().ExecContext(ctx, fmt.Sprintf(`
|
|
CREATE TRIGGER refuse_directory BEFORE INSERT ON files
|
|
WHEN NEW.path = '%s'
|
|
BEGIN SELECT RAISE(ABORT, 'simulated index error'); END`, dirPath))
|
|
require.NoError(t, err)
|
|
|
|
repos := database.NewRepositories(db)
|
|
v := newBackupVaultik(ctx, cfg, store, repos, db, fs)
|
|
|
|
err = v.CreateSnapshot(&vaultik.SnapshotCreateOptions{
|
|
SkipErrors: true,
|
|
Snapshots: []string{"tree"},
|
|
})
|
|
require.ErrorContains(t, err, "simulated index error")
|
|
|
|
snapshots, err := repos.Snapshots.ListRecent(ctx, listRecentTestLimit)
|
|
require.NoError(t, err)
|
|
require.Len(t, snapshots, 1)
|
|
assert.Nil(t, snapshots[0].CompletedAt)
|
|
}
|