Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

2356 lines
76 KiB
Go

//go:build pgtest
package postgres
import (
"context"
"database/sql"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.kenn.io/agentsview/internal/db"
"go.kenn.io/agentsview/internal/export"
)
func TestPushMirrorsSessionProjectIdentitySnapshotsByArchiveGeneration(
t *testing.T,
) {
pgURL := testPGURL(t)
const schema = "agentsview_push_project_snapshot_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err)
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err)
require.NoError(t, EnsureSchema(ctx, pg, schema))
local, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err)
defer local.Close()
require.NoError(t, local.UpsertSession(db.Session{
ID: "snapshot-session", Project: "app", Machine: "laptop", Agent: "codex",
}))
require.NoError(t, local.UpsertProjectIdentityObservation(ctx,
export.ProjectIdentityObservation{
SessionID: "snapshot-session", Project: "app", Machine: "laptop",
RootPath: "/workspace/app", GitRemote: "https://github.com/acme/app.git",
GitRemoteName: "origin", RepositoryPath: "/workspace/app/.git",
WorktreeRootPath: "/workspace/app",
WorktreeRelationship: export.WorktreeMain,
CheckoutState: export.CheckoutDetached,
RemoteResolution: export.ProjectResolutionResolved,
ObservedAt: time.Date(2026, 7, 10, 12, 0, 0, 0, time.UTC),
},
))
archiveID, err := local.GetArchiveID(ctx)
require.NoError(t, err)
generation, err := local.GetDatabaseID(ctx)
require.NoError(t, err)
syncer := &Sync{
pg: pg, local: local, machine: "laptop", schema: schema, schemaDone: true,
}
require.NoError(t, syncer.syncProjectIdentityObservations(ctx, false))
var gotArchive, gotGeneration, gotSession, gotRemote string
var gotRelationship export.WorktreeRelationship
var gotCheckout export.CheckoutState
err = pg.QueryRowContext(ctx, `
SELECT source_archive_id, source_database_generation,
source_session_id, git_remote, worktree_relationship, checkout_state
FROM source_session_project_identity_snapshots
WHERE source_session_id = $1`, "snapshot-session",
).Scan(
&gotArchive, &gotGeneration, &gotSession, &gotRemote,
&gotRelationship, &gotCheckout,
)
require.NoError(t, err)
assert.Equal(t, archiveID, gotArchive)
assert.Equal(t, generation, gotGeneration)
assert.Equal(t, "snapshot-session", gotSession)
assert.Equal(t, "https://github.com/acme/app.git", gotRemote)
assert.Equal(t, export.WorktreeMain, gotRelationship)
assert.Equal(t, export.CheckoutDetached, gotCheckout)
_, err = pg.ExecContext(ctx, `
UPDATE source_session_project_identity_snapshots
SET git_remote = 'sentinel'
WHERE source_session_id = $1`, "snapshot-session")
require.NoError(t, err)
require.NoError(t, syncer.syncProjectIdentityObservations(ctx, false))
require.NoError(t, pg.QueryRowContext(ctx, `
SELECT git_remote FROM source_session_project_identity_snapshots
WHERE source_session_id = $1`, "snapshot-session").Scan(&gotRemote))
assert.Equal(t, "sentinel", gotRemote,
"unchanged local revision should skip PG publication")
require.NoError(t, syncer.syncProjectIdentityObservations(ctx, true))
require.NoError(t, pg.QueryRowContext(ctx, `
SELECT git_remote FROM source_session_project_identity_snapshots
WHERE source_session_id = $1`, "snapshot-session").Scan(&gotRemote))
assert.Equal(t, "https://github.com/acme/app.git", gotRemote,
"forced publication should rebuild PG identity rows")
require.NoError(t, local.DeleteSession("snapshot-session"))
require.NoError(t, syncer.syncProjectIdentityObservations(ctx, false))
var snapshotCount int
require.NoError(t, pg.QueryRowContext(ctx, `
SELECT COUNT(*) FROM source_session_project_identity_snapshots
WHERE source_archive_id = $1`, archiveID,
).Scan(&snapshotCount))
assert.Zero(t, snapshotCount)
}
func TestFilteredThenUnfilteredIdentityPublicationIncludesExcludedProject(
t *testing.T,
) {
pgURL := testPGURL(t)
const schema = "agentsview_filtered_identity_revision_test"
cleanNamedPGSchema(t, pgURL, schema)
t.Cleanup(func() { cleanNamedPGSchema(t, pgURL, schema) })
ctx := context.Background()
local, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, local.Close()) })
for _, project := range []string{"alpha", "beta"} {
sessionID := "identity-" + project
require.NoError(t, local.UpsertSession(db.Session{
ID: sessionID, Project: project, Machine: "laptop", Agent: "codex",
}))
require.NoError(t, local.UpsertProjectIdentityObservation(ctx,
export.ProjectIdentityObservation{
SessionID: sessionID, Project: project, Machine: "laptop",
RootPath: "/workspace/" + project,
GitRemote: "https://example.com/" + project + ".git",
RemoteResolution: export.ProjectResolutionResolved,
ObservedAt: time.Date(2026, 7, 11, 12, 0, 0, 0, time.UTC),
},
))
}
filtered, err := New(
pgURL, schema, local, "laptop", true,
SyncOptions{Projects: []string{"alpha"}},
)
require.NoError(t, err)
require.NoError(t, filtered.EnsureSchema(ctx))
require.NoError(t, filtered.syncProjectIdentityObservations(ctx, false))
_, err = filtered.pg.ExecContext(ctx, `
UPDATE source_project_identity_observations
SET git_remote_name = 'sentinel'
WHERE project = 'alpha'`)
require.NoError(t, err)
require.NoError(t, local.UpsertProjectIdentityObservation(ctx,
export.ProjectIdentityObservation{
SessionID: "identity-beta", Project: "beta", Machine: "laptop",
RootPath: "/workspace/beta",
GitRemote: "https://example.com/beta.git",
GitRemoteName: "upstream",
RemoteResolution: export.ProjectResolutionResolved,
ObservedAt: time.Date(2026, 7, 11, 13, 0, 0, 0, time.UTC),
},
))
require.NoError(t, filtered.syncProjectIdentityObservations(ctx, false))
var alphaRemoteName string
require.NoError(t, filtered.pg.QueryRowContext(ctx, `
SELECT git_remote_name FROM source_project_identity_observations
WHERE project = 'alpha'`).Scan(&alphaRemoteName))
assert.Equal(t, "sentinel", alphaRemoteName,
"out-of-scope changes must not republish the filtered identity scope")
require.NoError(t, filtered.Close())
unfiltered, err := New(
pgURL, schema, local, "laptop", true, SyncOptions{},
)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, unfiltered.Close()) })
require.NoError(t, unfiltered.EnsureSchema(ctx))
require.NoError(t, unfiltered.syncProjectIdentityObservations(ctx, false))
var betaSnapshots int
require.NoError(t, unfiltered.pg.QueryRowContext(ctx, `
SELECT COUNT(*) FROM source_session_project_identity_snapshots
WHERE project = $1`, "beta").Scan(&betaSnapshots))
assert.Equal(t, 1, betaSnapshots)
}
func TestIdentityPublicationUpdatesOnlyChangedRowsAndAppliesTombstones(
t *testing.T,
) {
pgURL := testPGURL(t)
const schema = "agentsview_identity_delta_test"
cleanNamedPGSchema(t, pgURL, schema)
t.Cleanup(func() { cleanNamedPGSchema(t, pgURL, schema) })
ctx := context.Background()
local, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, local.Close()) })
observedAt := time.Date(2026, 7, 11, 12, 0, 0, 0, time.UTC)
for _, project := range []string{"alpha", "beta"} {
sessionID := "identity-" + project
require.NoError(t, local.UpsertSession(db.Session{
ID: sessionID, Project: project, Machine: "laptop", Agent: "codex",
}))
require.NoError(t, local.UpsertProjectIdentityObservation(ctx,
export.ProjectIdentityObservation{
SessionID: sessionID, Project: project, Machine: "laptop",
RootPath: "/workspace/" + project,
GitRemote: "https://example.com/" + project + ".git",
GitRemoteName: "origin",
RemoteResolution: export.ProjectResolutionResolved,
ObservedAt: observedAt,
},
))
}
require.NoError(t, local.UpsertSession(db.Session{
ID: "identity-gamma", Project: "gamma", Machine: "laptop", Agent: "codex",
}))
require.NoError(t, local.UpsertProjectIdentityObservation(ctx,
export.ProjectIdentityObservation{
SessionID: "identity-gamma", Project: "gamma", Machine: "laptop",
RootPath: "/workspace/gamma",
RemoteResolution: export.ProjectResolutionUnknown,
ObservedAt: observedAt,
},
))
syncer, err := New(pgURL, schema, local, "laptop", true, SyncOptions{})
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, syncer.Close()) })
require.NoError(t, syncer.EnsureSchema(ctx))
require.NoError(t, syncer.syncProjectIdentityObservations(ctx, false))
_, err = syncer.pg.ExecContext(ctx, `
UPDATE source_project_identity_observations
SET git_remote_name = 'sentinel'
WHERE project = 'beta'`)
require.NoError(t, err)
require.NoError(t, local.UpsertProjectIdentityObservation(ctx,
export.ProjectIdentityObservation{
SessionID: "identity-alpha", Project: "alpha", Machine: "laptop",
RootPath: "/workspace/alpha",
GitRemote: "https://example.com/alpha.git",
GitRemoteName: "upstream",
RemoteResolution: export.ProjectResolutionResolved,
ObservedAt: observedAt.Add(time.Hour),
},
))
require.NoError(t, local.UpsertProjectIdentityObservation(ctx,
export.ProjectIdentityObservation{
SessionID: "identity-gamma", Project: "gamma", Machine: "laptop",
RootPath: "/workspace/gamma",
GitRemote: "https://example.com/gamma.git",
GitRemoteName: "origin",
RemoteResolution: export.ProjectResolutionResolved,
ObservedAt: observedAt.Add(time.Hour),
},
))
require.NoError(t, syncer.syncProjectIdentityObservations(ctx, false))
var alphaRemoteName, betaRemoteName string
require.NoError(t, syncer.pg.QueryRowContext(ctx, `
SELECT git_remote_name FROM source_project_identity_observations
WHERE project = 'alpha'`).Scan(&alphaRemoteName))
require.NoError(t, syncer.pg.QueryRowContext(ctx, `
SELECT git_remote_name FROM source_project_identity_observations
WHERE project = 'beta'`).Scan(&betaRemoteName))
assert.Equal(t, "upstream", alphaRemoteName)
assert.Equal(t, "sentinel", betaRemoteName,
"incremental publication must not rewrite unchanged rows")
var gammaFallbacks, gammaRemotes int
require.NoError(t, syncer.pg.QueryRowContext(ctx, `
SELECT COUNT(*) FROM source_project_identity_observations
WHERE project = 'gamma' AND git_remote = ''`).Scan(&gammaFallbacks))
require.NoError(t, syncer.pg.QueryRowContext(ctx, `
SELECT COUNT(*) FROM source_project_identity_observations
WHERE project = 'gamma' AND git_remote = $1`,
"https://example.com/gamma.git").Scan(&gammaRemotes))
assert.Zero(t, gammaFallbacks)
assert.Equal(t, 1, gammaRemotes)
require.NoError(t, local.DeleteSession("identity-alpha"))
require.NoError(t, syncer.syncProjectIdentityObservations(ctx, false))
var snapshotCount int
require.NoError(t, syncer.pg.QueryRowContext(ctx, `
SELECT COUNT(*) FROM source_session_project_identity_snapshots
WHERE source_session_id = $1`, "identity-alpha").Scan(&snapshotCount))
assert.Zero(t, snapshotCount)
}
// TestPushSystemFingerprintCollisionRegression verifies that the fast-path
// in pushMessages correctly detects a change when the is_system flags are
// reclassified between two ordinal sets that previously collided under the
// two-component (SUM, SUM-of-squares) fingerprint: {0,4,5} and {1,2,6}
// both produce sum=9, sumSq=41.
//
// Steps:
// 1. Push a session with 7 messages where ordinals {0,4,5} are system.
// 2. Without changing content lengths, reclassify to {1,2,6} as system.
// 3. Push again with full=false.
// 4. Confirm PG now reflects the updated is_system values.
func TestPushSystemFingerprintCollisionRegression(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_sysfingerprint_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
// Local SQLite DB.
localDB, err := db.Open(
filepath.Join(t.TempDir(), "local.db"),
)
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
// Mark schema done so Push skips EnsureSchema.
schemaDone: true,
}
const sessID = "fp-collision-001"
sess := db.Session{
ID: sessID,
Project: "test-proj",
Machine: "test-machine",
Agent: "claude",
MessageCount: 7,
CreatedAt: "2026-01-01T00:00:00Z",
}
require.NoError(t, localDB.UpsertSession(sess), "UpsertSession")
// First set: system ordinals {0,4,5}.
firstSet := map[int]bool{0: true, 4: true, 5: true}
msgs := make([]db.Message, 7)
for i := range 7 {
msgs[i] = db.Message{
SessionID: sessID,
Ordinal: i,
Role: "user",
Content: "x",
ContentLength: 1,
IsSystem: firstSet[i],
}
}
require.NoError(t, localDB.InsertMessages(msgs), "InsertMessages (first set)")
// First push.
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push (first)")
// Verify PG reflects system ordinals {0,4,5}.
checkIsSystem(t, pg, sessID, firstSet, 7)
// Switch to {1,2,6} — same sum(ordinal)=9, same sum(ordinal²)=41,
// but the string fingerprint differs ("0,4,5" vs "1,2,6").
// Replace local messages with updated is_system flags.
secondSet := map[int]bool{1: true, 2: true, 6: true}
for i := range 7 {
msgs[i].IsSystem = secondSet[i]
}
require.NoError(t, localDB.ReplaceSessionMessages(sessID, msgs),
"ReplaceSessionMessages (second set)")
// Force re-evaluation by clearing both the watermark and the cached
// session-level boundary fingerprints. The session-level fingerprint
// does not include is_system flags (only metadata like MessageCount),
// so the boundary cache must be cleared for the incremental push to
// reach pushMessages and compare the message-level string fingerprint.
require.NoError(t, localDB.SetSyncState("last_push_at", ""),
"clearing last_push_at")
require.NoError(t, localDB.SetSyncState(lastPushBoundaryStateKey, ""),
"clearing boundary state")
// Second push — must NOT skip due to fingerprint match.
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push (second)")
// Verify PG now reflects updated system ordinals {1,2,6}.
checkIsSystem(t, pg, sessID, secondSet, 7)
}
func TestPushMessageContentHashRewriteRegression(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_contenthash_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
const sessID = "content-hash-rewrite-001"
sess := db.Session{
ID: sessID,
Project: "test-proj",
Machine: "test-machine",
Agent: "shelley",
MessageCount: 2,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}
require.NoError(t, localDB.UpsertSession(sess), "UpsertSession")
msgs := []db.Message{
{
SessionID: sessID,
Ordinal: 1,
Role: "user",
Content: "question",
ContentLength: len("question"),
},
{
SessionID: sessID,
Ordinal: 2,
Role: "assistant",
Content: "answer aaaa",
ContentLength: len("answer aaaa"),
},
}
require.NoError(t, localDB.InsertMessages(msgs),
"InsertMessages first content")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push first content")
assertPGMessageContent(t, pg, sessID, 2, "answer aaaa")
msgs[1].Content = "answer bbbb"
msgs[1].ContentLength = len("answer bbbb")
require.NoError(t, localDB.ReplaceSessionMessages(sessID, msgs),
"ReplaceSessionMessages rewritten content")
require.NoError(t, localDB.SetSyncState("last_push_at", ""),
"clearing last_push_at")
require.NoError(t, localDB.SetSyncState(lastPushBoundaryStateKey, ""),
"clearing boundary state")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push rewritten content")
assertPGMessageContent(t, pg, sessID, 2, "answer bbbb")
}
func TestPushMessageFlagsRewriteRegression(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_msgflags_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
const sessID = "message-flags-rewrite-001"
sess := db.Session{
ID: sessID,
Project: "test-proj",
Machine: "test-machine",
Agent: "shelley",
MessageCount: 2,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}
require.NoError(t, localDB.UpsertSession(sess), "UpsertSession")
msgs := []db.Message{
{
SessionID: sessID,
Ordinal: 1,
Role: "user",
Content: "question",
ContentLength: len("question"),
},
{
SessionID: sessID,
Ordinal: 2,
Role: "assistant",
Content: "answer",
ContentLength: len("answer"),
},
}
require.NoError(t, localDB.InsertMessages(msgs),
"InsertMessages first metadata")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push first metadata")
assertPGMessageThinking(t, pg, sessID, 2, false, "")
msgs[1].ThinkingText = "private chain of thought"
msgs[1].HasThinking = true
require.NoError(t, localDB.ReplaceSessionMessages(sessID, msgs),
"ReplaceSessionMessages rewritten metadata")
require.NoError(t, localDB.SetSyncState("last_push_at", ""),
"clearing last_push_at")
require.NoError(t, localDB.SetSyncState(lastPushBoundaryStateKey, ""),
"clearing boundary state")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push rewritten metadata")
assertPGMessageThinking(t, pg, sessID, 2, true,
"private chain of thought")
}
func TestPushMessageNanosecondTimestampNoRewriteRegression(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_msgtime_nanos_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
const sessID = "message-nanotime-rewrite-001"
sess := db.Session{
ID: sessID,
Project: "test-proj",
Machine: "test-machine",
Agent: "shelley",
MessageCount: 1,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}
require.NoError(t, localDB.UpsertSession(sess), "UpsertSession")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sessID,
Ordinal: 1,
Role: "user",
Content: "question",
ContentLength: len("question"),
Timestamp: "2026-01-01T00:00:00.123456789Z",
}}), "InsertMessages")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push first timestamp")
assertPGMessageTimestamp(t, pg, sessID, 1,
"2026-01-01T00:00:00.123456Z")
ctidBefore := pgMessageCTID(t, pg, sessID, 1)
require.NoError(t, localDB.SetSyncState("last_push_at", ""),
"clearing last_push_at")
require.NoError(t, localDB.SetSyncState(lastPushBoundaryStateKey, ""),
"clearing boundary state")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push same timestamp")
assert.Equal(t, ctidBefore, pgMessageCTID(t, pg, sessID, 1),
"microsecond-equivalent timestamps should hit the fast path")
}
// TestPushSessionTerminationStatus verifies that pushSession round-trips
// the termination_status column to PG: a non-nil value writes the string,
// and a subsequent push with nil clears the column back to NULL via the
// ON CONFLICT DO UPDATE path.
func TestPushSessionTerminationStatus(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_termstatus_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
markerID, err := sync.pushMarkerID()
require.NoError(t, err, "pushMarkerID")
pending := "tool_call_pending"
sess := db.Session{
ID: "term-test-1",
Project: "p",
Machine: "test-machine",
Agent: "claude",
MessageCount: 1,
UserMessageCount: 1,
// CreatedAt must be parseable by ParseSQLiteTimestamp;
// PG's NOT NULL on created_at would otherwise reject NULL.
CreatedAt: "2024-01-01T00:00:00Z",
TerminationStatus: &pending,
}
pushOnce := func(s db.Session) {
t.Helper()
tx, err := pg.BeginTx(ctx, nil)
require.NoError(t, err, "BeginTx")
if err := sync.pushSession(ctx, tx, s, markerID, nil); err != nil {
_ = tx.Rollback()
t.Fatalf("pushSession: %v", err)
}
require.NoError(t, tx.Commit(), "Commit")
}
pushOnce(sess)
var got *string
require.NoError(t, pg.QueryRow(
`SELECT termination_status FROM sessions WHERE id = $1`,
sess.ID,
).Scan(&got), "read back")
require.NotNil(t, got)
assert.Equal(t, "tool_call_pending", *got)
// Update to NULL and verify ON CONFLICT clears it.
sess.TerminationStatus = nil
pushOnce(sess)
require.NoError(t, pg.QueryRow(
`SELECT termination_status FROM sessions WHERE id = $1`,
sess.ID,
).Scan(&got), "read back 2")
assert.Nil(t, got)
}
func TestPushSessionPreservesSourceMachine(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_source_machine_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "push-host",
schema: schema,
schemaDone: true,
}
remoteSession := db.Session{
ID: "remote-source-machine-1",
Project: "proj",
Machine: "remote-host",
Agent: "claude",
MessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}
tx, err := pg.BeginTx(ctx, nil)
require.NoError(t, err, "BeginTx")
markerID, err := sync.pushMarkerID()
require.NoError(t, err, "pushMarkerID")
require.NoError(t, sync.pushSession(ctx, tx, remoteSession, markerID, nil), "pushSession")
require.NoError(t, tx.Commit(), "Commit")
var got string
require.NoError(t, pg.QueryRow(
`SELECT machine FROM sessions WHERE id = $1`,
remoteSession.ID,
).Scan(&got), "read back machine")
assert.Equal(t, "remote-host", got)
}
func TestPushPreservesPGServeLocalCurationFields(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_preserve_pg_curation_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
const sessID = "pg-curation-preserve-001"
firstMessage := "source before"
sess := db.Session{
ID: sessID,
Project: "test-proj",
Machine: "test-machine",
Agent: "claude",
FirstMessage: &firstMessage,
MessageCount: 1,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}
require.NoError(t, localDB.UpsertSession(sess), "UpsertSession")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sessID,
Ordinal: 0,
Role: "user",
Content: firstMessage,
ContentLength: len(firstMessage),
}}), "InsertMessages")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push before local PG curation")
store, err := NewStore(pgURL, schema, true)
require.NoError(t, err, "NewStore")
defer store.Close()
renamed := "Renamed in PG serve"
require.NoError(t, store.RenameSession(sessID, &renamed),
"RenameSession")
require.NoError(t, store.SoftDeleteSession(sessID),
"SoftDeleteSession")
updatedFirstMessage := "source after"
sess.FirstMessage = &updatedFirstMessage
require.NoError(t, localDB.UpsertSession(sess),
"UpsertSession updated source row")
require.NoError(t, localDB.SetSyncState("last_push_at", ""),
"clearing last_push_at")
require.NoError(t, localDB.SetSyncState(lastPushBoundaryStateKey, ""),
"clearing boundary state")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push after local PG curation")
var gotDisplayName sql.NullString
var gotDeleted bool
var gotFirstMessage sql.NullString
require.NoError(t, pg.QueryRow(
`SELECT display_name, deleted_at IS NOT NULL, first_message
FROM sessions WHERE id = $1`,
sessID,
).Scan(&gotDisplayName, &gotDeleted, &gotFirstMessage),
"reading pushed curation row")
require.True(t, gotDisplayName.Valid, "display_name should persist")
assert.Equal(t, renamed, gotDisplayName.String)
assert.True(t, gotDeleted, "PG-local trash state should persist")
require.True(t, gotFirstMessage.Valid, "first_message should remain visible")
assert.Equal(t, updatedFirstMessage, gotFirstMessage.String,
"source-owned fields should still update")
}
func TestPushPreservesPGServePermanentDeletes(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_preserve_pg_deletes_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
const deleteIfTrashedID = "pg-delete-preserve-001"
const emptyTrashID = "pg-delete-preserve-002"
sourceRows := []db.Session{
{
ID: deleteIfTrashedID,
Project: "test-proj",
Machine: "test-machine",
Agent: "claude",
MessageCount: 1,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
},
{
ID: emptyTrashID,
Project: "test-proj",
Machine: "test-machine",
Agent: "claude",
MessageCount: 1,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:01Z",
},
}
for _, sess := range sourceRows {
require.NoError(t, localDB.UpsertSession(sess),
"UpsertSession "+sess.ID)
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sess.ID,
Ordinal: 0,
Role: "user",
Content: sess.ID,
ContentLength: len(sess.ID),
}}), "InsertMessages "+sess.ID)
}
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push before PG permanent deletes")
store, err := NewStore(pgURL, schema, true)
require.NoError(t, err, "NewStore")
defer store.Close()
require.NoError(t, store.SoftDeleteSession(deleteIfTrashedID),
"SoftDeleteSession deleteIfTrashedID")
deleted, err := store.DeleteSessionIfTrashed(deleteIfTrashedID)
require.NoError(t, err, "DeleteSessionIfTrashed")
assert.EqualValues(t, 1, deleted)
deletedCount, err := store.SoftDeleteSessions([]string{emptyTrashID})
require.NoError(t, err, "SoftDeleteSessions")
assert.Equal(t, 1, deletedCount)
emptied, err := store.EmptyTrash()
require.NoError(t, err, "EmptyTrash")
assert.Equal(t, 1, emptied)
for _, sess := range sourceRows {
updated := sess
updated.MessageCount = 2
require.NoError(t, localDB.UpsertSession(updated),
"UpsertSession updated "+sess.ID)
require.NoError(t, localDB.ReplaceSessionMessages(sess.ID, []db.Message{
{
SessionID: sess.ID,
Ordinal: 0,
Role: "user",
Content: sess.ID + "-updated",
ContentLength: len(sess.ID + "-updated"),
},
{
SessionID: sess.ID,
Ordinal: 1,
Role: "assistant",
Content: "reply",
ContentLength: len("reply"),
},
}), "ReplaceSessionMessages "+sess.ID)
}
_, err = sync.Push(ctx, true, nil)
require.NoError(t, err, "full Push after PG permanent deletes")
for _, sessionID := range []string{deleteIfTrashedID, emptyTrashID} {
var count int
require.NoError(t, pg.QueryRow(
`SELECT COUNT(*) FROM sessions WHERE id = $1`,
sessionID,
).Scan(&count), "count deleted session "+sessionID)
assert.Zero(t, count, "permanently deleted session should stay absent")
}
var excludedCount int
require.NoError(t, pg.QueryRow(
`SELECT COUNT(*) FROM excluded_sessions
WHERE id IN ($1, $2)`,
deleteIfTrashedID, emptyTrashID,
).Scan(&excludedCount), "count pg excluded sessions")
assert.Equal(t, 2, excludedCount)
}
func TestPushPurgesRowsForPGExcludedSessions(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_excluded_purge_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
const sessionID = "pg-excluded-purge-001"
sess := db.Session{
ID: sessionID,
Project: "test-proj",
Machine: "test-machine",
Agent: "claude",
MessageCount: 1,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}
require.NoError(t, localDB.UpsertSession(sess), "UpsertSession")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sessionID,
Ordinal: 0,
Role: "user",
Content: "hello",
ContentLength: len("hello"),
}}), "InsertMessages")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "initial Push")
_, err = pg.Exec(
`INSERT INTO excluded_sessions (id) VALUES ($1)`,
sessionID,
)
require.NoError(t, err, "insert excluded session")
updated := sess
updated.MessageCount = 2
require.NoError(t, localDB.UpsertSession(updated), "update local session")
require.NoError(t, localDB.ReplaceSessionMessages(sessionID, []db.Message{
{
SessionID: sessionID,
Ordinal: 0,
Role: "user",
Content: "hello updated",
ContentLength: len("hello updated"),
},
{
SessionID: sessionID,
Ordinal: 1,
Role: "assistant",
Content: "reply",
ContentLength: len("reply"),
},
}), "ReplaceSessionMessages")
res, err := sync.Push(ctx, true, nil)
require.NoError(t, err, "full Push with excluded row")
assert.Zero(t, res.SessionsPushed)
var count int
require.NoError(t, pg.QueryRow(
`SELECT COUNT(*) FROM sessions WHERE id = $1`,
sessionID,
).Scan(&count), "count purged session")
assert.Zero(t, count, "excluded session row should be purged")
}
func TestPushSessionSkipsPGExcludedSession(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_session_excluded_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
const sessionID = "pg-excluded-push-session-001"
_, err = pg.Exec(
`INSERT INTO excluded_sessions (id) VALUES ($1)`,
sessionID,
)
require.NoError(t, err, "insert excluded session")
tx, err := pg.BeginTx(ctx, nil)
require.NoError(t, err, "BeginTx")
markerID, err := sync.pushMarkerID()
require.NoError(t, err, "pushMarkerID")
err = sync.pushSession(ctx, tx, db.Session{
ID: sessionID,
Project: "test-proj",
Machine: "test-machine",
Agent: "claude",
CreatedAt: "2026-01-01T00:00:00Z",
}, markerID, nil)
require.ErrorIs(t, err, errSessionExcluded)
require.NoError(t, tx.Rollback(), "Rollback")
var count int
require.NoError(t, pg.QueryRow(
`SELECT COUNT(*) FROM sessions WHERE id = $1`,
sessionID,
).Scan(&count), "count skipped session")
assert.Zero(t, count)
}
func TestPushUpdatesSourceCurationFieldsWithoutPGOverride(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_source_curation_update_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
const renamedID = "pg-source-curation-rename-001"
sourceNameOne := "Source name one"
sourceNameTwo := "Source name two"
renamed := db.Session{
ID: renamedID,
Project: "test-proj",
Machine: "test-machine",
Agent: "claude",
MessageCount: 1,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}
require.NoError(t, localDB.UpsertSession(renamed), "UpsertSession renamed")
require.NoError(t, localDB.RenameSession(renamedID, &sourceNameOne),
"RenameSession renamed initial source name")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: renamedID,
Ordinal: 0,
Role: "user",
Content: renamedID,
ContentLength: len(renamedID),
}}), "InsertMessages renamed")
const restoredID = "pg-source-curation-restore-001"
restored := db.Session{
ID: restoredID,
Project: "test-proj",
Machine: "test-machine",
Agent: "claude",
MessageCount: 1,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:01Z",
}
require.NoError(t, localDB.UpsertSession(restored), "UpsertSession restored")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: restoredID,
Ordinal: 0,
Role: "user",
Content: restoredID,
ContentLength: len(restoredID),
}}), "InsertMessages restored")
require.NoError(t, localDB.SoftDeleteSession(restoredID),
"SoftDeleteSession restored")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push initial source curation")
require.NoError(t, localDB.RenameSession(renamedID, &sourceNameTwo),
"RenameSession renamed source update")
restoredCount, err := localDB.RestoreSession(restoredID)
require.NoError(t, err, "RestoreSession restoredID")
assert.EqualValues(t, 1, restoredCount)
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push source curation update")
var gotDisplayName sql.NullString
require.NoError(t, pg.QueryRow(
`SELECT display_name FROM sessions WHERE id = $1`,
renamedID,
).Scan(&gotDisplayName), "read renamed display_name")
require.True(t, gotDisplayName.Valid, "display_name should remain populated")
assert.Equal(t, sourceNameTwo, gotDisplayName.String)
var deletedAt sql.NullTime
require.NoError(t, pg.QueryRow(
`SELECT deleted_at FROM sessions WHERE id = $1`,
restoredID,
).Scan(&deletedAt), "read restored deleted_at")
assert.False(t, deletedAt.Valid, "source restore should clear deleted_at")
}
func TestPushPreservesLegacyPGCurationWithoutSourceBaseline(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_legacy_source_curation_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
const renamedID = "pg-legacy-curation-rename-001"
sourceNameOne := "Source name one"
sourceNameTwo := "Source name two"
renamed := db.Session{
ID: renamedID,
Project: "test-proj",
Machine: "test-machine",
Agent: "claude",
MessageCount: 1,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}
require.NoError(t, localDB.UpsertSession(renamed), "UpsertSession renamed")
require.NoError(t, localDB.RenameSession(renamedID, &sourceNameOne),
"RenameSession renamed initial source name")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: renamedID,
Ordinal: 0,
Role: "user",
Content: renamedID,
ContentLength: len(renamedID),
}}), "InsertMessages renamed")
const trashedID = "pg-legacy-curation-trash-001"
trashed := db.Session{
ID: trashedID,
Project: "test-proj",
Machine: "test-machine",
Agent: "claude",
MessageCount: 1,
UserMessageCount: 1,
CreatedAt: "2026-01-01T00:00:01Z",
}
require.NoError(t, localDB.UpsertSession(trashed), "UpsertSession trashed")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: trashedID,
Ordinal: 0,
Role: "user",
Content: trashedID,
ContentLength: len(trashedID),
}}), "InsertMessages trashed")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push initial legacy source rows")
pgName := "PG curated name"
_, err = pg.Exec(
`UPDATE sessions
SET display_name = $2,
source_display_name = NULL
WHERE id = $1`,
renamedID, pgName,
)
require.NoError(t, err, "simulate legacy PG rename")
_, err = pg.Exec(
`UPDATE sessions
SET deleted_at = NOW(),
source_deleted_at = NULL
WHERE id = $1`,
trashedID,
)
require.NoError(t, err, "simulate legacy PG trash")
require.NoError(t, localDB.RenameSession(renamedID, &sourceNameTwo),
"RenameSession renamed source update")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push legacy source curation update")
var gotDisplayName sql.NullString
var gotSourceDisplayName sql.NullString
require.NoError(t, pg.QueryRow(
`SELECT display_name, source_display_name
FROM sessions WHERE id = $1`,
renamedID,
).Scan(&gotDisplayName, &gotSourceDisplayName),
"read renamed legacy curation state")
require.True(t, gotDisplayName.Valid, "legacy display_name should remain populated")
assert.Equal(t, pgName, gotDisplayName.String)
require.True(t, gotSourceDisplayName.Valid,
"legacy source_display_name should capture the source baseline")
assert.Equal(t, sourceNameTwo, gotSourceDisplayName.String)
var deletedAt sql.NullTime
var sourceDeletedAt sql.NullTime
require.NoError(t, pg.QueryRow(
`SELECT deleted_at, source_deleted_at
FROM sessions WHERE id = $1`,
trashedID,
).Scan(&deletedAt, &sourceDeletedAt),
"read trashed legacy curation state")
assert.True(t, deletedAt.Valid,
"legacy PG delete should remain in place without a source baseline")
assert.False(t, sourceDeletedAt.Valid,
"active source row should keep a null source_deleted_at baseline")
}
// TestPushSyncsUsageEventsForZeroMessageSession verifies that a session
// carrying token/cost accounting as a usage_event but no transcript
// messages still has its usage_event pushed to PG. This is the shape of a
// hermes state.db-only session: parseHermesStateSession emits a single
// usage_event (model + tokens) with MessageCount 0. The session row (and
// its aggregate token columns) pushes via pushSession, but pushMessages
// must not skip usage_event syncing just because the message count is 0 --
// otherwise the dashboard shows tokens with a $0 cost.
func TestPushSyncsUsageEventsForZeroMessageSession(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_zeromsg_usage_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
const sessID = "hermes:zero-msg-001"
started := "2026-05-26T10:00:00Z"
sess := db.Session{
ID: sessID,
Project: "hermes-proj",
Machine: "test-machine",
Agent: "hermes",
MessageCount: 0,
StartedAt: &started,
CreatedAt: started,
TotalOutputTokens: 500000,
HasTotalOutputTokens: true,
}
require.NoError(t, localDB.UpsertSession(sess), "UpsertSession")
// gpt-5.5 usage event with NULL cost so it is priced from the catalog.
require.NoError(t, localDB.ReplaceSessionUsageEvents(sessID, []db.UsageEvent{{
SessionID: sessID,
Source: "session",
Model: "gpt-5.5",
InputTokens: 1000000,
OutputTokens: 500000,
CostUSD: nil,
OccurredAt: started,
DedupKey: "session:" + sessID,
}}), "ReplaceSessionUsageEvents")
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push")
// The usage_event must reach PG even though the session has no messages.
var pgUsageCount int
require.NoError(t, pg.QueryRow(
`SELECT COUNT(*) FROM usage_events WHERE session_id = $1`,
sessID,
).Scan(&pgUsageCount), "count pg usage_events")
assert.Equal(t, 1, pgUsageCount,
"usage_event for a zero-message session was not pushed")
// And the read side prices it from the gpt-5.5 catalog rate:
// input 5/Mtok, output 30/Mtok -> 1.0*5 + 0.5*30 = 20.
store, err := NewStore(pgURL, schema, true)
require.NoError(t, err, "NewStore")
defer store.Close()
result, err := store.GetDailyUsage(ctx, db.UsageFilter{
From: "2026-05-26",
To: "2026-05-26",
Timezone: "UTC",
})
require.NoError(t, err, "GetDailyUsage")
assert.InDelta(t, 20.0, result.Totals.TotalCost, 1e-9,
"gpt-5.5 usage should be priced from the catalog")
}
func TestPushSyncsCursorUsageEventsIntoPGDailyUsage(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_cursor_usage_pg_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "sessions.db"))
require.NoError(t, err, "open local db")
defer localDB.Close()
require.NoError(t, localDB.UpsertModelPricing([]db.ModelPricing{{
ModelPattern: "claude-4.6-opus-high-thinking",
InputPerMTok: 5.0,
OutputPerMTok: 25.0,
CacheCreationPerMTok: 6.25,
CacheReadPerMTok: 0.5,
}}), "UpsertModelPricing")
require.NoError(t, localDB.InsertCursorUsageEvents([]db.CursorUsageEvent{{
OccurredAt: "2026-05-14T10:05:00Z",
Model: "claude-4.6-opus-high-thinking",
Kind: "USAGE_EVENT_KIND_USAGE_BASED",
InputTokens: 1234,
OutputTokens: 567,
CacheWriteTokens: 12,
CacheReadTokens: 34,
ChargedCents: 15.66,
CursorTokenFee: 3.32,
UserID: "152683922",
UserEmail: "member@example.com",
IsHeadless: false,
}}), "InsertCursorUsageEvents")
sync := &Sync{
local: localDB,
pg: pg,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push")
assert.Equal(t, 1, pgTableCount(t, ctx, pg, "cursor_usage_events"))
store, err := NewStore(pgURL, schema, true)
require.NoError(t, err, "NewStore")
defer store.Close()
result, err := store.GetDailyUsage(ctx, db.UsageFilter{
From: "2026-05-14",
To: "2026-05-14",
Timezone: "UTC",
Breakdowns: true,
})
require.NoError(t, err, "GetDailyUsage")
require.Len(t, result.Daily, 1, "daily entries")
assert.Equal(t, 1234, result.Daily[0].InputTokens)
assert.Equal(t, 567, result.Daily[0].OutputTokens)
assert.Equal(t, 12, result.Daily[0].CacheCreationTokens)
assert.Equal(t, 34, result.Daily[0].CacheReadTokens)
assert.InDelta(t, 0.1566, result.Daily[0].TotalCost, 1e-9)
assert.Empty(t, result.Projects, "cursor-only usage should not emit project identities")
assert.NotContains(t, result.Projects, "")
assert.Equal(t, 0, result.SessionCounts.Total)
assert.Empty(t, result.SessionCounts.ByAgent)
assert.Empty(t, result.SessionCounts.ByProject)
require.Len(t, result.Daily[0].AgentBreakdowns, 1)
assert.Equal(t, "cursor", result.Daily[0].AgentBreakdowns[0].Agent)
}
func TestPushCursorUsageEventsPreservesRowsFromOtherMachines(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_cursor_usage_append_only_pg_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
_, err = pg.ExecContext(ctx, `
INSERT INTO cursor_usage_events (
occurred_at, model, kind,
input_tokens, output_tokens,
cache_write_tokens, cache_read_tokens,
charged_cents, cursor_token_fee,
user_id, user_email, is_headless, dedup_key
) VALUES (
'2026-05-14T09:05:00Z'::timestamptz,
'claude-4.6-opus-high-thinking',
'USAGE_EVENT_KIND_USAGE_BASED',
10, 20, 0, 30,
1.25, 0.25,
'other-user', 'other@example.com', false, 'other-machine-row'
)`)
require.NoError(t, err, "seed existing pg row")
localDB, err := db.Open(filepath.Join(t.TempDir(), "sessions.db"))
require.NoError(t, err, "open local db")
defer localDB.Close()
require.NoError(t, localDB.InsertCursorUsageEvents([]db.CursorUsageEvent{{
OccurredAt: "2026-05-14T10:05:00Z",
Model: "claude-4.6-opus-high-thinking",
Kind: "USAGE_EVENT_KIND_USAGE_BASED",
InputTokens: 1234,
OutputTokens: 567,
CacheWriteTokens: 12,
CacheReadTokens: 34,
ChargedCents: 15.66,
CursorTokenFee: 3.32,
UserID: "152683922",
UserEmail: "member@example.com",
IsHeadless: false,
DedupKey: "local-machine-row",
}}), "InsertCursorUsageEvents")
sync := &Sync{
local: localDB,
pg: pg,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
_, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push")
assert.Equal(t, 2, pgTableCount(t, ctx, pg, "cursor_usage_events"))
}
// checkIsSystem asserts that PG contains exactly wantTotal rows for the
// session with ordinals 0..wantTotal-1, and that each row's is_system
// matches wantSystem. Tracking the exact ordinal set prevents false
// positives from wrong-but-equal-count row sets.
func checkIsSystem(
t *testing.T,
pg *sql.DB,
sessID string,
wantSystem map[int]bool,
wantTotal int,
) {
t.Helper()
rows, err := pg.Query(
`SELECT ordinal, is_system FROM messages
WHERE session_id = $1 ORDER BY ordinal`,
sessID,
)
require.NoError(t, err, "querying PG messages")
defer rows.Close()
seen := make(map[int]bool, wantTotal)
for rows.Next() {
var ordinal int
var isSystem bool
require.NoError(t, rows.Scan(&ordinal, &isSystem), "scanning row")
seen[ordinal] = true
want := wantSystem[ordinal]
assert.Equal(t, want, isSystem, "ordinal %d is_system", ordinal)
}
require.NoError(t, rows.Err(), "rows error")
assert.Len(t, seen, wantTotal,
"PG has %d message rows for session %s, want %d",
len(seen), sessID, wantTotal)
// Verify every expected ordinal was present (no gaps or substitutions).
for i := range wantTotal {
assert.True(t, seen[i], "ordinal %d missing from PG messages", i)
}
}
func assertPGMessageContent(
t *testing.T,
pg *sql.DB,
sessionID string,
ordinal int,
want string,
) {
t.Helper()
var got string
require.NoError(t, pg.QueryRow(
`SELECT content FROM messages
WHERE session_id = $1 AND ordinal = $2`,
sessionID, ordinal,
).Scan(&got), "read pg message content")
assert.Equal(t, want, got)
}
func assertPGMessageThinking(
t *testing.T,
pg *sql.DB,
sessionID string,
ordinal int,
wantHasThinking bool,
wantThinkingText string,
) {
t.Helper()
var gotHasThinking bool
var gotThinkingText string
require.NoError(t, pg.QueryRow(
`SELECT has_thinking, thinking_text FROM messages
WHERE session_id = $1 AND ordinal = $2`,
sessionID, ordinal,
).Scan(&gotHasThinking, &gotThinkingText), "read pg message thinking")
assert.Equal(t, wantHasThinking, gotHasThinking)
assert.Equal(t, wantThinkingText, gotThinkingText)
}
func assertPGMessageTimestamp(
t *testing.T,
pg *sql.DB,
sessionID string,
ordinal int,
want string,
) {
t.Helper()
var gotTime sql.NullTime
require.NoError(t, pg.QueryRow(
`SELECT timestamp FROM messages
WHERE session_id = $1 AND ordinal = $2`,
sessionID, ordinal,
).Scan(&gotTime), "read pg message timestamp")
require.True(t, gotTime.Valid, "timestamp should be non-NULL")
assert.Equal(t, want, FormatISO8601(gotTime.Time))
}
func pgMessageCTID(
t *testing.T,
pg *sql.DB,
sessionID string,
ordinal int,
) string {
t.Helper()
var ctid string
require.NoError(t, pg.QueryRow(
`SELECT ctid::text FROM messages
WHERE session_id = $1 AND ordinal = $2`,
sessionID, ordinal,
).Scan(&ctid), "read pg message ctid")
return ctid
}
// TestPushMessagesSanitizesNULBytes verifies that a message whose
// model and source fields carry NUL bytes (observed in production:
// the Antigravity gen_metadata heuristic persisted a raw protobuf
// fragment as the model name) pushes to PG without the whole-session
// rollback caused by SQLSTATE 22021 (invalid byte sequence for
// encoding "UTF8": 0x00). Model and source fields come from
// third-party session files, so the push boundary must be defensive
// regardless of any single parser fix.
func TestPushMessagesSanitizesNULBytes(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_nul_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(
filepath.Join(t.TempDir(), "local.db"),
)
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "test-machine",
schema: schema,
schemaDone: true,
}
// The exact 10-byte protobuf fragment persisted as a model
// name by the pre-fix Antigravity parser (hex
// 080020022A0201024001, contains 0x00).
badModel := "\x08\x00\x20\x02\x2a\x02\x01\x02\x40\x01"
const sessID = "nul-bytes-001"
sess := db.Session{
ID: sessID,
Project: "test-proj",
Machine: "test-machine",
Agent: "antigravity",
MessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
Cwd: "/tmp/with\x00nul",
GitBranch: "main\x00",
}
require.NoError(t, localDB.UpsertSession(sess), "UpsertSession")
msgs := []db.Message{{
SessionID: sessID,
Ordinal: 0,
Role: "assistant",
Content: "thinking summary",
ContentLength: 16,
Model: badModel,
SourceUUID: "uuid\x00tail",
}}
require.NoError(t, localDB.InsertMessages(msgs), "InsertMessages")
res, err := sync.Push(ctx, false, nil)
require.NoError(t, err, "Push")
assert.Zero(t, res.Errors, "push should report no failed sessions")
var model, sourceUUID string
err = pg.QueryRow(
`SELECT model, source_uuid FROM messages
WHERE session_id = $1 AND ordinal = 0`, sessID,
).Scan(&model, &sourceUUID)
require.NoError(t, err, "querying pushed message")
assert.Equal(t, sanitizePG(badModel), model, "model stripped of NUL")
assert.NotContains(t, model, "\x00")
assert.Equal(t, "uuidtail", sourceUUID, "source_uuid stripped of NUL")
// Second push with sync state cleared: the local token
// fingerprint (sanitized, see db.SanitizeUTF8) must match the
// PG-readback fingerprint despite the NUL bytes still stored
// locally, so the metadata fast path skips the rewrite. ctid
// changes on DELETE+reinsert, so an unchanged ctid proves the
// row was left alone.
var ctidBefore string
require.NoError(t, pg.QueryRow(
`SELECT ctid::text FROM messages
WHERE session_id = $1 AND ordinal = 0`, sessID,
).Scan(&ctidBefore), "reading ctid before second push")
require.NoError(t, localDB.SetSyncState("last_push_at", ""),
"clearing last_push_at")
require.NoError(t,
localDB.SetSyncState(lastPushBoundaryStateKey, ""),
"clearing boundary state")
res, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "Push (second)")
assert.Zero(t, res.Errors, "second push should report no failures")
var ctidAfter string
require.NoError(t, pg.QueryRow(
`SELECT ctid::text FROM messages
WHERE session_id = $1 AND ordinal = 0`, sessID,
).Scan(&ctidAfter), "reading ctid after second push")
assert.Equal(t, ctidBefore, ctidAfter,
"fast path should skip rewriting a NUL-field session")
}
// TestPushIncrementalWithOnlyForeignMachineSessions verifies reset detection
// does not misfire when every local session carries a machine value other than
// s.machine (e.g. orphan-copied sessions kept from a previous machine name).
// The push marker, not a per-machine row count, drives reset detection, so the
// second incremental push is a no-op rather than a forced full re-push.
func TestPushIncrementalWithOnlyForeignMachineSessions(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_foreign_machine_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "new-host",
schema: schema,
schemaDone: true,
}
const sessID = "foreign-machine-001"
sess := db.Session{
ID: sessID,
Project: "proj",
Machine: "old-host",
Agent: "claude",
MessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}
require.NoError(t, localDB.UpsertSession(sess), "UpsertSession")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sessID,
Ordinal: 0,
Role: "assistant",
Content: "hello",
ContentLength: 5,
}}), "InsertMessages")
res, err := sync.Push(ctx, false, nil)
require.NoError(t, err, "first Push")
assert.Zero(t, res.Errors, "first push should report no failures")
var machine string
require.NoError(t, pg.QueryRow(
`SELECT machine FROM sessions WHERE id = $1`, sessID,
).Scan(&machine), "reading pushed machine")
require.Equal(t, "old-host", machine, "source machine preserved")
var ctidBefore string
require.NoError(t, pg.QueryRow(
`SELECT ctid::text FROM messages
WHERE session_id = $1 AND ordinal = 0`, sessID,
).Scan(&ctidBefore), "reading ctid before second push")
// Second incremental push with sync state intact. Reset detection counts
// machine = "new-host" plus "old-host", finds the row, and the fingerprint
// fast path skips the session entirely; the unchanged ctid proves the
// message row was left alone instead of being rewritten by a forced full.
res, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "second Push")
assert.Zero(t, res.Errors, "second push should report no failures")
assert.Zero(t, res.SessionsPushed,
"unchanged foreign-machine session should not be re-pushed")
var ctidAfter string
require.NoError(t, pg.QueryRow(
`SELECT ctid::text FROM messages
WHERE session_id = $1 AND ordinal = 0`, sessID,
).Scan(&ctidAfter), "reading ctid after second push")
assert.Equal(t, ctidBefore, ctidAfter,
"second incremental push must not rewrite the session")
}
// TestPushDetectsResetWhenCompetingMachineRowsExist verifies that a PG reset is
// detected even when another pusher has repopulated rows under a machine value
// this host also writes. The local session carries Machine "remote-host" (as a
// remote host's sessions synced in over SSH would); after the first push the PG
// rows and this host's push marker are removed and a competing "remote-host"
// row is inserted, simulating the remote host re-pushing first after a shared
// PG reset. A machine-count check would see the competing row and skip the full
// push, leaving this host's session missing; the push marker is per-pusher, so
// the reset is detected and the session is re-pushed.
func TestPushDetectsResetWhenCompetingMachineRowsExist(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_reset_competing_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "this-host",
schema: schema,
schemaDone: true,
}
const sessID = "remote-host~sess-1"
require.NoError(t, localDB.UpsertSession(db.Session{
ID: sessID,
Project: "proj",
Machine: "remote-host",
Agent: "claude",
MessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}), "UpsertSession")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sessID,
Ordinal: 0,
Role: "assistant",
Content: "hello",
ContentLength: 5,
}}), "InsertMessages")
res, err := sync.Push(ctx, false, nil)
require.NoError(t, err, "first Push")
assert.Zero(t, res.Errors, "first push should report no failures")
// Simulate a PG reset where the real remote host re-pushed first: drop this
// host's rows and its push marker, then insert a competing "remote-host"
// row under a different id.
_, err = pg.Exec(`DELETE FROM sessions WHERE id = $1`, sessID)
require.NoError(t, err, "delete pushed session")
_, err = pg.Exec(
`DELETE FROM sync_metadata WHERE key LIKE 'push_marker:%'`,
)
require.NoError(t, err, "delete push marker")
_, err = pg.Exec(
`INSERT INTO sessions (id, machine, project, agent, created_at)
VALUES ('remote-host-native-1', 'remote-host', 'proj', 'claude', NOW())`,
)
require.NoError(t, err, "insert competing remote-host row")
res, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "second Push")
assert.Zero(t, res.Errors, "second push should report no failures")
var exists bool
require.NoError(t, pg.QueryRow(
`SELECT EXISTS (SELECT 1 FROM sessions WHERE id = $1)`, sessID,
).Scan(&exists), "checking re-pushed session")
assert.True(t, exists,
"reset must be detected and this host's session re-pushed")
}
// TestPushMarkerNotWrittenWhenResetRecoveryFails verifies the push marker is
// written only after a push finalizes. When a reset is detected but the
// recovery push fails before finalization, the marker must stay absent so the
// next push re-detects the reset; otherwise the local watermark would remain at
// the old value while PG holds a fresh marker, and reset-lost sessions would be
// skipped indefinitely.
func TestPushMarkerNotWrittenWhenResetRecoveryFails(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_reset_recovery_fail_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "this-host",
schema: schema,
schemaDone: true,
}
const sessID = "reset-recovery-1"
require.NoError(t, localDB.UpsertSession(db.Session{
ID: sessID,
Project: "proj",
Machine: "this-host",
Agent: "claude",
MessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}), "UpsertSession")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sessID,
Ordinal: 0,
Role: "assistant",
Content: "hello",
ContentLength: 5,
}}), "InsertMessages")
res, err := sync.Push(ctx, false, nil)
require.NoError(t, err, "first Push")
assert.Zero(t, res.Errors, "first push should report no failures")
markerCount := func() int {
var n int
require.NoError(t, pg.QueryRow(
`SELECT COUNT(*) FROM sync_metadata
WHERE key LIKE 'push_marker:%'`,
).Scan(&n), "counting push markers")
return n
}
require.Equal(t, 1, markerCount(), "marker present after first push")
// Simulate a PG reset: drop this host's row and marker, keeping the local
// watermark and boundary state so the session would otherwise be skipped.
_, err = pg.Exec(`DELETE FROM sessions WHERE id = $1`, sessID)
require.NoError(t, err, "delete pushed session")
_, err = pg.Exec(
`DELETE FROM sync_metadata WHERE key LIKE 'push_marker:%'`,
)
require.NoError(t, err, "delete push marker")
require.Equal(t, 0, markerCount(), "marker cleared for reset simulation")
// Sabotage the recovery push so it fails after reset detection but before
// finalization: drop a model_pricing column syncModelPricing reads. The
// reset branch re-runs EnsureSchema, but CREATE TABLE IF NOT EXISTS does
// not re-add a column to an existing table, so the failure persists.
_, err = pg.Exec(
`ALTER TABLE model_pricing DROP COLUMN cache_read_per_mtok`,
)
require.NoError(t, err, "drop model_pricing column")
_, err = sync.Push(ctx, false, nil)
require.Error(t, err, "recovery push should fail at model pricing sync")
assert.Equal(t, 0, markerCount(),
"marker must not be written when recovery push fails")
// Repair the column; the next push must re-detect the reset (marker still
// absent) and re-push the session.
_, err = pg.Exec(
`ALTER TABLE model_pricing
ADD COLUMN cache_read_per_mtok DOUBLE PRECISION NOT NULL DEFAULT 0`,
)
require.NoError(t, err, "restore model_pricing column")
res, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "recovery push after repair")
assert.Zero(t, res.Errors, "repaired push should report no failures")
var exists bool
require.NoError(t, pg.QueryRow(
`SELECT EXISTS (SELECT 1 FROM sessions WHERE id = $1)`, sessID,
).Scan(&exists), "checking re-pushed session")
assert.True(t, exists, "session must be re-pushed after reset recovery")
assert.Equal(t, 1, markerCount(), "marker restored after successful push")
}
// TestPushUpdatesSentinelMachineWhenSyncMachineChanges verifies that a session
// stored with the "local" sentinel machine is re-pushed under the new fallback
// when Sync.machine changes, rather than being skipped by a fingerprint that
// ignored the resolved machine. The second push clears the local watermark so
// the session is re-evaluated; without the resolved machine in the fingerprint
// it would match and be skipped, leaving PG with the stale machine name.
func TestPushUpdatesSentinelMachineWhenSyncMachineChanges(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_sentinel_machine_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "host-a",
schema: schema,
schemaDone: true,
}
const sessID = "sentinel-machine-1"
require.NoError(t, localDB.UpsertSession(db.Session{
ID: sessID,
Project: "proj",
Machine: "local",
Agent: "claude",
MessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}), "UpsertSession")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sessID,
Ordinal: 0,
Role: "assistant",
Content: "hello",
ContentLength: 5,
}}), "InsertMessages")
res, err := sync.Push(ctx, false, nil)
require.NoError(t, err, "first Push")
assert.Zero(t, res.Errors, "first push should report no failures")
machine := func() string {
var m string
require.NoError(t, pg.QueryRow(
`SELECT machine FROM sessions WHERE id = $1`, sessID,
).Scan(&m), "reading machine")
return m
}
require.Equal(t, "host-a", machine(), "sentinel pushed under host-a")
// Rename: change the fallback machine and re-evaluate the session by
// clearing the watermark, mirroring any path that re-lists it.
sync.machine = "host-b"
require.NoError(t, localDB.SetSyncState("last_push_at", ""),
"clearing last_push_at")
res, err = sync.Push(ctx, false, nil)
require.NoError(t, err, "second Push")
assert.Zero(t, res.Errors, "second push should report no failures")
assert.Equal(t, "host-b", machine(),
"sentinel machine must follow the new fallback")
}
func TestPushAdoptsOwnerlessRowsFromPreviousMarkerMachine(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_legacy_marker_machine_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
const markerID = "legacy-marker-1"
require.NoError(t, localDB.SetSyncState("pg_push_marker_id", markerID),
"seed local push marker")
sync := &Sync{
pg: pg,
local: localDB,
machine: "host-b",
schema: schema,
schemaDone: true,
}
const sessID = "legacy-previous-machine-1"
require.NoError(t, localDB.UpsertSession(db.Session{
ID: sessID,
Project: "proj",
Machine: "host-b",
Agent: "claude",
MessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}), "UpsertSession")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sessID,
Ordinal: 0,
Role: "assistant",
Content: "hello",
ContentLength: 5,
}}), "InsertMessages")
_, err = pg.ExecContext(ctx, `
INSERT INTO sync_metadata (key, value)
VALUES ($1, $2)
`, pushMarkerKeyPrefix+markerID, "host-a")
require.NoError(t, err, "seed previous marker machine")
_, err = pg.ExecContext(ctx, `
INSERT INTO sessions (
id, machine, owner_marker, project, agent, created_at
) VALUES ($1, $2, $3, $4, $5, NOW())
`, sessID, "host-a", "", "proj", "claude")
require.NoError(t, err, "seed ownerless legacy session")
res, err := sync.Push(ctx, true, nil)
require.NoError(t, err, "Push")
assert.Zero(t, res.Errors, "push should report no failed sessions")
assert.Zero(t, res.SkippedConflicts,
"previous-marker-machine legacy row should be adopted")
assert.Equal(t, 1, res.SessionsPushed,
"legacy row should be counted as pushed")
var machine, ownerMarker string
require.NoError(t, pg.QueryRow(
`SELECT machine, owner_marker FROM sessions WHERE id = $1`, sessID,
).Scan(&machine, &ownerMarker), "reading adopted row")
assert.Equal(t, "host-b", machine)
assert.Equal(t, markerID, ownerMarker)
var markerMachine string
require.NoError(t, pg.QueryRow(
`SELECT value FROM sync_metadata WHERE key = $1`,
pushMarkerKeyPrefix+markerID,
).Scan(&markerMachine), "reading marker machine")
assert.Equal(t, "host-b", markerMachine)
var aliases string
require.NoError(t, pg.QueryRow(
`SELECT value FROM sync_metadata WHERE key = $1`,
pushMarkerMachineAliasesKeyPrefix+markerID,
).Scan(&aliases), "reading marker aliases")
assert.JSONEq(t, `["host-a"]`, aliases)
const laterSessID = "legacy-previous-machine-2"
require.NoError(t, localDB.UpsertSession(db.Session{
ID: laterSessID,
Project: "proj",
Machine: "host-b",
Agent: "claude",
MessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}), "UpsertSession later legacy row")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: laterSessID,
Ordinal: 0,
Role: "assistant",
Content: "later",
ContentLength: 5,
}}), "InsertMessages later legacy row")
_, err = pg.ExecContext(ctx, `
INSERT INTO sessions (
id, machine, owner_marker, project, agent, created_at
) VALUES ($1, $2, $3, $4, $5, NOW())
`, laterSessID, "host-a", "", "proj", "claude")
require.NoError(t, err, "seed later ownerless legacy session")
res, err = sync.Push(ctx, true, nil)
require.NoError(t, err, "second Push")
assert.Zero(t, res.Errors, "second push should report no failed sessions")
assert.Zero(t, res.SkippedConflicts,
"preserved marker alias should adopt later legacy row")
require.NoError(t, pg.QueryRow(
`SELECT machine, owner_marker FROM sessions WHERE id = $1`,
laterSessID,
).Scan(&machine, &ownerMarker), "reading later adopted row")
assert.Equal(t, "host-b", machine)
assert.Equal(t, markerID, ownerMarker)
}
func TestFilteredPushAdoptsOwnerlessRowsFromLegacyUnscopedMarker(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_filtered_legacy_marker_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
const markerID = "legacy-marker-filtered-1"
require.NoError(t, localDB.SetSyncState("pg_push_marker_id", markerID),
"seed local push marker")
projects := []string{"proj"}
scope := pushSyncStateScope("team-target", projects, nil)
sync := &Sync{
pg: pg,
local: localDB,
syncState: newScopedSyncStateStore(localDB, scope, false),
machine: "host-b",
schema: schema,
schemaDone: true,
syncStateTarget: scope,
projects: projects,
}
require.NoError(t, sync.effectiveSyncState().SetSyncState(
"last_push_at", "2025-12-31T00:00:00.000Z",
), "seed scoped local push state")
const sessID = "legacy-filtered-previous-machine-1"
require.NoError(t, localDB.UpsertSession(db.Session{
ID: sessID,
Project: "proj",
Machine: "host-b",
Agent: "claude",
MessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}), "UpsertSession")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sessID,
Ordinal: 0,
Role: "assistant",
Content: "hello",
ContentLength: 5,
}}), "InsertMessages")
_, err = pg.ExecContext(ctx, `
INSERT INTO sync_metadata (key, value)
VALUES ($1, $2)
`, pushMarkerKeyPrefix+markerID, "host-a")
require.NoError(t, err, "seed legacy unscoped marker machine")
_, err = pg.ExecContext(ctx, `
INSERT INTO sessions (
id, machine, owner_marker, project, agent, created_at
) VALUES ($1, $2, $3, $4, $5, NOW())
`, sessID, "host-a", "", "proj", "claude")
require.NoError(t, err, "seed ownerless legacy session")
res, err := sync.Push(ctx, false, nil)
require.NoError(t, err, "Push")
assert.Zero(t, res.Errors, "push should report no failed sessions")
assert.Zero(t, res.SkippedConflicts,
"legacy unscoped marker machine should be used as an alias")
assert.Equal(t, 1, res.SessionsPushed,
"legacy row should be counted as pushed")
var machine, ownerMarker string
require.NoError(t, pg.QueryRow(
`SELECT machine, owner_marker FROM sessions WHERE id = $1`, sessID,
).Scan(&machine, &ownerMarker), "reading adopted row")
assert.Equal(t, "host-b", machine)
assert.Equal(t, markerID, ownerMarker)
var markerMachine string
require.NoError(t, pg.QueryRow(
`SELECT value FROM sync_metadata WHERE key = $1`,
sync.pushMarkerMetadataKey(pushMarkerKeyPrefix, markerID),
).Scan(&markerMachine), "reading scoped marker machine")
assert.Equal(t, "host-b", markerMachine)
var aliases string
require.NoError(t, pg.QueryRow(
`SELECT value FROM sync_metadata WHERE key = $1`,
sync.pushMarkerMetadataKey(
pushMarkerMachineAliasesKeyPrefix, markerID,
),
).Scan(&aliases), "reading scoped marker aliases")
assert.JSONEq(t, `["host-a"]`, aliases)
}
func TestPushReportsSkippedConflicts(t *testing.T) {
pgURL := testPGURL(t)
const schema = "agentsview_push_skipped_conflicts_test"
pg, err := Open(pgURL, schema, true)
require.NoError(t, err, "Open")
defer pg.Close()
ctx := context.Background()
_, err = pg.Exec(`DROP SCHEMA IF EXISTS ` + schema + ` CASCADE`)
require.NoError(t, err, "drop schema")
require.NoError(t, EnsureSchema(ctx, pg, schema), "EnsureSchema")
localDB, err := db.Open(filepath.Join(t.TempDir(), "local.db"))
require.NoError(t, err, "db.Open")
defer localDB.Close()
sync := &Sync{
pg: pg,
local: localDB,
machine: "machine-b",
schema: schema,
schemaDone: true,
}
const sessID = "conflict-001"
require.NoError(t, localDB.UpsertSession(db.Session{
ID: sessID,
Project: "proj",
Machine: "machine-b",
Agent: "claude",
MessageCount: 1,
CreatedAt: "2026-01-01T00:00:00Z",
}), "UpsertSession")
require.NoError(t, localDB.InsertMessages([]db.Message{{
SessionID: sessID,
Ordinal: 0,
Role: "assistant",
Content: "hello",
ContentLength: 5,
}}), "InsertMessages")
_, err = pg.ExecContext(ctx, `
INSERT INTO sessions (
id, machine, owner_marker, project, agent, created_at
) VALUES ($1, $2, $3, $4, $5, NOW())
`, sessID, "machine-a", "other-owner", "proj", "claude")
require.NoError(t, err, "insert conflicting owner")
res, err := sync.Push(ctx, false, nil)
require.NoError(t, err, "Push")
assert.Zero(t, res.Errors, "push should not report failed sessions")
assert.Zero(t, res.SessionsPushed, "conflicting session should not be counted as pushed")
assert.Equal(t, 1, res.SkippedConflicts, "skipped conflicts should be observable in PushResult")
}