From 5db2e920e5f19abb4dcc4a47a829c0cb3723131e Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Wed, 14 Oct 2026 02:03:51 +0200 Subject: [PATCH] Label the index cleanup totals as totals (closes #306) The summary printed by the local index cleanup before each backup gave, in brackets after each removed count, the number of snapshots, files, chunks or blobs left in the local index, worded "(N remain)", which read as incomplete or orphaned rows left over: removing the only incomplete snapshot printed "1 removed (1 remain)". Each line now labels that figure as the total of its kind left in the local index. Model: opus-5-5 --- internal/vaultik/prune_count_test.go | 60 ++++++++++++++++++++++++++++ internal/vaultik/snapshot.go | 12 ++++-- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/internal/vaultik/prune_count_test.go b/internal/vaultik/prune_count_test.go index 11401c6..2009e01 100644 --- a/internal/vaultik/prune_count_test.go +++ b/internal/vaultik/prune_count_test.go @@ -1,13 +1,17 @@ package vaultik //nolint:testpackage // exercises unexported count helpers import ( + "bytes" "context" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "sneak.berlin/go/vaultik/internal/database" "sneak.berlin/go/vaultik/internal/log" + "sneak.berlin/go/vaultik/internal/snapshot" + "sneak.berlin/go/vaultik/internal/ui" ) // TestTableCountForReportSurfacesReadFailure is the regression guard for @@ -76,3 +80,59 @@ func TestCountDiffUnknownWhenEitherSideUnknown(t *testing.T) { assert.Nil(t, countDiff(&before, nil), "unknown after yields unknown delta") assert.Nil(t, countDiff(nil, nil)) } + +// TestPruneDatabaseSummaryLabelsTotals removes the only incomplete +// snapshot from an index that also holds a complete one. The figure in +// brackets is every snapshot left in the local index, so the line must +// say so rather than read as incomplete snapshots that remain. +func TestPruneDatabaseSummaryLabelsTotals(t *testing.T) { + log.Initialize(log.Config{}) + t.Parallel() + + ctx := context.Background() + + db, err := database.New(ctx, ":memory:") + require.NoError(t, err) + t.Cleanup(func() { _ = db.Close() }) + + repos := database.NewRepositories(db) + + startedAt := time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC) + completedAt := startedAt.Add(time.Minute) + + require.NoError(t, repos.Snapshots.Create(ctx, nil, &database.Snapshot{ + ID: "host_complete_2026-01-02T03:04:05Z", + Hostname: "host", + StartedAt: startedAt, + CompletedAt: &completedAt, + })) + require.NoError(t, repos.Snapshots.Create(ctx, nil, &database.Snapshot{ + ID: "host_incomplete_2026-01-02T03:04:05Z", + Hostname: "host", + StartedAt: startedAt, + })) + + var out bytes.Buffer + + v := &Vaultik{ + DB: db, + Repositories: repos, + SnapshotManager: snapshot.NewSnapshotManager( + snapshot.SnapshotManagerParams{Repos: repos}), + UI: ui.NewWithColor(&out, false), + } + v.SetContext(ctx) + + result, err := v.PruneDatabase() + require.NoError(t, err) + require.Equal(t, int64(1), result.SnapshotsDeleted) + + assert.Contains(t, out.String(), "Incomplete snapshots: 1 removed "+ + "(total snapshots left in the local index: 1).") + assert.Contains(t, out.String(), "Orphaned files: 0 removed "+ + "(total files left in the local index: 0).") + assert.Contains(t, out.String(), "Orphaned chunks: 0 removed "+ + "(total chunks left in the local index: 0).") + assert.Contains(t, out.String(), "Orphaned blobs: 0 removed "+ + "(total blobs left in the local index: 0).") +} diff --git a/internal/vaultik/snapshot.go b/internal/vaultik/snapshot.go index 63a9809..52a638a 100644 --- a/internal/vaultik/snapshot.go +++ b/internal/vaultik/snapshot.go @@ -1902,13 +1902,17 @@ func (v *Vaultik) PruneDatabase() (*PruneResult, error) { snapshotsRemain := countDiff(snapshotCountBefore, &result.SnapshotsDeleted) v.UI.Completef("Pruned local index database.") - v.UI.Detailf("Incomplete snapshots: %s removed (%s remain).", + v.UI.Detailf("Incomplete snapshots: %s removed "+ + "(total snapshots left in the local index: %s).", countText(&result.SnapshotsDeleted), countText(snapshotsRemain)) - v.UI.Detailf("Orphaned files: %s removed (%s remain).", + v.UI.Detailf("Orphaned files: %s removed "+ + "(total files left in the local index: %s).", countText(result.FilesDeleted), countText(fileCountAfter)) - v.UI.Detailf("Orphaned chunks: %s removed (%s remain).", + v.UI.Detailf("Orphaned chunks: %s removed "+ + "(total chunks left in the local index: %s).", countText(result.ChunksDeleted), countText(chunkCountAfter)) - v.UI.Detailf("Orphaned blobs: %s removed (%s remain).", + v.UI.Detailf("Orphaned blobs: %s removed "+ + "(total blobs left in the local index: %s).", countText(result.BlobsDeleted), countText(blobCountAfter)) return result, nil