f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
225 lines
7.1 KiB
Go
225 lines
7.1 KiB
Go
//go:build pgtest
|
|
|
|
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.kenn.io/agentsview/internal/db"
|
|
)
|
|
|
|
func TestBackfillIsAutomatedPGMatchingHashUsesBoundedEvidence(t *testing.T) {
|
|
pgURL := testPGURL(t)
|
|
cleanPGSchema(t, pgURL)
|
|
t.Cleanup(func() { cleanPGSchema(t, pgURL) })
|
|
|
|
local := testDB(t)
|
|
ps, err := New(
|
|
pgURL, "agentsview", local,
|
|
"automated-audit-machine", true,
|
|
SyncOptions{},
|
|
)
|
|
require.NoError(t, err, "creating sync")
|
|
defer ps.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
require.NoError(t, ps.EnsureSchema(ctx), "ensure schema")
|
|
|
|
largePrefix := "You are a code reviewer." + strings.Repeat("x", 4<<10)
|
|
lateSubstring := strings.Repeat("z", 2<<10) +
|
|
" invoked by roborev to perform this review"
|
|
largeHuman := strings.Repeat("h", 2<<10)
|
|
rows := []struct {
|
|
id string
|
|
firstMessage string
|
|
userMessageCount int
|
|
isAutomated bool
|
|
}{
|
|
{id: "prefix-first-message", firstMessage: largePrefix, userMessageCount: 1},
|
|
{id: "prefix-first-user", firstMessage: "Generated title", userMessageCount: 1},
|
|
{id: "late-substring", firstMessage: lateSubstring, userMessageCount: 1},
|
|
{id: "unicode-exact", firstMessage: "\u2003Warmup\u00a0", userMessageCount: 1},
|
|
{
|
|
id: "stale-multi-turn", firstMessage: "# Fix Request for login flow",
|
|
userMessageCount: 2, isAutomated: true,
|
|
},
|
|
{id: "large-human", firstMessage: largeHuman, userMessageCount: 1},
|
|
}
|
|
for _, row := range rows {
|
|
_, err := ps.DB().ExecContext(ctx,
|
|
`INSERT INTO sessions (
|
|
id, machine, project, agent, first_message,
|
|
user_message_count, is_automated
|
|
) VALUES ($1, 'host', 'proj', 'codex', $2, $3, $4)`,
|
|
row.id, row.firstMessage, row.userMessageCount, row.isAutomated,
|
|
)
|
|
require.NoError(t, err, "insert %s", row.id)
|
|
}
|
|
_, err = ps.DB().ExecContext(ctx,
|
|
`INSERT INTO messages (session_id, ordinal, role, content)
|
|
VALUES ($1, 0, 'user', $2)`,
|
|
"prefix-first-user", largePrefix,
|
|
)
|
|
require.NoError(t, err, "insert prefix-matching first user message")
|
|
_, err = ps.DB().ExecContext(ctx,
|
|
`INSERT INTO sync_metadata (key, value) VALUES ($1, $2)
|
|
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`,
|
|
db.ClassifierHashKey, db.ClassifierHash(),
|
|
)
|
|
require.NoError(t, err, "stamp matching classifier hash")
|
|
|
|
progress, err := backfillIsAutomatedPGWithProgress(ctx, ps.DB())
|
|
require.NoError(t, err, "bounded matching-hash audit")
|
|
assert.Equal(t, len(rows), progress.RowsPrefetched)
|
|
assert.Equal(t, 2, progress.RowsFullText,
|
|
"only truncated non-matches should require complete text")
|
|
|
|
for id, want := range map[string]bool{
|
|
"prefix-first-message": true,
|
|
"prefix-first-user": true,
|
|
"late-substring": true,
|
|
"unicode-exact": true,
|
|
"stale-multi-turn": false,
|
|
"large-human": false,
|
|
} {
|
|
var got bool
|
|
require.NoError(t, ps.DB().QueryRowContext(ctx,
|
|
`SELECT is_automated FROM sessions WHERE id = $1`, id,
|
|
).Scan(&got), "query %s", id)
|
|
assert.Equal(t, want, got, id)
|
|
}
|
|
}
|
|
|
|
// TestPushSessionTrustsLocalIsAutomated verifies that
|
|
// pushSession copies sess.IsAutomated verbatim instead of
|
|
// re-running db.IsAutomatedSession on the first_message.
|
|
// Achieved by setting a user prefix locally, upserting a
|
|
// matching session (so IsAutomated=1), then clearing the
|
|
// user prefix BEFORE push and confirming the PG row stays
|
|
// IsAutomated=1.
|
|
func TestPushSessionTrustsLocalIsAutomated(t *testing.T) {
|
|
t.Cleanup(func() { db.SetUserAutomationPrefixes(nil) })
|
|
pgURL := testPGURL(t)
|
|
cleanPGSchema(t, pgURL)
|
|
t.Cleanup(func() { cleanPGSchema(t, pgURL) })
|
|
|
|
local := testDB(t)
|
|
|
|
// Set a user prefix BEFORE inserting so UpsertSession
|
|
// sets is_automated=1 on the SQLite row.
|
|
db.SetUserAutomationPrefixes([]string{"You are analyzing an essay"})
|
|
fm := "You are analyzing an essay about epistemology."
|
|
require.NoError(t, local.UpsertSession(db.Session{
|
|
ID: "essay-1",
|
|
Project: "proj",
|
|
Machine: "local",
|
|
Agent: "claude",
|
|
FirstMessage: &fm,
|
|
MessageCount: 2,
|
|
UserMessageCount: 1,
|
|
CreatedAt: time.Now().UTC().Format(time.RFC3339Nano),
|
|
}), "upsert")
|
|
|
|
// Clear the user prefix so a recompute in pushSession
|
|
// would now classify this row as is_automated=0. If
|
|
// pushSession trusts the local value, PG sees =1 anyway.
|
|
db.SetUserAutomationPrefixes(nil)
|
|
|
|
ps, err := New(
|
|
pgURL, "agentsview", local,
|
|
"trust-test-machine", true,
|
|
SyncOptions{},
|
|
)
|
|
require.NoError(t, err, "creating sync")
|
|
defer ps.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(
|
|
context.Background(), 30*time.Second,
|
|
)
|
|
defer cancel()
|
|
|
|
require.NoError(t, ps.EnsureSchema(ctx), "ensure schema")
|
|
_, err = ps.Push(ctx, false, nil)
|
|
require.NoError(t, err, "push")
|
|
|
|
var got bool
|
|
require.NoError(t, ps.DB().QueryRowContext(ctx,
|
|
`SELECT is_automated FROM sessions WHERE id = $1`,
|
|
"essay-1",
|
|
).Scan(&got), "query pg")
|
|
assert.True(t, got,
|
|
"pushSession recomputed is_automated; expected to trust local value")
|
|
}
|
|
|
|
// TestBackfillIsAutomatedPGRerunsOnHashChange exercises the
|
|
// PG-side hash-driven backfill: after a classifier change
|
|
// (here, adding a user prefix), EnsureSchema re-runs the
|
|
// backfill and flips matching rows to is_automated=true.
|
|
func TestBackfillIsAutomatedPGRerunsOnHashChange(t *testing.T) {
|
|
t.Cleanup(func() { db.SetUserAutomationPrefixes(nil) })
|
|
pgURL := testPGURL(t)
|
|
cleanPGSchema(t, pgURL)
|
|
t.Cleanup(func() { cleanPGSchema(t, pgURL) })
|
|
|
|
local := testDB(t)
|
|
fm := "You are analyzing an essay about epistemology."
|
|
require.NoError(t, local.UpsertSession(db.Session{
|
|
ID: "essay-pg",
|
|
Project: "proj",
|
|
Machine: "local",
|
|
Agent: "claude",
|
|
FirstMessage: &fm,
|
|
MessageCount: 2,
|
|
UserMessageCount: 1,
|
|
CreatedAt: time.Now().UTC().Format(time.RFC3339Nano),
|
|
}), "upsert")
|
|
|
|
ps, err := New(
|
|
pgURL, "agentsview", local,
|
|
"backfill-test-machine", true,
|
|
SyncOptions{},
|
|
)
|
|
require.NoError(t, err, "creating sync")
|
|
defer ps.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(
|
|
context.Background(), 30*time.Second,
|
|
)
|
|
defer cancel()
|
|
|
|
require.NoError(t, ps.EnsureSchema(ctx), "ensure schema")
|
|
_, err = ps.Push(ctx, false, nil)
|
|
require.NoError(t, err, "push")
|
|
|
|
// Confirm the PG row starts as is_automated=false.
|
|
var pre bool
|
|
require.NoError(t, ps.DB().QueryRowContext(ctx,
|
|
`SELECT is_automated FROM sessions WHERE id = $1`,
|
|
"essay-pg",
|
|
).Scan(&pre), "query pre")
|
|
require.False(t, pre, "precondition: PG row should be is_automated=false")
|
|
|
|
// Add a user prefix so the classifier hash changes,
|
|
// then re-run the PG backfill directly (bypassing
|
|
// Sync.EnsureSchema's memo so the second pass actually
|
|
// executes). The matching row should flip to true.
|
|
db.SetUserAutomationPrefixes([]string{"You are analyzing an essay"})
|
|
require.NoError(t, backfillIsAutomatedPG(ctx, ps.DB()),
|
|
"backfill after prefix add")
|
|
|
|
var got bool
|
|
require.NoError(t, ps.DB().QueryRowContext(ctx,
|
|
`SELECT is_automated FROM sessions WHERE id = $1`,
|
|
"essay-pg",
|
|
).Scan(&got), "query post")
|
|
assert.True(t, got,
|
|
"PG row should be is_automated=true after backfill on hash change")
|
|
}
|