Never skip a local index error for a directory or symlink (closes #284)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
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, so countFailedFile no longer checks for a directory. The test injects the error with a SQLite trigger that refuses the directory's files row. Model: opus-5-5
This commit was merged in pull request #285.
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user