Files
executus/contrib/store/audit_store_test.go
steve c8559676ed
executus CI / test (push) Has been cancelled
P4b: skill noun + contrib/store (SQLite for budget/persona/skill/audit)
Merges the skill half of the persona/skill pair plus the second nested module.
(Squashed onto main from phase-4b-skill; the audit/budget/persona batteries it
was stacked on already landed via the P4 merge.)

- skill/: clean-redesign Skill noun + LEAN SkillStore (lifecycle/versions/
  schedule only) + ToRunnable + Memory default.
- contrib/store/: separate go.mod carrying modernc.org/sqlite, so the driver
  never enters the core go.sum. db.Budget()/Personas()/Skills()/Audit() back
  all four store seams (JSON-blob + indexed columns; round-trip tested).
  Includes the verified gadfly #5 fixes (AppendVersion tx+UNIQUE+error,
  Mark*ScheduledRun atomic json_set, busy_timeout, NaN guard).
- CI: builds + tests the nested module and asserts it owns the sqlite driver.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 00:15:00 -04:00

68 lines
2.2 KiB
Go

package store
import (
"context"
"testing"
"time"
"gitea.stevedudenhoeffer.com/steve/executus/audit"
)
func TestSQLiteAuditStore(t *testing.T) {
ctx := context.Background()
db, err := Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
st := db.Audit()
if err := st.Initialize(ctx); err != nil {
t.Fatal(err)
}
now := time.Now().UTC()
// parent run
if err := st.StartRun(ctx, audit.SkillRun{ID: "r1", SkillID: "agent-x", CallerID: "c1",
Inputs: map[string]any{"q": "hi"}, StartedAt: now}); err != nil {
t.Fatal(err)
}
// child run
st.StartRun(ctx, audit.SkillRun{ID: "r2", SkillID: "skill-y", CallerID: "c1", ParentRunID: "r1", StartedAt: now.Add(time.Second)})
st.AppendLog(ctx, audit.SkillRunLog{RunID: "r1", Sequence: 1, EventType: "step", Payload: map[string]any{"i": 1}, CreatedAt: now})
if err := st.FinishRun(ctx, "r1", audit.RunStats{Status: "ok", Output: "done", ToolCalls: 2, InputTokens: 10, OutputTokens: 5}); err != nil {
t.Fatal(err)
}
got, err := st.GetRun(ctx, "r1")
if err != nil || got.Status != "ok" || got.Output != "done" || got.FinishedAt == nil ||
got.Inputs["q"] != "hi" || got.TotalInputTokens != 10 {
t.Fatalf("GetRun: %v %+v", err, got)
}
if logs, _ := st.ListLogsByRun(ctx, "r1"); len(logs) != 1 || logs[0].EventType != "step" {
t.Errorf("ListLogsByRun = %+v", logs)
}
if kids, _ := st.ListChildrenByParent(ctx, "r1"); len(kids) != 1 || kids[0].ID != "r2" {
t.Errorf("ListChildrenByParent = %+v", kids)
}
if chain, _ := st.WalkParentChain(ctx, "r2"); len(chain) != 2 || chain[1].ID != "r1" {
t.Errorf("WalkParentChain = %+v", chain)
}
if byCaller, _ := st.ListRunsByCaller(ctx, "c1", 10); len(byCaller) != 2 {
t.Errorf("ListRunsByCaller = %d, want 2", len(byCaller))
}
// filter: top-level only
tl, _ := st.ListRunsFiltered(ctx, audit.RunFilter{TopLevelOnly: true}, 0, 10)
if len(tl) != 1 || tl[0].ID != "r1" {
t.Errorf("TopLevelOnly filter = %+v", tl)
}
// last-run map
last, _ := st.LastRunBySkills(ctx, []string{"agent-x", "skill-y"}, true)
if _, ok := last["agent-x"]; !ok {
t.Errorf("LastRunBySkills missing agent-x: %+v", last)
}
if n, _ := st.CountRunsBySkill(ctx, "agent-x", false); n != 1 {
t.Errorf("CountRunsBySkill = %d, want 1", n)
}
}