Files
magpie/internal/usage/cache_bounds_test.go
T

156 lines
4.7 KiB
Go

package usage
import (
"bytes"
"fmt"
"os"
"testing"
"time"
)
func TestUsageSameSizeAndMtimeRewrite(t *testing.T) {
for _, appendAfter := range []bool{false, true} {
t.Run(fmt.Sprint("append=", appendAfter), func(t *testing.T) {
pageHome(t)
historyLog(t, 1034)
Summarize(All)
QueryPage(All, Filter{}, 0, 100)
before, err := os.Stat(Path())
if err != nil {
t.Fatal(err)
}
data, err := os.ReadFile(Path())
if err != nil {
t.Fatal(err)
}
edited := bytes.Replace(data, []byte(`"in":10`), []byte(`"in":73`), 1)
if bytes.Equal(edited, data) || len(edited) != len(data) {
t.Fatal("fixture must change at the same size")
}
if err := os.WriteFile(Path(), edited, 0600); err != nil {
t.Fatal(err)
}
if err := os.Chtimes(Path(), before.ModTime(), before.ModTime()); err != nil {
t.Fatal(err)
}
if appendAfter {
Append(Record{Time: time.Now(), Agent: "codex", Provider: "relay", Model: "m", Input: 19})
}
got, want := Summarize(All), summarize(All, time.Now(), Load(time.Time{}))
if got.Totals != want.Totals {
t.Fatalf("same-stat summary stale: got %+v want %+v", got.Totals, want.Totals)
}
equalPage(t, QueryPage(All, Filter{}, 0, 100), pageFromLedger(All, Filter{}, 0, 100, LedgerOf(All, Filter{})))
})
}
}
func cacheBudget(t *testing.T, n int64) {
t.Helper()
old := requestCacheBytes
requestCacheBytes = n
t.Cleanup(func() { requestCacheBytes = old })
}
func TestUsageRawCacheBudget(t *testing.T) {
pageHome(t)
cacheBudget(t, 128)
historyLog(t, 1034)
assertFresh := func() {
t.Helper()
cold := Load(time.Time{})
for _, p := range []Period{Today, Week, Month, All} {
got, want := Summarize(p), summarize(p, time.Now(), cold)
if got.Totals != want.Totals {
t.Fatalf("uncached %s summary stale: %+v / %+v", p, got.Totals, want.Totals)
}
equalPage(t, QueryPage(p, Filter{}, 0, 100), pageFromLedger(p, Filter{}, 0, 100, LedgerOf(p, Filter{})))
}
logIndex.Lock()
retained := len(logIndex.snapshot.blocks)
logIndex.Unlock()
if retained != 0 {
t.Fatalf("retained %d over-budget blocks", retained)
}
requestCache.Lock()
chunks, pages := len(requestCache.chunks)+len(requestCache.gateways), len(requestCache.pages)
requestCache.Unlock()
if chunks != 0 || pages != 0 {
t.Fatalf("uncached raw history retained chunks=%d pages=%d", chunks, pages)
}
}
assertFresh()
Append(Record{Time: time.Now(), Agent: "codex", Provider: "relay", Model: "m", Input: 11})
assertFresh()
historyLog(t, 20) // rewrite/truncate after the oversized metadata-only index
assertFresh()
}
func TestUsageRawAndPricedChunksShareBudget(t *testing.T) {
pageHome(t)
historyLog(t, 1034)
snapshot := readLogSnapshot()
cacheBudget(t, snapshot.bytes+1)
equalPage(t, QueryPage(All, Filter{}, 0, 100), pageFromLedger(All, Filter{}, 0, 100, LedgerOf(All, Filter{})))
requestCache.Lock()
defer requestCache.Unlock()
retained := snapshot.bytes
for _, c := range requestCache.gateways {
retained += c.Bytes
}
for _, c := range requestCache.chunks {
retained += c.Bytes
}
if retained > requestCacheBytes {
t.Fatalf("raw + priced bytes %d exceed %d", retained, requestCacheBytes)
}
}
func TestUsageLargeSummaryIsCompleteButNotCached(t *testing.T) {
pageHome(t)
for i := 0; i < summaryCacheSessions+1; i++ {
Append(Record{Time: time.Now(), Agent: "codex", Provider: "relay", Model: "m", Session: fmt.Sprint(i), Input: 1})
}
got := Summarize(All)
if len(got.Sessions) != summaryCacheSessions+1 || got.Calls != summaryCacheSessions+1 {
t.Fatalf("large summary lost sessions: %d / %d", len(got.Sessions), got.Calls)
}
summaries.Lock()
_, kept := summaries.entries[All]
summaries.Unlock()
if kept {
t.Fatal("oversized Sessions retained in summary cache")
}
}
// Rebuilding an uncached snapshot with a larger budget keeps its version and
// page key. The preceding query must leave an initialized page map to write.
func TestUsagePageCacheAfterUncachedQuery(t *testing.T) {
pageHome(t)
historyLog(t, 1034)
normalBudget := requestCacheBytes
cacheBudget(t, 128)
QueryPage(All, Filter{}, 0, 100)
logIndex.Lock()
uncached := logIndex.snapshot.uncached
logIndex.Unlock()
if !uncached {
t.Fatal("fixture must exceed the tiny budget")
}
requestCache.Lock()
key := requestCache.key
requestCache.Unlock()
requestCacheBytes = normalBudget
got := QueryPage(All, Filter{}, 0, 100)
equalPage(t, got, pageFromLedger(All, Filter{}, 0, 100, LedgerOf(All, Filter{})))
requestCache.Lock()
defer requestCache.Unlock()
if requestCache.key != key {
t.Fatal("page key changed instead of exercising the same cache")
}
if _, ok := requestCache.pages[pageKey{All, Filter{}, 0, 100}]; !ok {
t.Fatal("page not cached after rebuilding within budget")
}
}