Files
vaultik/internal/vaultik/verify_quiet_test.go
T
clawbot 85d4ef118d
check / check (push) Waiting to run
Keep command output to the README's stdout and stderr rules (closes #224)
The startup banner moves from stdout to stderr, so a `completion`
script, a `config get` value and the hidden `__complete` command print
only their own output. `--quiet`, `--cron` and `--json` still suppress
it.

A failing `remote info`, `prune` or `snapshot remove` under `--json`
now reports its error on stderr. Their reporters returned early under
`--json`, so the failure reached neither stream.

`snapshot verify --quiet` writes no report. A failure is still
returned and printed on stderr, with the same exit status.

Judgement call: the banner's stream, posted on the issue for the owner.

Model: opus-5-5
2026-10-07 01:12:15 +02:00

103 lines
3.2 KiB
Go

package vaultik_test
import (
"bytes"
"context"
"io"
"os"
"path/filepath"
"testing"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
"sneak.berlin/go/vaultik/internal/log"
"sneak.berlin/go/vaultik/internal/snapshot"
"sneak.berlin/go/vaultik/internal/ui"
"sneak.berlin/go/vaultik/internal/vaultik"
)
// TestVerify_QuietSuppressesReport is the --quiet contract for
// `snapshot verify`: neither shallow nor deep verify writes its report,
// a failed verify still returns its error (which the cli layer prints
// on stderr), and the --json document still emits.
func TestVerify_QuietSuppressesReport(t *testing.T) {
log.Initialize(log.Config{})
t.Parallel()
fs := afero.NewOsFs()
tempDir := t.TempDir()
dataDir := filepath.Join(tempDir, "source")
storeDir := filepath.Join(tempDir, "remote")
dbPath := filepath.Join(tempDir, "index.sqlite")
chunkSize := int64(32 * 1024)
maxBlobSize := int64(128 * 1024)
require.NoError(t, fs.MkdirAll(dataDir, 0o755))
require.NoError(t, afero.WriteFile(fs,
filepath.Join(dataDir, "data.bin"),
bytesPattern("quiet-", int(maxBlobSize*2)), 0o644))
ctx := context.Background()
cfg, storer, snapshotID := runFileStorageBackup(
ctx, t, fs, dataDir, storeDir, dbPath, chunkSize, maxBlobSize)
// The UI writes to the same buffer as Stdout, as both write to the
// process's stdout in production.
var stdout bytes.Buffer
newQuietVerifier := func() *vaultik.Vaultik {
v := &vaultik.Vaultik{
Config: cfg,
Storage: storer,
Fs: fs,
Stdout: &stdout,
Stderr: io.Discard,
UI: ui.NewWithColor(&stdout, false),
}
v.SetContext(ctx)
v.UI.SetQuiet(true)
return v
}
require.NoError(t, newQuietVerifier().VerifySnapshotWithOptions(
snapshotID, &vaultik.VerifyOptions{}))
require.Empty(t, stdout.String(),
"shallow verify must write no report under --quiet")
require.NoError(t, newQuietVerifier().VerifySnapshotWithOptions(
snapshotID, &vaultik.VerifyOptions{Deep: true}))
require.Empty(t, stdout.String(),
"deep verify must write no report under --quiet")
require.NoError(t, newQuietVerifier().VerifySnapshotWithOptions(
snapshotID, &vaultik.VerifyOptions{JSON: true}))
require.Equal(t, "ok", decodeVerifyResult(t, stdout.Bytes()).Status,
"the --json document must still emit under --quiet")
// A snapshot without its encrypted database fails shallow verify. A
// failed report also lists each missing blob and each blob of the
// wrong size, so remove one blob and grow another.
require.NoError(t, os.Remove(filepath.Join(storeDir, "metadata",
snapshot.RemoteSnapshotKey(snapshotID), "db.zst.age")))
blobFiles, err := filepath.Glob(
filepath.Join(storeDir, "blobs", "*", "*", "*"))
require.NoError(t, err)
require.GreaterOrEqual(t, len(blobFiles), 2,
"the snapshot must span two blobs, one to remove and one to grow")
require.NoError(t, os.Remove(blobFiles[0]))
growOneBlob(t, fs, filepath.Join(storeDir, "blobs"))
stdout.Reset()
require.Error(t, newQuietVerifier().VerifySnapshotWithOptions(
snapshotID, &vaultik.VerifyOptions{}),
"--quiet must not change the outcome of a failed verify")
require.Empty(t, stdout.String(),
"a failed verify must write no report under --quiet")
}