Files
kenn-io--agentsview/internal/sync/stored_source_hints_test.go
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

284 lines
9.9 KiB
Go

package sync
import (
"context"
"database/sql"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.kenn.io/agentsview/internal/db"
"go.kenn.io/agentsview/internal/dbtest"
"go.kenn.io/agentsview/internal/parser"
"go.kenn.io/agentsview/internal/testjsonl"
)
type hintRecordingFactory struct {
agent parser.AgentType
caps parser.Capabilities
seen *[][]string
}
func (f hintRecordingFactory) Definition() parser.AgentDef {
return parser.AgentDef{Type: f.agent, DisplayName: string(f.agent)}
}
func (f hintRecordingFactory) Capabilities() parser.Capabilities { return f.caps }
func (f hintRecordingFactory) NewProvider(cfg parser.ProviderConfig) parser.Provider {
return &hintRecordingProvider{ProviderBase: parser.ProviderBase{
Def: f.Definition(), Caps: f.caps, Config: cfg.Clone(),
}, seen: f.seen}
}
type hintRecordingProvider struct {
parser.ProviderBase
seen *[][]string
}
func (p *hintRecordingProvider) WatchPlan(context.Context) (parser.WatchPlan, error) {
return parser.WatchPlan{Roots: []parser.WatchRoot{{Path: p.Config.Roots[0]}}}, nil
}
func (p *hintRecordingProvider) SourcesForChangedPath(
_ context.Context,
req parser.ChangedPathRequest,
) ([]parser.SourceRef, error) {
*p.seen = append(*p.seen, append([]string(nil), req.StoredSourcePaths...))
return []parser.SourceRef{{
Provider: p.Definition().Type, Key: req.Path,
DisplayPath: req.Path, FingerprintKey: req.Path,
}}, nil
}
func (p *hintRecordingProvider) Parse(context.Context, parser.ParseRequest) (parser.ParseOutcome, error) {
return parser.ParseOutcome{}, nil
}
func TestClassifyProviderChangedPathSchedulesStoredSourceHintsByCapability(t *testing.T) {
for _, tc := range []struct {
name string
caps parser.CapabilitySupport
want bool
}{
{name: "supported", caps: parser.CapabilitySupported, want: true},
{name: "unsupported", caps: parser.CapabilityUnsupported, want: false},
} {
t.Run(tc.name, func(t *testing.T) {
root := t.TempDir()
changedPath := filepath.Join(root, "container.db")
require.NoError(t, os.WriteFile(changedPath, []byte("fixture"), 0o600))
persistedPath := filepath.Join(root, "archive", "stored#member")
database := dbtest.OpenTestDB(t)
require.NoError(t, database.UpsertSession(db.Session{
ID: "hint-agent:stored", Agent: "hint-agent", Project: "fixture",
Machine: "local", FilePath: strPtr(persistedPath),
}))
var seen [][]string
caps := parser.Capabilities{Source: parser.SourceCapabilities{
ClassifyChangedPath: parser.CapabilitySupported,
StoredSourceHints: tc.caps,
}}
factory := hintRecordingFactory{agent: "hint-agent", caps: caps, seen: &seen}
engine := &Engine{
db: database, machine: "local",
agentDirs: map[parser.AgentType][]string{"hint-agent": {root}},
providerFactories: map[parser.AgentType]parser.ProviderFactory{"hint-agent": factory},
providerMigrationModes: map[parser.AgentType]parser.ProviderMigrationMode{
"hint-agent": parser.ProviderMigrationProviderAuthoritative,
},
}
files := engine.classifyProviderChangedPath(changedPath)
require.Len(t, files, 1)
require.Len(t, seen, 1)
if tc.want {
assert.Equal(t, []string{persistedPath}, seen[0])
} else {
assert.Empty(t, seen[0])
}
})
}
}
func TestClassifyCodexChangedPathAllocationsStayBounded(t *testing.T) {
measure := func(t *testing.T, hintCount int) float64 {
t.Helper()
root := t.TempDir()
path := filepath.Join(root, "rollout-2026-07-11T10-00-00-alloc.jsonl")
content := testjsonl.JoinJSONL(
testjsonl.CodexSessionMetaJSON(
"alloc", "/workspace/agentsview", "codex_cli_rs", "2026-07-11T10:00:00Z",
),
testjsonl.CodexMsgJSON("user", "measure allocations", "2026-07-11T10:00:01Z"),
)
require.NoError(t, os.WriteFile(path, []byte(content), 0o600))
database := dbtest.OpenTestDB(t)
for i := range hintCount {
hint := filepath.Join(root, "archive", fmt.Sprintf("%04d.jsonl", i))
require.NoError(t, database.UpsertSession(db.Session{
ID: fmt.Sprintf("codex:hint-%04d", i), Agent: string(parser.AgentCodex),
Project: "archive", Machine: "local", FilePath: strPtr(hint),
}))
}
engine := &Engine{
db: database, machine: "local",
agentDirs: map[parser.AgentType][]string{parser.AgentCodex: {root}},
providerFactories: providerFactoryMap(parser.ProviderFactories()),
providerMigrationModes: map[parser.AgentType]parser.ProviderMigrationMode{
parser.AgentCodex: parser.ProviderMigrationProviderAuthoritative,
},
}
warm := engine.classifyProviderChangedPath(path)
require.Len(t, warm, 1)
assert.Equal(t, path, warm[0].Path)
assert.Equal(t, parser.AgentCodex, warm[0].Agent)
var got []parser.DiscoveredFile
allocs := testing.AllocsPerRun(5, func() {
got = engine.classifyProviderChangedPath(path)
})
require.Len(t, got, 1)
assert.Equal(t, path, got[0].Path)
assert.Equal(t, parser.AgentCodex, got[0].Agent)
return allocs
}
smallAllocs := measure(t, 10)
largeAllocs := measure(t, 2000)
assert.LessOrEqual(t, largeAllocs, smallAllocs*2,
"stored archives must not scale Codex changed-path allocations")
}
func TestClassifyProviderChangedPathPreservesHintDependentTombstones(t *testing.T) {
tests := []struct {
name string
agent parser.AgentType
setup func(t *testing.T) (root, changedPath, deletedPath string)
}{
{
name: "Forge dbBackedSourceSet", agent: parser.AgentForge,
setup: func(t *testing.T) (string, string, string) {
root := t.TempDir()
path := writeProcessProviderForgeDB(t, root)
conn, err := sql.Open("sqlite3", path)
require.NoError(t, err)
_, err = conn.Exec(`DELETE FROM conversations WHERE conversation_id = 'conv-001'`)
require.NoError(t, err)
require.NoError(t, conn.Close())
return root, path, path + "#conv-001"
},
},
{
name: "Zed multiSessionContainerSourceSet", agent: parser.AgentZed,
setup: func(t *testing.T) (string, string, string) {
root := t.TempDir()
path := filepath.Join(root, "threads", "threads.db")
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
store, err := sql.Open("sqlite3", path)
require.NoError(t, err)
_, err = store.Exec(`CREATE TABLE threads (
id TEXT PRIMARY KEY, summary TEXT NOT NULL, updated_at TEXT NOT NULL,
data_type TEXT NOT NULL, data BLOB NOT NULL, parent_id TEXT,
folder_paths TEXT, folder_paths_order TEXT, created_at TEXT)`)
require.NoError(t, err)
require.NoError(t, store.Close())
return root, path, parser.ZedSQLiteVirtualPath(path, "deleted")
},
},
{
name: "Devin direct DB reader", agent: parser.AgentDevin,
setup: func(t *testing.T) (string, string, string) {
root := t.TempDir()
path, _ := writeProcessProviderDevinFixture(
t, root, "deleted", "reply", 1710000000000, 1710000005000,
)
conn, err := sql.Open("sqlite3", path)
require.NoError(t, err)
_, err = conn.Exec(`DELETE FROM sessions WHERE id = 'deleted'`)
require.NoError(t, err)
require.NoError(t, conn.Close())
return root, path, path + "#deleted"
},
},
{
name: "Kiro SQLite helper", agent: parser.AgentKiro,
setup: func(t *testing.T) (string, string, string) {
root := t.TempDir()
path := filepath.Join(root, "data.sqlite3")
store, err := sql.Open("sqlite3", path)
require.NoError(t, err)
_, err = store.Exec(`CREATE TABLE conversations_v2 (
key TEXT NOT NULL, conversation_id TEXT NOT NULL, value TEXT NOT NULL,
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL,
PRIMARY KEY (key, conversation_id))`)
require.NoError(t, err)
payload, err := os.ReadFile(filepath.Join(
"..", "parser", "testdata", "kiro_sqlite", "standard_payload.json",
))
require.NoError(t, err)
_, err = store.Exec(`INSERT INTO conversations_v2
(key, conversation_id, value, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`,
"/workspace/agentsview", "deleted", string(payload),
1710000000000, 1710000005000)
require.NoError(t, err)
_, err = store.Exec(`DELETE FROM conversations_v2 WHERE conversation_id = 'deleted'`)
require.NoError(t, err)
require.NoError(t, store.Close())
return root, path, parser.KiroSQLiteVirtualPath(path, "deleted")
},
},
{
name: "Windsurf direct state DB reader", agent: parser.AgentWindsurf,
setup: func(t *testing.T) (string, string, string) {
root := filepath.Join(t.TempDir(), "Windsurf", "User")
path := filepath.Join(root, "workspaceStorage", "fixture", "state.vscdb")
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
writeSyncWindsurfStateDB(t, path, windsurfSyncPayload("deleted", "reply"))
updateSyncWindsurfStateDB(t, path, `{}`)
return root, path, path + "#deleted"
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
root, changedPath, deletedPath := tc.setup(t)
database := dbtest.OpenTestDB(t)
require.NoError(t, database.UpsertSession(db.Session{
ID: string(tc.agent) + ":deleted", Agent: string(tc.agent),
Project: "fixture", Machine: "local", FilePath: strPtr(deletedPath),
}))
engine := &Engine{
db: database, machine: "local", skipCache: make(map[string]int64),
agentDirs: map[parser.AgentType][]string{tc.agent: {root}},
providerFactories: providerFactoryMap(parser.ProviderFactories()),
providerMigrationModes: map[parser.AgentType]parser.ProviderMigrationMode{
tc.agent: parser.ProviderMigrationProviderAuthoritative,
},
}
files := engine.classifyProviderChangedPath(changedPath)
var tombstone parser.DiscoveredFile
for _, file := range files {
if file.Path == deletedPath {
tombstone = file
}
}
require.Equal(t, deletedPath, tombstone.Path)
assert.Equal(t, tc.agent, tombstone.Agent)
result := engine.processFile(context.Background(), tombstone)
require.NoError(t, result.err)
assert.True(t, result.forceReplace)
assert.Empty(t, result.results)
})
}
}