98 lines
3.1 KiB
Go
98 lines
3.1 KiB
Go
package runtime
|
|
|
|
import (
|
|
"database/sql"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
func TestStoreMissingSQLiteReturnsEmptySnapshot(t *testing.T) {
|
|
snapshot, err := Store{CodexHome: t.TempDir()}.Snapshot()
|
|
if err != nil {
|
|
t.Fatalf("Snapshot returned error: %v", err)
|
|
}
|
|
if len(snapshot.Threads) != 0 || len(snapshot.SpawnEdges) != 0 || len(snapshot.Goals) != 0 {
|
|
t.Fatalf("expected empty snapshot, got %#v", snapshot)
|
|
}
|
|
if snapshot.Source.Confidence != "low" || snapshot.Source.Kind != "sqlite_missing" {
|
|
t.Fatalf("unexpected source evidence: %#v", snapshot.Source)
|
|
}
|
|
}
|
|
|
|
func TestStoreReadsThreadsEdgesAndGoalsFromReadonlySQLite(t *testing.T) {
|
|
root := t.TempDir()
|
|
createRuntimeSQLite(t, root)
|
|
|
|
snapshot, err := Store{CodexHome: root}.Snapshot()
|
|
if err != nil {
|
|
t.Fatalf("Snapshot returned error: %v", err)
|
|
}
|
|
if len(snapshot.Threads) != 2 {
|
|
t.Fatalf("threads = %#v", snapshot.Threads)
|
|
}
|
|
if snapshot.Threads[0].ID != "thread-a" || snapshot.Threads[0].Role != "analyst" {
|
|
t.Fatalf("unexpected first thread: %#v", snapshot.Threads[0])
|
|
}
|
|
if len(snapshot.SpawnEdges) != 1 || snapshot.SpawnEdges[0].FromThreadID != "thread-a" || snapshot.SpawnEdges[0].ToThreadID != "thread-b" || snapshot.SpawnEdges[0].Reason != "handoff" {
|
|
t.Fatalf("unexpected edges: %#v", snapshot.SpawnEdges)
|
|
}
|
|
if len(snapshot.Goals) != 1 || snapshot.Goals[0].ThreadID != "thread-b" || snapshot.Goals[0].Status != "in_progress" {
|
|
t.Fatalf("unexpected goals: %#v", snapshot.Goals)
|
|
}
|
|
if snapshot.Source.Confidence != "high" || snapshot.Source.Kind != "sqlite_readonly" {
|
|
t.Fatalf("unexpected source evidence: %#v", snapshot.Source)
|
|
}
|
|
}
|
|
|
|
func createRuntimeSQLite(t *testing.T, root string) {
|
|
t.Helper()
|
|
statePath := filepath.Join(root, "state_5.sqlite")
|
|
goalsPath := filepath.Join(root, "goals_1.sqlite")
|
|
stateDB, err := sql.Open("sqlite", statePath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer stateDB.Close()
|
|
execSQL(t, stateDB, `CREATE TABLE threads (
|
|
id TEXT PRIMARY KEY,
|
|
role TEXT,
|
|
status TEXT,
|
|
created_at TEXT,
|
|
updated_at TEXT
|
|
)`)
|
|
execSQL(t, stateDB, `CREATE TABLE thread_spawn_edges (
|
|
from_thread_id TEXT,
|
|
to_thread_id TEXT,
|
|
reason TEXT,
|
|
created_at TEXT
|
|
)`)
|
|
execSQL(t, stateDB, `INSERT INTO threads (id, role, status, created_at, updated_at) VALUES
|
|
('thread-a', 'analyst', 'done', '2026-05-25T01:00:00Z', '2026-05-25T01:05:00Z'),
|
|
('thread-b', 'operator', 'running', '2026-05-25T01:06:00Z', '2026-05-25T01:07:00Z')`)
|
|
execSQL(t, stateDB, `INSERT INTO thread_spawn_edges (from_thread_id, to_thread_id, reason, created_at) VALUES
|
|
('thread-a', 'thread-b', 'handoff', '2026-05-25T01:06:00Z')`)
|
|
|
|
goalsDB, err := sql.Open("sqlite", goalsPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer goalsDB.Close()
|
|
execSQL(t, goalsDB, `CREATE TABLE thread_goals (
|
|
thread_id TEXT,
|
|
goal TEXT,
|
|
status TEXT,
|
|
updated_at TEXT
|
|
)`)
|
|
execSQL(t, goalsDB, `INSERT INTO thread_goals (thread_id, goal, status, updated_at) VALUES
|
|
('thread-b', 'ship phase 3', 'in_progress', '2026-05-25T01:08:00Z')`)
|
|
}
|
|
|
|
func execSQL(t *testing.T, db *sql.DB, query string) {
|
|
t.Helper()
|
|
if _, err := db.Exec(query); err != nil {
|
|
t.Fatalf("exec %q: %v", query, err)
|
|
}
|
|
}
|