package storage import ( "database/sql" "errors" "testing" ) func TestCreateRoleConfig_AssignsIncrementingVersions(t *testing.T) { db := testDB(t) v1, err := db.CreateRoleConfig("coder", `{"role":"coder"}`, "human") if err != nil { t.Fatalf("CreateRoleConfig v1: %v", err) } if v1.Version != 1 { t.Errorf("expected version 1, got %d", v1.Version) } if v1.Status != "draft" { t.Errorf("expected status draft, got %q", v1.Status) } v2, err := db.CreateRoleConfig("coder", `{"role":"coder","system_prompt":"v2"}`, "human") if err != nil { t.Fatalf("CreateRoleConfig v2: %v", err) } if v2.Version != 2 { t.Errorf("expected version 2, got %d", v2.Version) } // A different role starts its own version sequence at 1. other, err := db.CreateRoleConfig("reviewer", `{"role":"reviewer"}`, "human") if err != nil { t.Fatalf("CreateRoleConfig other role: %v", err) } if other.Version != 1 { t.Errorf("expected version 1 for a new role, got %d", other.Version) } } func TestGetActiveRoleConfig_NoneActive_ReturnsErrNoRows(t *testing.T) { db := testDB(t) if _, err := db.CreateRoleConfig("coder", `{"role":"coder"}`, "human"); err != nil { t.Fatalf("CreateRoleConfig: %v", err) } _, err := db.GetActiveRoleConfig("coder") if !errors.Is(err, sql.ErrNoRows) { t.Fatalf("expected sql.ErrNoRows, got %v", err) } } func TestListRoleConfigVersions_OrderedByVersion(t *testing.T) { db := testDB(t) for i := 0; i < 3; i++ { if _, err := db.CreateRoleConfig("coder", `{"role":"coder"}`, "human"); err != nil { t.Fatalf("CreateRoleConfig: %v", err) } } versions, err := db.ListRoleConfigVersions("coder") if err != nil { t.Fatalf("ListRoleConfigVersions: %v", err) } if len(versions) != 3 { t.Fatalf("expected 3 versions, got %d", len(versions)) } for i, v := range versions { if v.Version != i+1 { t.Errorf("versions[%d].Version = %d, want %d", i, v.Version, i+1) } } } // TestActivateRoleConfigVersion_ExactlyOneActive is the invariant test called // out in the phase spec: activating version 2 while version 1 is active must // atomically flip version 1 to retired. func TestActivateRoleConfigVersion_ExactlyOneActive(t *testing.T) { db := testDB(t) v1, err := db.CreateRoleConfig("coder", `{"role":"coder","system_prompt":"v1"}`, "human") if err != nil { t.Fatalf("CreateRoleConfig v1: %v", err) } v2, err := db.CreateRoleConfig("coder", `{"role":"coder","system_prompt":"v2"}`, "human") if err != nil { t.Fatalf("CreateRoleConfig v2: %v", err) } if err := db.ActivateRoleConfigVersion("coder", v1.Version); err != nil { t.Fatalf("activate v1: %v", err) } active, err := db.GetActiveRoleConfig("coder") if err != nil { t.Fatalf("GetActiveRoleConfig: %v", err) } if active.Version != v1.Version { t.Fatalf("expected active version %d, got %d", v1.Version, active.Version) } if active.ActivatedAt == nil { t.Errorf("expected ActivatedAt to be set") } // Now activate v2 — v1 must atomically flip to retired, and v2 becomes // the sole active row. if err := db.ActivateRoleConfigVersion("coder", v2.Version); err != nil { t.Fatalf("activate v2: %v", err) } active, err = db.GetActiveRoleConfig("coder") if err != nil { t.Fatalf("GetActiveRoleConfig after v2 activation: %v", err) } if active.Version != v2.Version { t.Fatalf("expected active version %d, got %d", v2.Version, active.Version) } all, err := db.ListRoleConfigVersions("coder") if err != nil { t.Fatalf("ListRoleConfigVersions: %v", err) } activeCount := 0 for _, row := range all { if row.Status == "active" { activeCount++ } if row.Version == v1.Version { if row.Status != "retired" { t.Errorf("expected v1 to be retired, got %q", row.Status) } if row.RetiredAt == nil { t.Errorf("expected v1 RetiredAt to be set") } } } if activeCount != 1 { t.Fatalf("expected exactly 1 active row, got %d", activeCount) } } func TestActivateRoleConfigVersion_UnknownVersion_Errors(t *testing.T) { db := testDB(t) if _, err := db.CreateRoleConfig("coder", `{"role":"coder"}`, "human"); err != nil { t.Fatalf("CreateRoleConfig: %v", err) } if err := db.ActivateRoleConfigVersion("coder", 99); err == nil { t.Fatalf("expected error activating unknown version") } // No row should have been left active. if _, err := db.GetActiveRoleConfig("coder"); !errors.Is(err, sql.ErrNoRows) { t.Fatalf("expected sql.ErrNoRows, got %v", err) } }