mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 08:57:57 +00:00
test(gateway): anchor audit-replay fixture to hour buckets
WindowTokens buckets are whole unix hours, so a fixture built from raw offsets like "1 minute ago" landed in the previous hour bucket whenever the suite ran in the first minute of an hour — the 1h-window assertion then saw 0 tokens and failed. Anchor rows to hour boundaries instead (H-2 / H-1 / current bucket) and assert the exact current-bucket value; the suite is now deterministic for any run time.
This commit is contained in:
@ -92,21 +92,25 @@ func TestAuditRotationRecords(t *testing.T) {
|
|||||||
func TestLoadAuditFullReplay(t *testing.T) {
|
func TestLoadAuditFullReplay(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
path := filepath.Join(dir, "audit.jsonl")
|
path := filepath.Join(dir, "audit.jsonl")
|
||||||
// timestamps relative to now: buckets are whole unix hours, so window
|
// timestamps anchored to whole unix hours (see aggregateLocked): window
|
||||||
// assertions must not depend on the minute-of-hour of the test run.
|
// assertions must hold no matter which minute-of-hour the suite runs at,
|
||||||
|
// so rows are placed relative to hour boundaries, not raw offsets from now.
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
thisHour := now.Truncate(time.Hour)
|
||||||
|
twoBucketsAgo := thisHour.Add(-90 * time.Minute) // bucket H-2
|
||||||
|
prevBucketMid := thisHour.Add(-30 * time.Minute) // bucket H-1
|
||||||
lines := []string{
|
lines := []string{
|
||||||
`{"obj":"access","time":1699999999000,"key":"k","method":"GET","path":"/api/stats","status":200}`,
|
`{"obj":"access","time":1699999999000,"key":"k","method":"GET","path":"/api/stats","status":200}`,
|
||||||
fmt.Sprintf(`{"time":%d,"key":"k","type":"chat","model":"m","source":"s","prompt_tokens":100,"completion_tokens":50,"latency_ms":10,"ok":true,"status":200}`, now.Add(-65*time.Minute).UnixMilli()),
|
fmt.Sprintf(`{"time":%d,"key":"k","type":"chat","model":"m","source":"s","prompt_tokens":100,"completion_tokens":50,"latency_ms":10,"ok":true,"status":200}`, twoBucketsAgo.UnixMilli()),
|
||||||
`{this is not valid json`,
|
`{this is not valid json`,
|
||||||
fmt.Sprintf(`{"time":%d,"key":"k","type":"stream","model":"m","source":"s","prompt_tokens":200,"completion_tokens":20,"latency_ms":20,"ok":false,"status":503}`, now.Add(-30*time.Minute).UnixMilli()),
|
fmt.Sprintf(`{"time":%d,"key":"k","type":"stream","model":"m","source":"s","prompt_tokens":200,"completion_tokens":20,"latency_ms":20,"ok":false,"status":503}`, prevBucketMid.UnixMilli()),
|
||||||
"garbage-not-json\n",
|
"garbage-not-json\n",
|
||||||
fmt.Sprintf(`{"time":%d,"key":"k","type":"chat","model":"m2","source":"s","prompt_tokens":7,"completion_tokens":3,"latency_ms":5,"ok":true,"status":200}`, now.Add(-30*time.Minute).UnixMilli()),
|
fmt.Sprintf(`{"time":%d,"key":"k","type":"chat","model":"m2","source":"s","prompt_tokens":7,"completion_tokens":3,"latency_ms":5,"ok":true,"status":200}`, prevBucketMid.UnixMilli()),
|
||||||
}
|
}
|
||||||
loaded := strings.Join(lines, "\n") + "\n" + strings.Repeat("x", 1<<18) + "\n"
|
loaded := strings.Join(lines, "\n") + "\n" + strings.Repeat("x", 1<<18) + "\n"
|
||||||
// oversized row at the END proves the scanner tolerates >64KB lines and
|
// oversized row at the END proves the scanner tolerates >64KB lines and
|
||||||
// still finishes the replay instead of truncating silently.
|
// still finishes the replay instead of truncating silently.
|
||||||
loaded += fmt.Sprintf(`{"time":%d,"key":"k","type":"chat","model":"m","source":"s","prompt_tokens":1,"completion_tokens":1,"latency_ms":1,"ok":true,"status":200}`, now.Add(-time.Minute).UnixMilli()) + "\n"
|
loaded += fmt.Sprintf(`{"time":%d,"key":"k","type":"chat","model":"m","source":"s","prompt_tokens":1,"completion_tokens":1,"latency_ms":1,"ok":true,"status":200}`, now.UnixMilli()) + "\n"
|
||||||
if err := os.WriteFile(path, []byte(loaded), 0644); err != nil {
|
if err := os.WriteFile(path, []byte(loaded), 0644); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -126,9 +130,10 @@ func TestLoadAuditFullReplay(t *testing.T) {
|
|||||||
t.Fatalf("ring must hold only real requests, got %d rows: %#v", len(s.recs), s.recs)
|
t.Fatalf("ring must hold only real requests, got %d rows: %#v", len(s.recs), s.recs)
|
||||||
}
|
}
|
||||||
// quota window rebuilt from full history. All-time and multi-hour windows
|
// quota window rebuilt from full history. All-time and multi-hour windows
|
||||||
// must see everything; a 1h window must NOT return all records (the old
|
// must see everything. A 1h window must NOT return all records (the old
|
||||||
// ms/seconds unit bug made any sec>0 window return everything), and must
|
// ms/seconds unit bug made any sec>0 window return everything): buckets
|
||||||
// always include the row written one minute ago (current hour bucket).
|
// are whole unix hours, so only rows in the current hour bucket qualify
|
||||||
|
// — exactly the just-now row (2 tokens).
|
||||||
if w := s.WindowTokens("m", "s", 0); w != 372 {
|
if w := s.WindowTokens("m", "s", 0); w != 372 {
|
||||||
t.Fatalf("all-time window want 372, got %d", w)
|
t.Fatalf("all-time window want 372, got %d", w)
|
||||||
}
|
}
|
||||||
@ -138,8 +143,8 @@ func TestLoadAuditFullReplay(t *testing.T) {
|
|||||||
if w := s.WindowTokens("m", "s", 24*hourSec); w != 372 {
|
if w := s.WindowTokens("m", "s", 24*hourSec); w != 372 {
|
||||||
t.Fatalf("24h window want 372, got %d", w)
|
t.Fatalf("24h window want 372, got %d", w)
|
||||||
}
|
}
|
||||||
if w := s.WindowTokens("m", "s", hourSec); w < 2 || w >= 372 {
|
if w := s.WindowTokens("m", "s", hourSec); w != 2 {
|
||||||
t.Fatalf("1h window want [2,372), got %d", w)
|
t.Fatalf("1h window want 2 (current-bucket row only), got %d", w)
|
||||||
}
|
}
|
||||||
if w := s.WindowTokens("m2", "s", 24*hourSec); w != 10 {
|
if w := s.WindowTokens("m2", "s", 24*hourSec); w != 10 {
|
||||||
t.Fatalf("m2 24h window want 10, got %d", w)
|
t.Fatalf("m2 24h window want 10, got %d", w)
|
||||||
|
|||||||
Reference in New Issue
Block a user