e1ff3210eb
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
76 lines
3.3 KiB
Go
76 lines
3.3 KiB
Go
package store
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
// R-435: WindowCreditsBetween lists only windows closed inside the interval (or still open), and each
|
||
// window explains at most its hub-set max_remove — a box's count_after cannot widen it, and a window with
|
||
// no usable count_after explains exactly its cap. Only a window with no cap makes the answer unknown.
|
||
// RED-PROOF (security review 2026-10-08): drop the max_remove cap → „lying box" returns 69, not 34 → FAILS.
|
||
func TestR435_RemovedByWindowsBetween(t *testing.T) {
|
||
s := newTestStore(t)
|
||
sum := func(cust string, from, to time.Time) (int, bool) {
|
||
cs, unk := s.WindowCreditsBetween(cust, from, to)
|
||
n := 0
|
||
for _, c := range cs {
|
||
n += c.Explains
|
||
}
|
||
return n, unk
|
||
}
|
||
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, maxRemove any) {
|
||
t.Helper()
|
||
var c any
|
||
if closed != "" {
|
||
c = closed
|
||
}
|
||
closesBy := "2026-10-01 23:00:00" // a window's deadline; an open one past it before `from` is stale
|
||
if _, err := s.db.Exec(`INSERT INTO offsite_windows (customer_id, opened_at, closes_by, closed_at, count_before, count_after, max_remove) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||
cust, opened, closesBy, c, before, after, maxRemove); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
ins("a", "2026-10-01 03:00:00", "2026-10-01 03:10:00", 69, 60, 34) // inside → 9
|
||
ins("a", "2026-09-20 03:00:00", "2026-09-20 03:10:00", 80, 69, 40) // before the interval
|
||
ins("a", "2026-10-03 03:00:00", "2026-10-03 03:10:00", 60, 50, 30) // after the interval
|
||
ins("b", "2026-10-01 03:00:00", "2026-10-01 03:10:00", 50, 40, 25) // another customer
|
||
|
||
from, to := at("2026-09-30 00:00:00"), at("2026-10-02 00:00:00")
|
||
if n, unk := sum("a", from, to); n != 9 || unk {
|
||
t.Fatalf("a: want 9 known, got %d unknown=%v", n, unk)
|
||
}
|
||
if n, unk := sum("c", from, to); n != 0 || unk {
|
||
t.Fatalf("no window: want 0 known, got %d unknown=%v", n, unk)
|
||
}
|
||
// a box that claims it removed everything explains only the cap the hub granted
|
||
ins("l", "2026-10-01 03:00:00", "2026-10-01 03:10:00", 69, 0, 34)
|
||
if n, unk := sum("l", from, to); n != 34 || unk {
|
||
t.Fatalf("lying box: want the cap 34, got %d unknown=%v", n, unk)
|
||
}
|
||
// a timeout close (count_after −1) inside the interval → explains its cap
|
||
ins("d", "2026-10-01 03:00:00", "2026-10-01 03:40:00", 69, -1, 10)
|
||
if n, unk := sum("d", from, to); n != 10 || unk {
|
||
t.Fatalf("timeout-closed window: want its cap 10, got %d unknown=%v", n, unk)
|
||
}
|
||
// a window still open → explains its cap
|
||
ins("e", "2026-10-01 03:00:00", "", 69, nil, 10)
|
||
if n, unk := sum("e", from, to); n != 10 || unk {
|
||
t.Fatalf("open window: want its cap 10, got %d unknown=%v", n, unk)
|
||
}
|
||
// a window stuck open past its deadline before the interval explains nothing
|
||
ins("g", "2026-09-01 03:00:00", "", 69, nil, 10)
|
||
if _, err := s.db.Exec(`UPDATE offsite_windows SET closes_by = '2026-09-01 04:00:00' WHERE customer_id = 'g'`); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if n, unk := sum("g", from, to); n != 0 || unk {
|
||
t.Fatalf("stale open window: want 0, got %d unknown=%v", n, unk)
|
||
}
|
||
// a window with no cap → unknown (the caller falls back to the half-rule)
|
||
ins("f", "2026-10-01 03:00:00", "2026-10-01 03:10:00", 69, 60, nil)
|
||
if _, unk := sum("f", from, to); !unk {
|
||
t.Fatal("a window with no cap must make the interval unknown")
|
||
}
|
||
}
|