Files
Agent-System-Backend/internal/store/context_manifest_test.go
T
weichen 06367422d6 Integrate context candidate with current Backend main
Why: Validate W6 against account-audit migration v31 on the current authoritative base.

Evidence: Focused store/runmgr/migrator and full Go tests passed in the isolated probe after resolving migration collisions.

Impact: Renumbers unpublished W6 migrations to v32-v36; this probe commit is not a shipping branch or live migration.

Refs: feature/rdm-two-trees-latest-templates W6 integration probe
2026-10-14 06:26:36 +00:00

277 lines
10 KiB
Go

package store
import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
"agent-system-backend/pkg/extension"
)
func TestContextManifestTerminalRequiresMatchingAttempt(t *testing.T) {
st, err := OpenSQLite(filepath.Join(t.TempDir(), "receipt.db"))
if err != nil {
t.Fatal(err)
}
defer st.Close()
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "attempt-1", InputSHA256: "input-hash", Delivery: "responded"}
if err := st.AppendContextManifest("alice", "run-1", m); err == nil {
t.Fatal("terminal receipt without attempted send")
}
m.Delivery = "attempted"
if err := st.AppendContextManifest("alice", "run-1", m); err != nil {
t.Fatal(err)
}
if err := st.AppendContextManifest("alice", "run-1", m); err == nil {
t.Fatal("duplicate attempted receipt")
}
m.Delivery = "responded"
if err := st.AppendContextManifest("alice", "run-2", m); err == nil {
t.Fatal("attempt reused across target")
}
if err := st.AppendContextManifest("alice", "run-1", m); err != nil {
t.Fatal(err)
}
m.Delivery = "unknown"
if err := st.AppendContextManifest("alice", "run-1", m); err == nil {
t.Fatal("one attempt acquired two terminal states")
}
}
func TestContextManifestDoesNotPersistWorkflowOutputInResolveScope(t *testing.T) {
st, err := OpenSQLite(filepath.Join(t.TempDir(), "scope-privacy.db"))
if err != nil {
t.Fatal(err)
}
defer st.Close()
const restricted = "OLD_RESTRICTED_BODY"
m := extension.ProjectContextManifest{ProjectID: "project-1", AttemptID: "judge-attempt", InputSHA256: "input-hash", Delivery: "attempted"}
scope := ContextManifestScope{Bound: true}
if err := st.AppendContextManifestWithScope("alice", "run-1", m, scope); err != nil {
t.Fatal(err)
}
m.Delivery = "responded"
if err := st.AppendContextManifestWithScope("alice", "run-1", m, scope); err != nil {
t.Fatal(err)
}
rows, err := st.db.Query(st.q(`SELECT delivery,manifest_json FROM context_manifests WHERE attempt_id=? ORDER BY created_at,id`), m.AttemptID)
if err != nil {
t.Fatal(err)
}
defer rows.Close()
seen := 0
for rows.Next() {
var delivery, manifest string
if err := rows.Scan(&delivery, &manifest); err != nil {
t.Fatal(err)
}
seen++
if strings.Contains(manifest, restricted) {
t.Errorf("%s trusted receipt persisted workflow output body", delivery)
}
}
if err := rows.Err(); err != nil {
t.Fatal(err)
}
if seen != 2 {
t.Fatalf("expected attempted and responded receipts, got %d", seen)
}
var rawColumns int
if err := st.db.QueryRow(`SELECT COUNT(*) FROM pragma_table_info('context_manifests') WHERE name IN ('resolve_goal','resolve_target_id')`).Scan(&rawColumns); err != nil || rawColumns != 0 {
t.Fatalf("fresh receipt schema retains raw scope columns: columns=%d err=%v", rawColumns, err)
}
}
func TestCandidateV35RawScopeColumnsAreScrubbedOnUpgrade(t *testing.T) {
path := filepath.Join(t.TempDir(), "candidate-v35.db")
st, err := OpenSQLite(path)
if err != nil {
t.Fatal(err)
}
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "old-model", InputSHA256: "input", Delivery: "attempted"}
if err := st.AppendContextManifestWithScope("alice", "run-1", m, ContextManifestScope{Bound: true}); err != nil {
t.Fatal(err)
}
for _, query := range []string{
`DELETE FROM schema_migrations WHERE version=36`,
`ALTER TABLE context_manifests ADD COLUMN resolve_goal TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE context_manifests ADD COLUMN resolve_target_id TEXT NOT NULL DEFAULT ''`,
`UPDATE context_manifests SET resolve_goal='OLD_RESTRICTED_BODY',resolve_target_id='doc-private'`,
} {
if _, err := st.db.Exec(query); err != nil {
t.Fatal(err)
}
}
if err := st.Close(); err != nil {
t.Fatal(err)
}
upgraded, err := OpenSQLite(path)
if err != nil {
t.Fatal(err)
}
defer upgraded.Close()
var columns int
if err := upgraded.db.QueryRow(`SELECT COUNT(*) FROM pragma_table_info('context_manifests') WHERE name IN ('resolve_goal','resolve_target_id')`).Scan(&columns); err != nil || columns != 0 {
t.Fatalf("candidate raw scope columns survived v36: columns=%d err=%v", columns, err)
}
var count int
if err := upgraded.db.QueryRow(`SELECT COUNT(*) FROM context_manifests WHERE attempt_id='old-model'`).Scan(&count); err != nil || count != 1 {
t.Fatalf("trusted manifest disappeared during scrub: count=%d err=%v", count, err)
}
for _, suffix := range []string{"", "-wal"} {
data, err := os.ReadFile(path + suffix)
if err == nil && bytes.Contains(data, []byte("OLD_RESTRICTED_BODY")) {
t.Fatalf("candidate raw scope bytes remain in SQLite %s after upgrade", suffix)
}
}
}
func TestCandidateV35CommittedDropKeepsPendingScrubAcrossRestart(t *testing.T) {
path := filepath.Join(t.TempDir(), "candidate-crash.db")
st, err := OpenSQLite(path)
if err != nil {
t.Fatal(err)
}
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "crash-model", InputSHA256: "input", Delivery: "attempted"}
if err := st.AppendContextManifestWithScope("alice", "run-1", m, ContextManifestScope{Bound: true}); err != nil {
t.Fatal(err)
}
for _, query := range []string{
`DELETE FROM schema_migrations WHERE version=36`,
`ALTER TABLE context_manifests ADD COLUMN resolve_goal TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE context_manifests ADD COLUMN resolve_target_id TEXT NOT NULL DEFAULT ''`,
`UPDATE context_manifests SET resolve_goal='OLD_RESTRICTED_BODY',resolve_target_id='doc-private'`,
} {
if _, err := st.db.Exec(query); err != nil {
t.Fatal(err)
}
}
// Apply only the transactional DDL and ledger. A process crash at this
// boundary must not make the next open mistake the file for scrubbed.
if err := st.migratePass(); err != nil {
t.Fatal(err)
}
var pending int
if err := st.db.QueryRow(`SELECT pending FROM context_scope_scrub_state WHERE id=1`).Scan(&pending); err != nil || pending != 1 {
t.Fatalf("committed v36 lost physical scrub obligation: pending=%d err=%v", pending, err)
}
if err := st.Close(); err != nil {
t.Fatal(err)
}
restarted, err := OpenSQLite(path)
if err != nil {
t.Fatal(err)
}
defer restarted.Close()
if err := restarted.db.QueryRow(`SELECT pending FROM context_scope_scrub_state WHERE id=1`).Scan(&pending); err != nil || pending != 0 {
t.Fatalf("restart did not complete pending scrub: pending=%d err=%v", pending, err)
}
for _, suffix := range []string{"", "-wal"} {
data, err := os.ReadFile(path + suffix)
if err == nil && bytes.Contains(data, []byte("OLD_RESTRICTED_BODY")) {
t.Fatalf("restart left candidate raw bytes in SQLite %s", suffix)
}
}
}
func TestEarlierV36WithoutMarkerIsConservativelyRescrubbed(t *testing.T) {
path := filepath.Join(t.TempDir(), "old-v36.db")
st, err := OpenSQLite(path)
if err != nil {
t.Fatal(err)
}
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "old-v36-model", InputSHA256: "input", Delivery: "attempted"}
if err := st.AppendContextManifestWithScope("alice", "run-1", m, ContextManifestScope{Bound: true}); err != nil {
t.Fatal(err)
}
for _, query := range []string{
`ALTER TABLE context_manifests ADD COLUMN resolve_goal TEXT NOT NULL DEFAULT ''`,
`UPDATE context_manifests SET resolve_goal='OLD_RESTRICTED_BODY'`,
`UPDATE context_manifests SET resolve_goal=''`,
`ALTER TABLE context_manifests DROP COLUMN resolve_goal`,
`DROP TABLE context_scope_scrub_state`,
} {
if _, err := st.db.Exec(query); err != nil {
t.Fatal(err)
}
}
if err := st.Close(); err != nil {
t.Fatal(err)
}
restarted, err := OpenSQLite(path)
if err != nil {
t.Fatal(err)
}
defer restarted.Close()
var pending int
if err := restarted.db.QueryRow(`SELECT pending FROM context_scope_scrub_state WHERE id=1`).Scan(&pending); err != nil || pending != 0 {
t.Fatalf("earlier v36 without marker was not re-scrubbed: pending=%d err=%v", pending, err)
}
for _, suffix := range []string{"", "-wal"} {
data, err := os.ReadFile(path + suffix)
if err == nil && bytes.Contains(data, []byte("OLD_RESTRICTED_BODY")) {
t.Fatalf("old v36 raw bytes survived re-scrub in SQLite %s", suffix)
}
}
}
func TestCandidateV35PostgresRawScopeScrubAndFreshSchema(t *testing.T) {
st := openTestStore(t)
if st.Dialect() != "postgres" {
t.Skip("isolated PostgreSQL DSN not configured")
}
checkColumns := func(want int) {
t.Helper()
var count int
if err := st.db.QueryRow(`SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=current_schema() AND table_name='context_manifests' AND column_name IN ('resolve_goal','resolve_target_id')`).Scan(&count); err != nil || count != want {
t.Fatalf("raw scope columns=%d want=%d err=%v", count, want, err)
}
}
checkColumns(0)
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "candidate-model", InputSHA256: "input", Delivery: "attempted"}
if err := st.AppendContextManifestWithScope("alice", "run-1", m, ContextManifestScope{Bound: true}); err != nil {
t.Fatal(err)
}
for _, query := range []string{
`DELETE FROM schema_migrations WHERE version=36`,
`ALTER TABLE context_manifests ADD COLUMN resolve_goal TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE context_manifests ADD COLUMN resolve_target_id TEXT NOT NULL DEFAULT ''`,
`UPDATE context_manifests SET resolve_goal='OLD_RESTRICTED_BODY',resolve_target_id='doc-private'`,
} {
if _, err := st.db.Exec(query); err != nil {
t.Fatal(err)
}
}
checkColumns(2)
if err := st.migrate(); err != nil {
t.Fatal(err)
}
checkColumns(0)
var count int
if err := st.db.QueryRow(`SELECT COUNT(*) FROM context_manifests WHERE attempt_id='candidate-model'`).Scan(&count); err != nil || count != 1 {
t.Fatalf("trusted manifest lost during PG scrub: count=%d err=%v", count, err)
}
}
func TestContextManifestTerminalCannotChangeTrustedResolveScope(t *testing.T) {
st, err := OpenSQLite(filepath.Join(t.TempDir(), "scope.db"))
if err != nil {
t.Fatal(err)
}
defer st.Close()
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "model-1", WorkflowStepAttemptID: "engine/draft/1", InputSHA256: "input-hash", Delivery: "attempted"}
scope := ContextManifestScope{Bound: true}
if err := st.AppendContextManifestWithScope("alice", "run-1", m, scope); err != nil {
t.Fatal(err)
}
m.Delivery = "responded"
if err := st.AppendContextManifest("alice", "run-1", m); err == nil {
t.Fatal("terminal receipt dropped its trusted Resolve scope")
}
if err := st.AppendContextManifestWithScope("alice", "run-1", m, scope); err != nil {
t.Fatal(err)
}
}