check / check (push) Waiting to run
Every backup writes vaultik-destination.txt at the destination root. While the local index holds snapshots stored there, a destination without it is not present: snapshot create refuses before writing, prune and snapshot purge fail, and snapshot list and snapshot remove act as for a destination that cannot be listed. snapshot create and snapshot remove check it first. remote nuke deletes the marker last. The file:// backend never creates its destination directory, which protects a directory on a removable volume, not a mount point. Tests back up into directories that exist; fixtures for a written destination carry the marker. Judgement call: the marker is rewritten on every backup. Deviation: for the complexity limit, two blocks moved, unchanged, into snapshotNamesToCreate and confirmRemoveSnapshot. Model: opus-5-5
153 lines
4.4 KiB
Go
153 lines
4.4 KiB
Go
package vaultik_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"sneak.berlin/go/vaultik/internal/log"
|
|
"sneak.berlin/go/vaultik/internal/ui"
|
|
"sneak.berlin/go/vaultik/internal/vaultik"
|
|
)
|
|
|
|
// TestPurgeSnapshots_QuietSuppressesReport checks that `snapshot purge
|
|
// --keep-latest --force` under --quiet deletes the older snapshot and
|
|
// prints nothing.
|
|
func TestPurgeSnapshots_QuietSuppressesReport(t *testing.T) {
|
|
log.Initialize(log.Config{})
|
|
t.Parallel()
|
|
|
|
v := setupPurgeTest(t, snapHostname, []string{snapHomeT0, snapHomeT1})
|
|
|
|
var out bytes.Buffer
|
|
|
|
v.Stdout = &out
|
|
v.UI = ui.NewWithColor(&out, false)
|
|
v.UI.SetQuiet(true)
|
|
|
|
require.NoError(t, v.PurgeSnapshotsWithOptions(&vaultik.SnapshotPurgeOptions{
|
|
KeepLatest: true,
|
|
Force: true,
|
|
}))
|
|
|
|
assert.Equal(t, []string{snapHomeT1}, listRemainingSnapshots(t, v))
|
|
assert.Empty(t, out.String())
|
|
}
|
|
|
|
// TestPurgeSnapshots_QuietPromptListsSnapshots checks that `snapshot
|
|
// purge --keep-latest` without --force still lists the snapshots its
|
|
// prompt asks to delete under --quiet.
|
|
func TestPurgeSnapshots_QuietPromptListsSnapshots(t *testing.T) {
|
|
log.Initialize(log.Config{})
|
|
t.Parallel()
|
|
|
|
v := setupPurgeTest(t, snapHostname, []string{snapHomeT0, snapHomeT1})
|
|
|
|
var out bytes.Buffer
|
|
|
|
v.Stdout = &out
|
|
v.UI = ui.NewWithColor(&out, false)
|
|
v.UI.SetQuiet(true)
|
|
|
|
// Stdin is empty, so the prompt reads no answer and cancels.
|
|
require.NoError(t, v.PurgeSnapshotsWithOptions(&vaultik.SnapshotPurgeOptions{
|
|
KeepLatest: true,
|
|
}))
|
|
|
|
assert.Contains(t, out.String(), snapHomeT0)
|
|
assert.Contains(t, out.String(), "Delete 1 snapshot(s)? [y/N]")
|
|
assert.Len(t, listRemainingSnapshots(t, v), 2)
|
|
}
|
|
|
|
// TestPruneBlobs_QuietPromptShowsBlobTotal checks that `prune` without
|
|
// --force still states the count and total size of the blobs its prompt
|
|
// asks to delete under --quiet.
|
|
func TestPruneBlobs_QuietPromptShowsBlobTotal(t *testing.T) {
|
|
log.Initialize(log.Config{})
|
|
t.Parallel()
|
|
|
|
env := newListEnv(t)
|
|
env.v.UI.SetQuiet(true)
|
|
|
|
// The store holds no manifest, so nothing references this blob.
|
|
addBlob(t, env.store.testStorer, testBlobHashA)
|
|
|
|
// Stdin is empty, so the prompt reads no answer and cancels.
|
|
require.NoError(t, env.v.PruneBlobs(&vaultik.PruneOptions{}))
|
|
|
|
assert.Contains(t, env.stdout.String(),
|
|
"Found 1 unreferenced blob(s) totaling")
|
|
assert.Contains(t, env.stdout.String(),
|
|
"Delete 1 unreferenced blob(s)? [y/N]")
|
|
}
|
|
|
|
// TestRemoveSnapshot_QuietSuppressesReport checks that `snapshot remove`
|
|
// under --quiet prints nothing with --dry-run or with --force, and that
|
|
// the --json document still prints.
|
|
func TestRemoveSnapshot_QuietSuppressesReport(t *testing.T) {
|
|
log.Initialize(log.Config{})
|
|
t.Parallel()
|
|
|
|
newQuietRemover := func() (*vaultik.TestVaultik, *testStorer) {
|
|
store := newTestStorer()
|
|
addManifest(t, store, "snapshot-001", nil)
|
|
|
|
// A backup wrote to this destination, so it carries the marker.
|
|
require.NoError(t, store.Put(context.Background(), markerFile,
|
|
bytes.NewReader(nil)))
|
|
|
|
tv := vaultik.NewForTesting(store)
|
|
tv.Repositories = newLocalIndex(t, "snapshot-001")
|
|
tv.UI.SetQuiet(true)
|
|
|
|
return tv, store
|
|
}
|
|
|
|
tv, store := newQuietRemover()
|
|
|
|
_, err := tv.RemoveSnapshot("snapshot-001",
|
|
&vaultik.RemoveOptions{DryRun: true})
|
|
require.NoError(t, err)
|
|
assert.Empty(t, tv.Stdout.String(),
|
|
"--dry-run must print nothing under --quiet")
|
|
|
|
_, err = tv.RemoveSnapshot("snapshot-001",
|
|
&vaultik.RemoveOptions{Force: true})
|
|
require.NoError(t, err)
|
|
assert.False(t, store.hasKey(remoteKeyPath("snapshot-001")))
|
|
assert.Empty(t, tv.Stdout.String(),
|
|
"--force must print nothing under --quiet")
|
|
|
|
tv, _ = newQuietRemover()
|
|
|
|
_, err = tv.RemoveSnapshot("snapshot-001",
|
|
&vaultik.RemoveOptions{JSON: true})
|
|
require.NoError(t, err)
|
|
|
|
var result vaultik.RemoveResult
|
|
|
|
require.NoError(t, json.Unmarshal(tv.Stdout.Bytes(), &result),
|
|
"the --json document must still print under --quiet")
|
|
assert.True(t, result.RemoteRemoved)
|
|
}
|
|
|
|
// TestNukeRemote_QuietSuppressesReport checks that `remote nuke --force`
|
|
// under --quiet prints nothing while it removes a snapshot's metadata
|
|
// and its blobs.
|
|
func TestNukeRemote_QuietSuppressesReport(t *testing.T) {
|
|
log.Initialize(log.Config{})
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
storeDir := filepath.Join(t.TempDir(), "store")
|
|
v, _, out := backUpToFileDestination(ctx, t, storeDir)
|
|
v.UI.SetQuiet(true)
|
|
|
|
require.NoError(t, v.NukeRemote(true))
|
|
assert.Empty(t, out.String())
|
|
}
|