From 3ac89dc374094d925c6cf6552505c0f0d0a11f98 Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Tue, 13 Oct 2026 22:44:02 +0000 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 cef9cbf..d9eef00 100644 --- a/internal/vaultik/snapshot.go +++ b/internal/vaultik/snapshot.go @@ -1890,13 +1890,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 -- 2.54.0