d34dfc84c0
On a pinned tier (the hub holds a CONFIRMED append-only key) the only legitimate fall of the box's snapshot count is a clean-up window the hub opened. The checker now compares prev - cur with what the windows closed since the previous trustworthy report removed (store.RemovedByWindowsBetween, 2 h slack for the in-run count lag); any unexplained fall, even one snapshot, raises offsite_snapshots_dropped (error) saying 'outside any clean-up window the hub opened'. A window that cannot say what it removed (timeout, still open) explains anything: no alarm, one INFO line. Non-pinned tiers keep the half-rule. Untrustworthy reports: unchanged (no alarm, baseline kept). Red tests: r435_pinned_drop_test.go (3 fail with the pinned rule disabled; WindowExplainsFall fails when windows are ignored), r435_windows_between_test.go. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
47 lines
1.8 KiB
Go
47 lines
1.8 KiB
Go
package store
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
// R-435: RemovedByWindowsBetween sums only windows closed inside the interval, and says „unknown"
|
||
// for a window that cannot say what it removed.
|
||
func TestR435_RemovedByWindowsBetween(t *testing.T) {
|
||
s := newTestStore(t)
|
||
at := func(ts string) time.Time { v, _ := time.Parse("2006-01-02 15:04:05", ts); return v }
|
||
ins := func(cust, opened, closed string, before, after any) {
|
||
t.Helper()
|
||
var c any
|
||
if closed != "" {
|
||
c = closed
|
||
}
|
||
if _, err := s.db.Exec(`INSERT INTO offsite_windows (customer_id, opened_at, closes_by, closed_at, count_before, count_after) VALUES (?, ?, ?, ?, ?, ?)`,
|
||
cust, opened, opened, c, before, after); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
ins("a", "2026-10-01 03:00:00", "2026-10-01 03:10:00", 69, 60) // inside → 9
|
||
ins("a", "2026-09-20 03:00:00", "2026-09-20 03:10:00", 80, 69) // before the interval
|
||
ins("a", "2026-10-03 03:00:00", "2026-10-03 03:10:00", 60, 50) // after the interval
|
||
ins("b", "2026-10-01 03:00:00", "2026-10-01 03:10:00", 50, 40) // another customer
|
||
|
||
from, to := at("2026-09-30 00:00:00"), at("2026-10-02 00:00:00")
|
||
if n, unk := s.RemovedByWindowsBetween("a", from, to); n != 9 || unk {
|
||
t.Fatalf("a: want 9 known, got %d unknown=%v", n, unk)
|
||
}
|
||
if n, unk := s.RemovedByWindowsBetween("c", from, to); n != 0 || unk {
|
||
t.Fatalf("no window: want 0 known, got %d unknown=%v", n, unk)
|
||
}
|
||
// a timeout close (count_after −1) inside the interval → unknown
|
||
ins("d", "2026-10-01 03:00:00", "2026-10-01 03:40:00", 69, -1)
|
||
if _, unk := s.RemovedByWindowsBetween("d", from, to); !unk {
|
||
t.Fatal("a timeout-closed window must make the interval unknown")
|
||
}
|
||
// a window still open → unknown
|
||
ins("e", "2026-10-01 03:00:00", "", 69, nil)
|
||
if _, unk := s.RemovedByWindowsBetween("e", from, to); !unk {
|
||
t.Fatal("an open window must make the interval unknown")
|
||
}
|
||
}
|