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

282 lines
8.0 KiB
Go

//go:build pgtest
package postgres
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.kenn.io/agentsview/internal/db"
)
// TestPGPushSecrets verifies that pg push carries secret_leak_count
// and secret_findings rows to PostgreSQL. It also verifies idempotency
// (delete-before-insert prevents duplicates on re-push).
func TestPGPushSecrets(t *testing.T) {
pgURL := testPGURL(t)
cleanPGSchema(t, pgURL)
t.Cleanup(func() { cleanPGSchema(t, pgURL) })
local := testDB(t)
ps, err := New(
pgURL, "agentsview", local, "machine-secrets", true,
SyncOptions{},
)
require.NoError(t, err, "creating sync")
defer ps.Close()
ctx := context.Background()
require.NoError(t, ps.EnsureSchema(ctx), "ensure schema")
// Seed a session with a message.
started := time.Now().UTC().Format(time.RFC3339)
firstMsg := "push secrets test"
sess := db.Session{
ID: "secrets-sess-001",
Project: "secrets-project",
Machine: "local",
Agent: "claude",
FirstMessage: &firstMsg,
StartedAt: &started,
MessageCount: 1,
}
require.NoError(t, local.UpsertSession(sess), "upsert session")
require.NoError(t, local.InsertMessages([]db.Message{{
SessionID: "secrets-sess-001",
Ordinal: 0,
Role: "user",
Content: firstMsg,
}}), "insert message")
// Add a secret finding to the session.
findings := []db.SecretFinding{
{
SessionID: "secrets-sess-001",
RuleName: "aws-access-key",
Confidence: "definite",
LocationKind: "message",
MessageOrdinal: 0,
MatchStart: 5,
MatchEnd: 25,
MatchIndex: 0,
RedactedMatch: "AKIA[REDACTED]",
RulesVersion: "v1.0",
},
}
require.NoError(t, local.ReplaceSessionSecretFindings(
"secrets-sess-001", findings, 1, "v1.0",
), "replace secret findings")
// Push to PG.
pushResult, err := ps.Push(ctx, false, nil)
require.NoError(t, err, "push")
require.Equal(t, 1, pushResult.SessionsPushed)
// Open a read Store and verify via ListSessions + ListSecretFindings.
store, err := NewStore(pgURL, "agentsview", true)
require.NoError(t, err, "opening store")
defer store.Close()
// ListSessions with HasSecret=true must return the session with
// SecretLeakCount=1.
page, err := store.ListSessions(ctx, db.SessionFilter{
HasSecret: true, Limit: 10,
})
require.NoError(t, err, "ListSessions HasSecret")
require.Len(t, page.Sessions, 1)
assert.Equal(t, 1, page.Sessions[0].SecretLeakCount)
// ListSecretFindings must return the pushed finding with
// Project and Agent populated.
fpage, err := store.ListSecretFindings(ctx, db.SecretFindingFilter{
Limit: 10,
})
require.NoError(t, err, "ListSecretFindings")
require.Len(t, fpage.Findings, 1)
f := fpage.Findings[0]
assert.Equal(t, "aws-access-key", f.RuleName)
assert.Equal(t, "secrets-project", f.Project)
assert.Equal(t, "claude", f.Agent)
assert.Equal(t, "AKIA[REDACTED]", f.RedactedMatch)
// Second push with no changes must be a no-op (fingerprint
// unchanged) and must not duplicate findings.
r2, err := ps.Push(ctx, false, nil)
require.NoError(t, err, "second push")
assert.Equal(t, 0, r2.SessionsPushed, "second push should be no-op")
fpage2, err := store.ListSecretFindings(ctx, db.SecretFindingFilter{
Limit: 10,
})
require.NoError(t, err, "ListSecretFindings after re-push")
assert.Len(t, fpage2.Findings, 1, "no duplicate findings after re-push")
}
// TestPushSecretFindingsReportsChange verifies pushSecretFindings reports
// whether it changed any rows, so the caller can bump sessions.updated_at
// for secret-only changes that pushSession and pushMessages would miss.
func TestPushSecretFindingsReportsChange(t *testing.T) {
pgURL := testPGURL(t)
cleanPGSchema(t, pgURL)
t.Cleanup(func() { cleanPGSchema(t, pgURL) })
local := testDB(t)
ps, err := New(
pgURL, "agentsview", local, "machine-findings-change", true,
SyncOptions{},
)
require.NoError(t, err, "creating sync")
defer ps.Close()
ctx := context.Background()
require.NoError(t, ps.EnsureSchema(ctx), "ensure schema")
// Seed a session (no findings yet) and push it so the PG row exists
// for the secret_findings foreign key.
started := time.Now().UTC().Format(time.RFC3339)
firstMsg := "findings change test"
const sessID = "findings-change-001"
sess := db.Session{
ID: sessID,
Project: "findings-project",
Machine: "local",
Agent: "claude",
FirstMessage: &firstMsg,
StartedAt: &started,
MessageCount: 1,
}
require.NoError(t, local.UpsertSession(sess), "upsert session")
require.NoError(t, local.InsertMessages([]db.Message{{
SessionID: sessID,
Ordinal: 0,
Role: "user",
Content: firstMsg,
}}), "insert message")
_, err = ps.Push(ctx, false, nil)
require.NoError(t, err, "seed push")
// pushOnce runs pushSecretFindings in its own transaction and returns
// whether it reported a change.
pushOnce := func() bool {
tx, err := ps.pg.BeginTx(ctx, nil)
require.NoError(t, err, "begin tx")
changed, err := ps.pushSecretFindings(ctx, tx, sessID)
if err != nil {
_ = tx.Rollback()
t.Fatalf("pushSecretFindings: %v", err)
}
require.NoError(t, tx.Commit(), "commit tx")
return changed
}
finding := db.SecretFinding{
SessionID: sessID,
RuleName: "aws-access-key",
Confidence: "definite",
LocationKind: "message",
MessageOrdinal: 0,
MatchStart: 5,
MatchEnd: 25,
MatchIndex: 0,
RedactedMatch: "AKIA[REDACTED]",
RulesVersion: "v1.0",
}
// No PG rows, no local findings: nothing changes.
assert.False(t, pushOnce(), "empty -> empty reported a change")
// Local gains a finding: the insert is a change.
require.NoError(t, local.ReplaceSessionSecretFindings(
sessID, []db.SecretFinding{finding}, 1, "v1.0",
), "seed finding")
assert.True(t, pushOnce(), "insert should report change")
// Re-pushing the same finding still rewrites rows (delete + insert),
// which counts as a change.
assert.True(t, pushOnce(), "rewrite should report change")
// Clearing local findings deletes the PG row: that is a change.
require.NoError(t, local.ReplaceSessionSecretFindings(
sessID, nil, 0, "v1.0",
), "clear findings")
assert.True(t, pushOnce(), "delete should report change")
// Back to empty on both sides: nothing changes.
assert.False(t, pushOnce(), "post-clear empty -> empty reported a change")
}
func TestPGConnectivity(t *testing.T) {
pgURL := testPGURL(t)
local := testDB(t)
ps, err := New(
pgURL, "agentsview", local,
"connectivity-test-machine", true,
SyncOptions{},
)
require.NoError(t, err, "creating sync")
defer ps.Close()
ctx, cancel := context.WithTimeout(
context.Background(), 10*time.Second,
)
defer cancel()
require.NoError(t, ps.EnsureSchema(ctx), "ensure schema")
status, err := ps.Status(ctx)
require.NoError(t, err, "get status")
t.Logf("PG Sync Status: %+v", status)
}
func TestPGPushCycle(t *testing.T) {
pgURL := testPGURL(t)
cleanPGSchema(t, pgURL)
t.Cleanup(func() { cleanPGSchema(t, pgURL) })
local := testDB(t)
ps, err := New(
pgURL, "agentsview", local, "machine-a", true,
SyncOptions{},
)
require.NoError(t, err, "creating sync")
defer ps.Close()
ctx := context.Background()
require.NoError(t, ps.EnsureSchema(ctx), "ensure schema")
started := time.Now().UTC().Format(time.RFC3339)
firstMsg := "hello from pg"
sess := db.Session{
ID: "pg-sess-001",
Project: "pg-project",
Machine: "local",
Agent: "test-agent",
FirstMessage: &firstMsg,
StartedAt: &started,
MessageCount: 1,
}
require.NoError(t, local.UpsertSession(sess), "upsert session")
require.NoError(t, local.InsertMessages([]db.Message{{
SessionID: "pg-sess-001",
Ordinal: 0,
Role: "user",
Content: firstMsg,
}}), "insert message")
pushResult, err := ps.Push(ctx, false, nil)
require.NoError(t, err, "push")
require.Equal(t, 1, pushResult.SessionsPushed)
require.Equal(t, 1, pushResult.MessagesPushed)
status, err := ps.Status(ctx)
require.NoError(t, err, "status")
assert.Equal(t, 1, status.PGSessions)
assert.Equal(t, 1, status.PGMessages)
}