check / check (push) Waiting to run
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
139 lines
4.5 KiB
Go
139 lines
4.5 KiB
Go
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
|
|
// the discarded-error bug: getTableCount for a table its query cannot
|
|
// resolve must not silently become 0. A count that could not be read is
|
|
// reported as unknown, which a reader can tell apart from an empty table.
|
|
func TestTableCountForReportSurfacesReadFailure(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() })
|
|
|
|
v := &Vaultik{DB: db}
|
|
v.SetContext(ctx)
|
|
|
|
// A table present in the schema reads as a real count.
|
|
blobs := v.tableCountForReport("blobs")
|
|
require.NotNil(t, blobs, "an existing table must read as a real count")
|
|
assert.Equal(t, int64(0), *blobs)
|
|
|
|
// A syntactically valid name the sanitizer accepts but whose table
|
|
// the query cannot resolve is the exact shape #96 describes: a
|
|
// would-be loud failure that used to be discarded into a 0.
|
|
_, err = v.getTableCount("snapshots_missing")
|
|
require.Error(t, err, "a query against a nonexistent table must fail")
|
|
|
|
missing := v.tableCountForReport("snapshots_missing")
|
|
assert.Nil(t, missing, "a failed read is unknown, not a count")
|
|
|
|
// The rendered count for a failed read must say unknown, never 0.
|
|
assert.Equal(t, unknownText, countText(missing))
|
|
assert.NotEqual(t, "0", countText(missing))
|
|
}
|
|
|
|
// TestCountTextDistinguishesEmptyFromUnknown pins the distinction the
|
|
// output has to preserve: 0 means the table was empty, "unknown" means
|
|
// the count could not be read.
|
|
func TestCountTextDistinguishesEmptyFromUnknown(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
zero := int64(0)
|
|
seven := int64(7)
|
|
|
|
assert.Equal(t, "0", countText(&zero))
|
|
assert.Equal(t, "7", countText(&seven))
|
|
assert.Equal(t, unknownText, countText(nil))
|
|
}
|
|
|
|
// TestCountDiffUnknownWhenEitherSideUnknown checks that a delta computed
|
|
// from an unreadable count is itself unknown rather than a plausible
|
|
// number.
|
|
func TestCountDiffUnknownWhenEitherSideUnknown(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
before := int64(10)
|
|
after := int64(3)
|
|
|
|
require.NotNil(t, countDiff(&before, &after))
|
|
assert.Equal(t, int64(7), *countDiff(&before, &after))
|
|
|
|
assert.Nil(t, countDiff(nil, &after), "unknown before yields unknown delta")
|
|
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).")
|
|
}
|