- Add unbounded CLI sessions agent-task with plan and code review logs - Add edge console with tests - Add node run manager for session lifecycle management - Add router and store tests - Update transport session handling - Add runtime proto definitions - Update configurations and packages
181 lines
3.9 KiB
Go
181 lines
3.9 KiB
Go
package store_test
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
_ "modernc.org/sqlite"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"iop/apps/node/internal/store"
|
|
)
|
|
|
|
const oldRunsSchema = `
|
|
CREATE TABLE runs (
|
|
run_id TEXT PRIMARY KEY,
|
|
adapter TEXT NOT NULL,
|
|
model TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'running',
|
|
created_at DATETIME NOT NULL,
|
|
completed_at DATETIME,
|
|
error TEXT
|
|
);
|
|
`
|
|
|
|
func seedOldSchemaDB(t *testing.T) string {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
dsn := filepath.Join(dir, "old.db")
|
|
db, err := sql.Open("sqlite", dsn)
|
|
if err != nil {
|
|
t.Fatalf("open: %v", err)
|
|
}
|
|
if _, err := db.Exec(oldRunsSchema); err != nil {
|
|
t.Fatalf("seed schema: %v", err)
|
|
}
|
|
if err := db.Close(); err != nil {
|
|
t.Fatalf("close: %v", err)
|
|
}
|
|
return dsn
|
|
}
|
|
|
|
func newTestStore(t *testing.T) *store.Store {
|
|
t.Helper()
|
|
s, err := store.New(":memory:", zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("store.New: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = s.Close() })
|
|
return s
|
|
}
|
|
|
|
func TestStore_RunSessionFields(t *testing.T) {
|
|
s := newTestStore(t)
|
|
ctx := context.Background()
|
|
|
|
r := store.RunRecord{
|
|
RunID: "run-1",
|
|
Adapter: "cli",
|
|
Model: "codex",
|
|
SessionID: "session-a",
|
|
Background: true,
|
|
Status: "running",
|
|
CreatedAt: time.Now(),
|
|
}
|
|
if err := s.InsertRun(ctx, r); err != nil {
|
|
t.Fatalf("InsertRun: %v", err)
|
|
}
|
|
|
|
got, err := s.GetRun(ctx, "run-1")
|
|
if err != nil {
|
|
t.Fatalf("GetRun: %v", err)
|
|
}
|
|
if got == nil {
|
|
t.Fatal("expected run record")
|
|
}
|
|
if got.SessionID != "session-a" {
|
|
t.Errorf("SessionID: got %q want %q", got.SessionID, "session-a")
|
|
}
|
|
if !got.Background {
|
|
t.Error("Background: expected true")
|
|
}
|
|
if got.Status != "running" {
|
|
t.Errorf("Status: got %q want %q", got.Status, "running")
|
|
}
|
|
|
|
if err := s.CompleteRun(ctx, "run-1", "cancelled", ""); err != nil {
|
|
t.Fatalf("CompleteRun: %v", err)
|
|
}
|
|
got, err = s.GetRun(ctx, "run-1")
|
|
if err != nil {
|
|
t.Fatalf("GetRun after complete: %v", err)
|
|
}
|
|
if got.Status != "cancelled" {
|
|
t.Errorf("Status after cancel: got %q want %q", got.Status, "cancelled")
|
|
}
|
|
}
|
|
|
|
func TestStore_DefaultSessionID(t *testing.T) {
|
|
s := newTestStore(t)
|
|
ctx := context.Background()
|
|
|
|
r := store.RunRecord{
|
|
RunID: "run-2",
|
|
Adapter: "cli",
|
|
Model: "codex",
|
|
SessionID: "", // empty → should default to "default"
|
|
Status: "running",
|
|
CreatedAt: time.Now(),
|
|
}
|
|
if err := s.InsertRun(ctx, r); err != nil {
|
|
t.Fatalf("InsertRun: %v", err)
|
|
}
|
|
|
|
got, err := s.GetRun(ctx, "run-2")
|
|
if err != nil {
|
|
t.Fatalf("GetRun: %v", err)
|
|
}
|
|
if got.SessionID != "default" {
|
|
t.Errorf("SessionID: got %q want %q", got.SessionID, "default")
|
|
}
|
|
}
|
|
|
|
func TestStore_MigratesExistingRunsTable(t *testing.T) {
|
|
dsn := seedOldSchemaDB(t)
|
|
|
|
s, err := store.New(dsn, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("store.New: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = s.Close() })
|
|
|
|
ctx := context.Background()
|
|
r := store.RunRecord{
|
|
RunID: "run-mig",
|
|
Adapter: "cli",
|
|
Model: "codex",
|
|
SessionID: "session-a",
|
|
Background: true,
|
|
Status: "running",
|
|
CreatedAt: time.Now(),
|
|
}
|
|
if err := s.InsertRun(ctx, r); err != nil {
|
|
t.Fatalf("InsertRun: %v", err)
|
|
}
|
|
|
|
got, err := s.GetRun(ctx, "run-mig")
|
|
if err != nil {
|
|
t.Fatalf("GetRun: %v", err)
|
|
}
|
|
if got == nil {
|
|
t.Fatal("expected run record")
|
|
}
|
|
if got.SessionID != "session-a" {
|
|
t.Errorf("SessionID: got %q want %q", got.SessionID, "session-a")
|
|
}
|
|
if !got.Background {
|
|
t.Error("Background: expected true")
|
|
}
|
|
}
|
|
|
|
func TestStore_MigrationIsIdempotent(t *testing.T) {
|
|
dsn := seedOldSchemaDB(t)
|
|
|
|
s1, err := store.New(dsn, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("store.New first: %v", err)
|
|
}
|
|
if err := s1.Close(); err != nil {
|
|
t.Fatalf("close first: %v", err)
|
|
}
|
|
|
|
s2, err := store.New(dsn, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("store.New second: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = s2.Close() })
|
|
}
|