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
343 lines
11 KiB
Go
343 lines
11 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.kenn.io/agentsview/internal/config"
|
|
"go.kenn.io/agentsview/internal/db"
|
|
"go.kenn.io/agentsview/internal/dbtest"
|
|
)
|
|
|
|
func TestDoctorSyncCurrentDatabaseReportsNormalStartupSync(t *testing.T) {
|
|
dataDir := testDataDir(t)
|
|
|
|
database, err := db.Open(filepath.Join(dataDir, "sessions.db"))
|
|
require.NoError(t, err, "open db")
|
|
require.NoError(t, database.Close(), "close db")
|
|
|
|
out, err := executeCommand(newRootCommand(), "doctor", "sync")
|
|
require.NoError(t, err, "doctor sync")
|
|
_, statErr := os.Stat(filepath.Join(dataDir, "config.toml"))
|
|
require.ErrorIs(t, statErr, os.ErrNotExist,
|
|
"doctor sync must not create config.toml")
|
|
|
|
assert.Contains(t, out, "Sync Diagnostics")
|
|
assert.Contains(t, out, "Data directory: "+dataDir)
|
|
assert.Contains(t, out, "Database: "+filepath.Join(dataDir, "sessions.db"))
|
|
assert.Contains(t, out,
|
|
fmt.Sprintf("SQLite user_version: %d", db.CurrentDataVersion()))
|
|
assert.Contains(t, out,
|
|
fmt.Sprintf("Binary data version: %d", db.CurrentDataVersion()))
|
|
assert.Contains(t, out,
|
|
"Startup sync decision: normal initial sync (no data-version resync)")
|
|
assert.Contains(t, out,
|
|
"Likely cause: data-version resync is not expected")
|
|
}
|
|
|
|
func TestDoctorSyncStaleDatabaseReportsLikelyAbortedResync(t *testing.T) {
|
|
dataDir := testDataDir(t)
|
|
dbPath := filepath.Join(dataDir, "sessions.db")
|
|
|
|
database, err := db.Open(dbPath)
|
|
require.NoError(t, err, "open db")
|
|
require.NoError(t, database.UpsertSession(db.Session{
|
|
ID: "stale-session",
|
|
Project: "proj",
|
|
Machine: "local",
|
|
Agent: "codex",
|
|
MessageCount: 1,
|
|
DataVersion: 0,
|
|
}), "insert session")
|
|
require.NoError(t, database.Close(), "close db")
|
|
|
|
conn, err := sql.Open("sqlite3", dbPath)
|
|
require.NoError(t, err, "raw sqlite open")
|
|
_, err = conn.Exec("PRAGMA user_version = 0")
|
|
require.NoError(t, err, "downgrade user_version")
|
|
require.NoError(t, conn.Close(), "close raw sqlite")
|
|
|
|
logPath := filepath.Join(dataDir, "debug.log")
|
|
require.NoError(t, os.WriteFile(logPath, []byte(
|
|
"2026/06/18 data version outdated; full resync required\n"+
|
|
"2026/06/18 resync aborted: 0 synced, 3 failed\n",
|
|
), 0o644), "write debug log")
|
|
|
|
out, err := executeCommand(newRootCommand(), "doctor", "sync")
|
|
require.NoError(t, err, "doctor sync")
|
|
|
|
assert.Contains(t, out, "SQLite user_version: 0")
|
|
assert.Contains(t, out,
|
|
fmt.Sprintf("Binary data version: %d", db.CurrentDataVersion()))
|
|
assert.Contains(t, out,
|
|
"Startup sync decision: full data-version resync required")
|
|
assert.Contains(t, out, "Session data versions:")
|
|
assert.Contains(t, out, "version 0: 1")
|
|
assert.Contains(t, out, "Recent debug.log evidence:")
|
|
assert.Contains(t, out, "resync aborted: 0 synced, 3 failed")
|
|
assert.Contains(t, out,
|
|
"Likely cause: previous data-version resync likely aborted before completion")
|
|
}
|
|
|
|
func TestDoctorSyncReportsSessionsMissingSecretScan(t *testing.T) {
|
|
dataDir := testDataDir(t)
|
|
dbPath := filepath.Join(dataDir, "sessions.db")
|
|
|
|
database, err := db.Open(dbPath)
|
|
require.NoError(t, err, "open db")
|
|
for _, id := range []string{"suspect", "scanned"} {
|
|
require.NoError(t, database.UpsertSession(db.Session{
|
|
ID: id, Project: "proj", Machine: "local", Agent: "claude",
|
|
MessageCount: 1,
|
|
}), "insert session %s", id)
|
|
}
|
|
require.NoError(t, database.Close(), "close db")
|
|
|
|
// Both sessions carry current signals, but only "scanned" ever had
|
|
// findings persisted (any non-empty rules version).
|
|
conn, err := sql.Open("sqlite3", dbPath)
|
|
require.NoError(t, err, "raw sqlite open")
|
|
_, err = conn.Exec(
|
|
`UPDATE sessions SET quality_signal_version = ?`,
|
|
db.CurrentQualitySignalVersion,
|
|
)
|
|
require.NoError(t, err, "set current signal versions")
|
|
_, err = conn.Exec(
|
|
`UPDATE sessions SET secrets_rules_version = 'v-old'
|
|
WHERE id = 'scanned'`,
|
|
)
|
|
require.NoError(t, err, "mark scanned session")
|
|
require.NoError(t, conn.Close(), "close raw sqlite")
|
|
|
|
out, err := executeCommand(newRootCommand(), "doctor", "sync")
|
|
require.NoError(t, err, "doctor sync")
|
|
|
|
assert.Contains(t, out,
|
|
"1 session(s) have current quality signals but no persisted secret scan")
|
|
assert.Contains(t, out, `run "agentsview secrets scan"`)
|
|
}
|
|
|
|
func TestDoctorSyncSilentWithoutMissingSecretScans(t *testing.T) {
|
|
dataDir := testDataDir(t)
|
|
|
|
database, err := db.Open(filepath.Join(dataDir, "sessions.db"))
|
|
require.NoError(t, err, "open db")
|
|
require.NoError(t, database.Close(), "close db")
|
|
|
|
out, err := executeCommand(newRootCommand(), "doctor", "sync")
|
|
require.NoError(t, err, "doctor sync")
|
|
|
|
assert.NotContains(t, out, "secrets scan",
|
|
"clean archives must not suggest a rescan")
|
|
}
|
|
|
|
func TestDoctorSyncNewerDatabaseReportsRefusedStartup(t *testing.T) {
|
|
dataDir := testDataDir(t)
|
|
dbPath := filepath.Join(dataDir, "sessions.db")
|
|
|
|
database, err := db.Open(dbPath)
|
|
require.NoError(t, err, "open db")
|
|
require.NoError(t, database.Close(), "close db")
|
|
|
|
futureVersion := db.CurrentDataVersion() + 10
|
|
conn, err := sql.Open("sqlite3", dbPath)
|
|
require.NoError(t, err, "raw sqlite open")
|
|
_, err = conn.Exec(fmt.Sprintf("PRAGMA user_version = %d", futureVersion))
|
|
require.NoError(t, err, "set future user_version")
|
|
require.NoError(t, conn.Close(), "close raw sqlite")
|
|
|
|
out, err := executeCommand(newRootCommand(), "doctor", "sync")
|
|
require.NoError(t, err, "doctor sync")
|
|
|
|
assert.Contains(t, out,
|
|
fmt.Sprintf("SQLite user_version: %d", futureVersion))
|
|
assert.Contains(t, out,
|
|
fmt.Sprintf("Binary data version: %d", db.CurrentDataVersion()))
|
|
assert.Contains(t, out,
|
|
"Startup sync decision: refuse startup (database requires newer agentsview)")
|
|
assert.Contains(t, out,
|
|
"Likely cause: SQLite user_version is newer than this binary")
|
|
assert.Contains(t, out, `Run "agentsview update"`)
|
|
}
|
|
|
|
func TestWriteDoctorSummaryMode(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
writeDoctorSummaryMode(&buf, doctorSyncReport{
|
|
doctorDBInspection: doctorDBInspection{
|
|
AntigravityCLITotal: 12,
|
|
AntigravityCLISummary: 5,
|
|
},
|
|
})
|
|
out := buf.String()
|
|
assert.Contains(t, out, "antigravity-cli")
|
|
assert.Contains(t, out, "5")
|
|
assert.Contains(t, out, "summary mode")
|
|
assert.Contains(t, out, "agy-reader")
|
|
}
|
|
|
|
func TestWriteDoctorSummaryModeSilentWhenNone(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
writeDoctorSummaryMode(&buf, doctorSyncReport{
|
|
doctorDBInspection: doctorDBInspection{
|
|
AntigravityCLITotal: 12, AntigravityCLISummary: 0,
|
|
},
|
|
})
|
|
assert.NotContains(t, buf.String(), "summary mode")
|
|
}
|
|
|
|
func TestWriteDoctorSummaryModeSilentOnErr(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
writeDoctorSummaryMode(&buf, doctorSyncReport{
|
|
doctorDBInspection: doctorDBInspection{
|
|
AntigravityCLITotal: 12,
|
|
AntigravityCLISummary: 5,
|
|
AntigravityCountsErr: errors.New("query failed"),
|
|
},
|
|
})
|
|
assert.Empty(t, buf.String())
|
|
}
|
|
|
|
func TestWriteDoctorUnknownSchema(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
writeDoctorUnknownSchema(&buf, doctorSyncReport{
|
|
doctorDBInspection: doctorDBInspection{
|
|
AntigravityUnknownSchema: 3,
|
|
},
|
|
})
|
|
out := buf.String()
|
|
assert.Contains(t, out, "3 session(s) on unrecognized Antigravity schema")
|
|
assert.Contains(t, out, "agy-schema:")
|
|
}
|
|
|
|
func TestWriteDoctorUnknownSchemaSilentWhenNone(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
writeDoctorUnknownSchema(&buf, doctorSyncReport{
|
|
doctorDBInspection: doctorDBInspection{
|
|
AntigravityUnknownSchema: 0,
|
|
},
|
|
})
|
|
assert.Empty(t, buf.String())
|
|
}
|
|
|
|
func TestWriteDoctorUnknownSchemaSilentOnErr(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
writeDoctorUnknownSchema(&buf, doctorSyncReport{
|
|
doctorDBInspection: doctorDBInspection{
|
|
AntigravityUnknownSchema: 3,
|
|
AntigravityCountsErr: errors.New("query failed"),
|
|
},
|
|
})
|
|
assert.Empty(t, buf.String())
|
|
}
|
|
|
|
func TestInspectDoctorDBCountsAntigravityCLISummaryMode(t *testing.T) {
|
|
dir := t.TempDir()
|
|
dbPath := filepath.Join(dir, "sessions.db")
|
|
|
|
database := dbtest.OpenTestDBAt(t, dbPath)
|
|
require.NoError(t, database.UpsertSession(db.Session{
|
|
ID: "agy-summary",
|
|
Agent: "antigravity-cli",
|
|
Machine: "local",
|
|
Project: "proj",
|
|
TranscriptFidelity: "summary",
|
|
}), "upsert summary session")
|
|
require.NoError(t, database.UpsertSession(db.Session{
|
|
ID: "agy-full",
|
|
Agent: "antigravity-cli",
|
|
Machine: "local",
|
|
Project: "proj",
|
|
TranscriptFidelity: "full",
|
|
}), "upsert full session")
|
|
require.NoError(t, database.UpsertSession(db.Session{
|
|
ID: "other-agent",
|
|
Agent: "claude-code",
|
|
Machine: "local",
|
|
Project: "proj",
|
|
}), "upsert other-agent session")
|
|
require.NoError(t, database.Close(), "close db")
|
|
|
|
insp := inspectDoctorDB(dbPath)
|
|
require.NoError(t, insp.AntigravityCountsErr, "antigravity counts query")
|
|
assert.Equal(t, 2, insp.AntigravityCLITotal, "AntigravityCLITotal")
|
|
assert.Equal(t, 1, insp.AntigravityCLISummary, "AntigravityCLISummary")
|
|
}
|
|
|
|
// TestInspectDoctorDBCountsUnknownSchema verifies the unrecognized-schema
|
|
// count spans both Antigravity agents and excludes known-range labels and
|
|
// other agents that carry a generic source_version.
|
|
func TestInspectDoctorDBCountsUnknownSchema(t *testing.T) {
|
|
dir := t.TempDir()
|
|
dbPath := filepath.Join(dir, "sessions.db")
|
|
|
|
database := dbtest.OpenTestDBAt(t, dbPath)
|
|
require.NoError(t, database.UpsertSession(db.Session{
|
|
ID: "agy-ide-unknown",
|
|
Agent: "antigravity",
|
|
Machine: "local",
|
|
Project: "proj",
|
|
SourceVersion: "agy-schema:abc123def456",
|
|
}), "upsert IDE unknown-schema session")
|
|
require.NoError(t, database.UpsertSession(db.Session{
|
|
ID: "agy-cli-unknown",
|
|
Agent: "antigravity-cli",
|
|
Machine: "local",
|
|
Project: "proj",
|
|
SourceVersion: "agy-schema:abc123def456",
|
|
}), "upsert CLI unknown-schema session")
|
|
require.NoError(t, database.UpsertSession(db.Session{
|
|
ID: "agy-known",
|
|
Agent: "antigravity-cli",
|
|
Machine: "local",
|
|
Project: "proj",
|
|
SourceVersion: "1.0.7-1.0.10",
|
|
}), "upsert known-range session")
|
|
require.NoError(t, database.UpsertSession(db.Session{
|
|
ID: "piebald-generic",
|
|
Agent: "piebald",
|
|
Machine: "local",
|
|
Project: "proj",
|
|
SourceVersion: "piebald-appdb-v1",
|
|
}), "upsert non-antigravity session")
|
|
require.NoError(t, database.Close(), "close db")
|
|
|
|
insp := inspectDoctorDB(dbPath)
|
|
require.NoError(t, insp.AntigravityCountsErr, "antigravity counts query")
|
|
assert.Equal(t, 2, insp.AntigravityUnknownSchema, "AntigravityUnknownSchema")
|
|
}
|
|
|
|
func TestDoctorSyncReportStatErrorDoesNotRenderAsMissingDatabase(t *testing.T) {
|
|
report := doctorSyncReport{
|
|
Config: config.Config{
|
|
DataDir: "/data",
|
|
DBPath: "/data/sessions.db",
|
|
},
|
|
doctorDBInspection: doctorDBInspection{
|
|
DBExists: false,
|
|
DBError: errors.New("stat /data/sessions.db: permission denied"),
|
|
},
|
|
}
|
|
|
|
var out bytes.Buffer
|
|
writeDoctorSyncReport(&out, report)
|
|
got := out.String()
|
|
|
|
assert.Contains(t, got, "Database exists: unknown")
|
|
assert.Contains(t, got,
|
|
"Startup sync decision: unknown (database could not be inspected)")
|
|
assert.Contains(t, got, "Session data versions:")
|
|
assert.Contains(t, got,
|
|
"unavailable: stat /data/sessions.db: permission denied")
|
|
assert.Contains(t, got,
|
|
"Likely cause: database could not be inspected; check database path and permissions")
|
|
assert.NotContains(t, got, "database will be created")
|
|
assert.NotContains(t, got, "database does not exist yet")
|
|
}
|