Refuse a backup destination an unplugged volume left empty (closes #239)
check / check (push) Waiting to run

Every backup writes vaultik-destination.txt at the destination root.
While the local index holds snapshots, a destination without it is
not present: snapshot create refuses before writing, prune and
snapshot purge fail, snapshot list and snapshot remove warn. remote
nuke deletes it last. The file:// backend no longer creates its
destination directory and checks for it on every write, which
protects a directory on a removable volume but not a mount point.

Tests back up into directories that exist; fixtures standing for a
written destination carry the marker, and two tests check they get
past it.

Judgement call: the marker is rewritten on every backup.
Deviation: for the complexity limit, snapshot name selection moved
into snapshotNamesToCreate and the --force/--json test into
confirmRemoveSnapshot, both unchanged.

Model: opus-5-5
This commit is contained in:
2026-10-13 23:42:41 +00:00
parent dd3667c8e9
commit aae82914ed
41 changed files with 591 additions and 114 deletions
+8 -1
View File
@@ -41,6 +41,11 @@ func TestEntrySnapshotPurgeKeepsMetadataWhenLocalIndexDeleteFails(t *testing.T)
// too, so the purge selects it rather than dropping it as stale.
seedStaleSnapshotRecord(t, indexPath)
// While the local index holds a snapshot, purge fails on a
// destination without the marker a backup writes.
require.NoError(t, os.WriteFile(
filepath.Join(dir, "store", "vaultik-destination.txt"), nil, 0o600))
manifestDir := filepath.Join(dir, "store", "metadata",
snapshot.RemoteSnapshotKey(stalePruneSnapshotID))
require.NoError(t, os.MkdirAll(manifestDir, 0o750))
@@ -59,10 +64,12 @@ func TestEntrySnapshotPurgeKeepsMetadataWhenLocalIndexDeleteFails(t *testing.T)
require.NoError(t, err)
require.NoError(t, db.Close())
code, _, _ := runEntry(t, flagConfig, configPath,
code, _, stderr := runEntry(t, flagConfig, configPath,
cmdSnapshot, cmdPurge, flagOlderThan, "30d", flagForce)
assert.Equal(t, 1, code)
assert.Contains(t, stderr, "simulated index error",
"the purge must fail at the local-index delete")
assert.FileExists(t, manifestPath,
"the snapshot is still in the local index, so its metadata must stay")
}