Prune read table row counts before and after to report how many orphaned files, chunks and blobs it removed, and discarded the error from every read. A failed query therefore reported as a count of 0, and the summary showed plausible wrong numbers. A count that cannot be read is now logged as a warning (on stderr, also under --json) and shown as "unknown"; a difference computed from an unknown count is itself unknown. 0 still means the table was empty. No --json document carries these counts, so none can show a false 0. model: claude-opus-4-8 (implementation, review); claude-fable-5-1 (merge)
80 lines
2.7 KiB
Go
80 lines
2.7 KiB
Go
package vaultik //nolint:testpackage // exercises unexported count helpers
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"sneak.berlin/go/vaultik/internal/database"
|
|
"sneak.berlin/go/vaultik/internal/log"
|
|
)
|
|
|
|
// 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.
|
|
//
|
|
//nolint:paralleltest // installs the global logger via log.Initialize
|
|
func TestTableCountForReportSurfacesReadFailure(t *testing.T) {
|
|
log.Initialize(log.Config{})
|
|
|
|
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, countUnknown, 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, countUnknown, 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))
|
|
}
|