Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

146 lines
3.8 KiB
Go

package persistence
import (
"path/filepath"
"testing"
"time"
)
func openTestSidecar(t *testing.T) (*SidecarStore, string) {
t.Helper()
path := filepath.Join(t.TempDir(), "sidecar.sqlite")
sc, err := OpenSidecar(path)
if err != nil {
t.Fatal(err)
}
return sc, path
}
// The durability contract behind the savings ledger: an observation
// survives a full close + reopen of the database — no flush step exists
// to forget.
func TestSavings_DurableAcrossReopen(t *testing.T) {
sc, path := openTestSidecar(t)
ev := SavingsEvent{
TS: time.Date(2026, 6, 1, 10, 0, 0, 0, time.UTC),
SessionID: "sess-1",
Tool: "get_symbol_source",
Repo: "repo-a",
Language: "go",
Returned: 23,
Saved: 77,
}
if err := sc.AddSavingsObservation(ev); err != nil {
t.Fatal(err)
}
if err := sc.Close(); err != nil {
t.Fatal(err)
}
sc2, err := OpenSidecar(path)
if err != nil {
t.Fatal(err)
}
defer sc2.Close()
buckets, firstSeen, lastUpdated, err := sc2.SavingsTotals()
if err != nil {
t.Fatal(err)
}
top := buckets[""]
if top.Calls != 1 || top.Saved != 77 || top.Returned != 23 {
t.Errorf("top-line bucket = %+v, want calls=1 saved=77 returned=23", top)
}
if r := buckets["repo:repo-a"]; r.Calls != 1 {
t.Errorf("repo bucket = %+v, want calls=1", r)
}
if l := buckets["lang:go"]; l.Calls != 1 {
t.Errorf("lang bucket = %+v, want calls=1", l)
}
if !firstSeen.Equal(ev.TS) || !lastUpdated.Equal(ev.TS) {
t.Errorf("meta stamps = (%v, %v), want both %v", firstSeen, lastUpdated, ev.TS)
}
evs, err := sc2.SavingsEventsSince(time.Time{})
if err != nil {
t.Fatal(err)
}
if len(evs) != 1 || evs[0].SessionID != "sess-1" || !evs[0].TS.Equal(ev.TS) {
t.Errorf("reloaded events = %+v", evs)
}
}
func TestSavings_MetaStampsMinMax(t *testing.T) {
sc, _ := openTestSidecar(t)
defer sc.Close()
t1 := time.Date(2026, 6, 1, 10, 0, 0, 0, time.UTC)
t2 := t1.Add(time.Hour)
if err := sc.AddSavingsObservation(SavingsEvent{TS: t1, Tool: "a"}); err != nil {
t.Fatal(err)
}
if err := sc.AddSavingsObservation(SavingsEvent{TS: t2, Tool: "b"}); err != nil {
t.Fatal(err)
}
_, firstSeen, lastUpdated, err := sc.SavingsTotals()
if err != nil {
t.Fatal(err)
}
if !firstSeen.Equal(t1) {
t.Errorf("first_seen = %v, want %v (first observation wins)", firstSeen, t1)
}
if !lastUpdated.Equal(t2) {
t.Errorf("last_updated = %v, want %v (latest observation wins)", lastUpdated, t2)
}
}
func TestSavings_ResetClearsButKeepsImportMark(t *testing.T) {
sc, _ := openTestSidecar(t)
defer sc.Close()
if err := sc.ImportLegacySavings(
map[string]SavingsTotalsRow{"": {Saved: 100, Returned: 10, Calls: 1}},
time.Now().UTC(), time.Now().UTC(), nil,
); err != nil {
t.Fatal(err)
}
if !sc.SavingsLegacyImportDone() {
t.Fatal("import mark must be set after ImportLegacySavings")
}
if err := sc.ResetSavings(); err != nil {
t.Fatal(err)
}
buckets, firstSeen, _, err := sc.SavingsTotals()
if err != nil {
t.Fatal(err)
}
if len(buckets) != 0 || !firstSeen.IsZero() {
t.Errorf("reset must clear totals + meta, got buckets=%v firstSeen=%v", buckets, firstSeen)
}
if !sc.SavingsLegacyImportDone() {
t.Error("reset must NOT clear the legacy-import mark (renamed files would re-import)")
}
}
func TestSavings_ImportIsIdempotent(t *testing.T) {
sc, _ := openTestSidecar(t)
defer sc.Close()
rows := map[string]SavingsTotalsRow{"": {Saved: 100, Returned: 10, Calls: 1}}
if err := sc.ImportLegacySavings(rows, time.Time{}, time.Time{}, nil); err != nil {
t.Fatal(err)
}
if err := sc.ImportLegacySavings(rows, time.Time{}, time.Time{}, nil); err != nil {
t.Fatal(err)
}
buckets, _, _, err := sc.SavingsTotals()
if err != nil {
t.Fatal(err)
}
if got := buckets[""].Calls; got != 1 {
t.Errorf("calls after double import = %d, want 1", got)
}
}