package postgres import ( "context" "errors" "strings" "testing" "time" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" "knowledge-governance/internal/governance" ) func TestListAndGetReleaseExecutionsReturnsAuditableProjection(t *testing.T) { now := time.Date(2026, 9, 6, 9, 0, 0, 0, time.UTC) row := releaseExecutionRow{id: "rx-safe", batchID: "rb-safe", operation: "publish", state: "completed", target: "production", authorizedBy: "owner", authorizationCode: "production_release_authorized", jobID: "job-safe", manifestSHA: strings.Repeat("a", 64), itemCount: 1, completedCount: 1, createdAt: now, completedAt: &now} db := &releaseExecutionReadDB{countRow: releaseExecutionCountRow{count: 1}, row: row, rows: &releaseExecutionRows{values: []releaseExecutionRow{row}}} store := NewStore(db) page, err := store.ListReleaseExecutions(context.Background(), governance.ReleaseExecutionFilter{Operation: "publish", State: "completed", Limit: 20}) if err != nil || page.Total != 1 || len(page.Items) != 1 { t.Fatalf("page=%#v error=%v", page, err) } db.rows = &releaseItemExecutionRows{values: []releaseItemExecutionRow{{publicationID: "tp-safe", title: "Synthetic", action: "delete", state: "verified", previousRevision: "prod-v0", productionRevision: "absent", completedAt: &now}}} detail, err := store.GetReleaseExecution(context.Background(), "rx-safe") if err != nil || len(detail.Items) != 1 || detail.Items[0].Action != "delete" || detail.Items[0].ProductionRevision != "absent" { t.Fatalf("detail=%#v error=%v", detail, err) } joined := strings.ToLower(strings.Join(db.queries, "\n")) for _, required := range []string{"from release_executions", "operation = $1", "state = $2", "from release_item_executions", "publication_id", "item.action"} { if !strings.Contains(joined, required) { t.Fatalf("release read SQL missing %q", required) } } for _, forbidden := range []string{"knowledge_id", "test_kb_id", "physical_path", "api_key", "secret"} { if strings.Contains(joined, forbidden) { t.Fatalf("release read SQL contains forbidden %q", forbidden) } } } func TestListReleaseExecutionsRejectsInvalidFilter(t *testing.T) { _, err := NewStore(&recordingDB{}).ListReleaseExecutions(context.Background(), governance.ReleaseExecutionFilter{Operation: "deploy", Limit: 20}) if !errors.Is(err, ErrInvalidReleaseExecutionFilter) { t.Fatalf("error=%v", err) } } type releaseExecutionCountRow struct{ count int } func (r releaseExecutionCountRow) Scan(dest ...any) error { *dest[0].(*int) = r.count; return nil } type releaseExecutionRows struct { values []releaseExecutionRow index int } func (r *releaseExecutionRows) Close() {} func (r *releaseExecutionRows) Err() error { return nil } func (r *releaseExecutionRows) CommandTag() pgconn.CommandTag { return pgconn.CommandTag{} } func (r *releaseExecutionRows) FieldDescriptions() []pgconn.FieldDescription { return nil } func (r *releaseExecutionRows) RawValues() [][]byte { return nil } func (r *releaseExecutionRows) Conn() *pgx.Conn { return nil } func (r *releaseExecutionRows) Next() bool { if r.index >= len(r.values) { return false } r.index++ return true } func (r *releaseExecutionRows) Scan(dest ...any) error { return r.values[r.index-1].Scan(dest...) } func (r *releaseExecutionRows) Values() ([]any, error) { return nil, nil } type releaseItemExecutionRow struct { publicationID, title, action, state, previousRevision, productionRevision, errorCode string createdAt time.Time completedAt *time.Time } type releaseItemExecutionRows struct { values []releaseItemExecutionRow index int } func (r *releaseItemExecutionRows) Close() {} func (r *releaseItemExecutionRows) Err() error { return nil } func (r *releaseItemExecutionRows) CommandTag() pgconn.CommandTag { return pgconn.CommandTag{} } func (r *releaseItemExecutionRows) FieldDescriptions() []pgconn.FieldDescription { return nil } func (r *releaseItemExecutionRows) RawValues() [][]byte { return nil } func (r *releaseItemExecutionRows) Conn() *pgx.Conn { return nil } func (r *releaseItemExecutionRows) Next() bool { if r.index >= len(r.values) { return false } r.index++ return true } func (r *releaseItemExecutionRows) Scan(dest ...any) error { value := r.values[r.index-1] *dest[0].(*string), *dest[1].(*string), *dest[2].(*string), *dest[3].(*string) = value.publicationID, value.title, value.action, value.state *dest[4].(*string), *dest[5].(*string), *dest[6].(*string) = value.previousRevision, value.productionRevision, value.errorCode *dest[7].(*time.Time), *dest[8].(**time.Time) = value.createdAt, value.completedAt return nil } func (r *releaseItemExecutionRows) Values() ([]any, error) { return nil, nil } type releaseExecutionReadDB struct { countRow, row pgx.Row rows pgx.Rows queries []string queryRows int } func (d *releaseExecutionReadDB) Begin(context.Context) (pgx.Tx, error) { return nil, errors.New("transactions are not supported by the reading fake") } func (d *releaseExecutionReadDB) QueryRow(_ context.Context, sql string, _ ...any) pgx.Row { d.queries = append(d.queries, sql) d.queryRows++ if d.queryRows == 1 { return d.countRow } return d.row } func (d *releaseExecutionReadDB) Query(_ context.Context, sql string, _ ...any) (pgx.Rows, error) { d.queries = append(d.queries, sql) return d.rows, nil } func (d *releaseExecutionReadDB) Exec(context.Context, string, ...any) (pgconn.CommandTag, error) { return pgconn.CommandTag{}, nil }