restic: tighten RunCheck lock sniff + RunStats zero-snapshot test
Narrow the LockPresent predicate from bare "locked" (too broad) to "stale lock" and "already locked" — the two phrases restic actually emits. Replace TestRunCheckParsesLock with table-driven TestRunCheckLockSniff covering both trigger phrases and a benign "locked-file" line that must not set LockPresent. Add TestRunStatsZeroSnapshots to pin that RunStats accepts zero-snapshot JSON without error.
This commit is contained in:
@@ -367,7 +367,7 @@ func (e Env) RunCheck(ctx context.Context, subsetPct int, handle LineHandler) (C
|
|||||||
var res CheckResult
|
var res CheckResult
|
||||||
sniff := func(stream, line string, ev any) {
|
sniff := func(stream, line string, ev any) {
|
||||||
if stream == "stderr" {
|
if stream == "stderr" {
|
||||||
if strings.Contains(line, "Found stale lock") || strings.Contains(line, "locked") {
|
if strings.Contains(line, "stale lock") || strings.Contains(line, "already locked") {
|
||||||
res.LockPresent = true
|
res.LockPresent = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,18 +54,34 @@ func TestRunPruneInvokesPrune(t *testing.T) {
|
|||||||
|
|
||||||
// --- B2: RunCheck ---
|
// --- B2: RunCheck ---
|
||||||
|
|
||||||
func TestRunCheckParsesLock(t *testing.T) {
|
func TestRunCheckLockSniff(t *testing.T) {
|
||||||
bin := setupScriptBin(t, `echo "Found stale lock" >&2`)
|
cases := []struct {
|
||||||
env := Env{Bin: bin}
|
name string
|
||||||
res, err := env.RunCheck(context.Background(), 0, nil)
|
stderrLine string
|
||||||
if err != nil {
|
wantLocked bool
|
||||||
t.Fatalf("RunCheck returned unexpected error: %v", err)
|
}{
|
||||||
|
{"stale lock", "Found stale lock from PID 1234", true},
|
||||||
|
{"already locked", "repository is already locked exclusively", true},
|
||||||
|
{"benign mention", "subdir/locked-file ok", false},
|
||||||
|
{"empty", "", false},
|
||||||
}
|
}
|
||||||
if !res.LockPresent {
|
for _, c := range cases {
|
||||||
t.Fatal("expected LockPresent=true")
|
t.Run(c.name, func(t *testing.T) {
|
||||||
}
|
// Script emits the line on stderr, then exits 0.
|
||||||
if res.ErrorsFound {
|
script := fmt.Sprintf(`printf '%%s\n' %q >&2`, c.stderrLine)
|
||||||
t.Fatal("expected ErrorsFound=false")
|
bin := setupScriptBin(t, script)
|
||||||
|
env := Env{Bin: bin}
|
||||||
|
res, err := env.RunCheck(context.Background(), 0, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RunCheck returned unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if res.LockPresent != c.wantLocked {
|
||||||
|
t.Fatalf("LockPresent: got %v, want %v (line: %q)", res.LockPresent, c.wantLocked, c.stderrLine)
|
||||||
|
}
|
||||||
|
if res.ErrorsFound {
|
||||||
|
t.Fatal("expected ErrorsFound=false")
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,3 +165,21 @@ func TestRunStatsErrorsWithoutJSON(t *testing.T) {
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunStatsZeroSnapshots(t *testing.T) {
|
||||||
|
// Confirms RunStats succeeds and returns a valid *RepoStats when the
|
||||||
|
// repo has no snapshots (snapshots_count=0). A regression that
|
||||||
|
// re-added a "SnapshotsCount > 0" guard would return an error here.
|
||||||
|
bin := setupScriptBin(t, `echo '{"total_size":0,"total_uncompressed_size":0,"snapshots_count":0,"total_file_count":0,"total_blob_count":0}'`)
|
||||||
|
env := Env{Bin: bin}
|
||||||
|
stats, err := env.RunStats(context.Background(), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RunStats with zero snapshots returned unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if stats == nil {
|
||||||
|
t.Fatal("expected non-nil *RepoStats, got nil")
|
||||||
|
}
|
||||||
|
if stats.SnapshotsCount != 0 {
|
||||||
|
t.Fatalf("SnapshotsCount: got %d, want 0", stats.SnapshotsCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user