Files
kenn-io--agentsview/internal/sync/verified_source_gate_integration_test.go
wehub-resource-sync f99010fae1
Desktop Artifacts / Desktop Build (Linux) (push) Waiting to run
Desktop Artifacts / Desktop Build (Windows) (push) Waiting to run
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Waiting to run
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
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

377 lines
11 KiB
Go

package sync
import (
"context"
"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/parser"
)
type verifiedSourceCountingProvider struct {
parser.ProviderBase
root string
sources map[string]parser.SourceRef
fingerprintCalls int
}
func (p *verifiedSourceCountingProvider) WatchPlan(
context.Context,
) (parser.WatchPlan, error) {
return parser.WatchPlan{Roots: []parser.WatchRoot{{
Path: p.root, Recursive: true,
}}}, nil
}
func (p *verifiedSourceCountingProvider) SourcesForChangedPath(
_ context.Context,
req parser.ChangedPathRequest,
) ([]parser.SourceRef, error) {
if source, ok := p.sources[filepath.Clean(req.Path)]; ok {
return []parser.SourceRef{source}, nil
}
return nil, nil
}
func (p *verifiedSourceCountingProvider) Fingerprint(
_ context.Context,
source parser.SourceRef,
) (parser.SourceFingerprint, error) {
p.fingerprintCalls++
return verifiedSourceFingerprint(source.DisplayPath)
}
func (p *verifiedSourceCountingProvider) Parse(
context.Context,
parser.ParseRequest,
) (parser.ParseOutcome, error) {
return parser.ParseOutcome{}, fmt.Errorf(
"unexpected parse after seeding stored source state",
)
}
type verifiedSourceCountingFactory struct {
provider *verifiedSourceCountingProvider
}
func (f verifiedSourceCountingFactory) Definition() parser.AgentDef {
return f.provider.Definition()
}
func (f verifiedSourceCountingFactory) Capabilities() parser.Capabilities {
return f.provider.Capabilities()
}
func (f verifiedSourceCountingFactory) NewProvider(
parser.ProviderConfig,
) parser.Provider {
return f.provider
}
func verifiedSourceFingerprint(path string) (parser.SourceFingerprint, error) {
info, err := os.Stat(path)
if err != nil {
return parser.SourceFingerprint{}, err
}
return parser.SourceFingerprint{
Key: path,
Size: info.Size(),
MTimeNS: parser.CodexEffectiveMtime(path, info.ModTime().UnixNano()),
Hash: "verified:" + filepath.Base(path),
}, nil
}
func newVerifiedSourceArchive(
t *testing.T,
count int,
) (*Engine, *verifiedSourceCountingProvider, []parser.DiscoveredFile) {
return newVerifiedSourceArchiveWithRewriter(t, count, nil)
}
func newVerifiedSourceArchiveWithRewriter(
t *testing.T,
count int,
pathRewriter func(string) string,
) (*Engine, *verifiedSourceCountingProvider, []parser.DiscoveredFile) {
t.Helper()
root := t.TempDir()
database := openTestDB(t)
provider := &verifiedSourceCountingProvider{
ProviderBase: parser.ProviderBase{
Def: parser.AgentDef{
Type: parser.AgentCodex, DisplayName: "Codex",
IDPrefix: "codex:", FileBased: true,
},
Caps: parser.Capabilities{Source: parser.SourceCapabilities{
WatchSources: parser.CapabilitySupported,
ClassifyChangedPath: parser.CapabilitySupported,
CompositeFingerprint: parser.CapabilitySupported,
VerifiedLocalStat: parser.CapabilitySupported,
}},
},
root: root,
sources: make(map[string]parser.SourceRef, count),
}
factory := verifiedSourceCountingFactory{provider: provider}
engine := NewEngine(database, EngineConfig{
AgentDirs: map[parser.AgentType][]string{
parser.AgentCodex: {root},
},
Machine: "host",
PathRewriter: pathRewriter,
ProviderFactories: []parser.ProviderFactory{factory},
ProviderMigrationModes: map[parser.AgentType]parser.ProviderMigrationMode{
parser.AgentCodex: parser.ProviderMigrationProviderAuthoritative,
},
})
files := make([]parser.DiscoveredFile, 0, count)
for i := range count {
uuid := fmt.Sprintf("00000000-0000-0000-0000-%012d", i+1)
path := filepath.Join(
root,
"rollout-2026-07-11T00-00-00-"+uuid+".jsonl",
)
require.NoError(t, os.WriteFile(path, []byte("session\n"), 0o600))
fingerprint, err := verifiedSourceFingerprint(path)
require.NoError(t, err)
filePath := path
if pathRewriter != nil {
filePath = pathRewriter(path)
}
fileSize := fingerprint.Size
fileMtime := fingerprint.MTimeNS
fileHash := fingerprint.Hash
require.NoError(t, database.UpsertSession(db.Session{
ID: "codex:" + uuid, Project: "project", Machine: "host",
Agent: string(parser.AgentCodex), FilePath: &filePath,
FileSize: &fileSize, FileMtime: &fileMtime, FileHash: &fileHash,
}))
require.NoError(t, database.SetSessionDataVersion(
"codex:"+uuid, db.CurrentDataVersion(),
))
source := parser.SourceRef{
Provider: parser.AgentCodex, Key: path,
DisplayPath: path, FingerprintKey: path,
ProjectHint: "project",
}
provider.sources[filepath.Clean(path)] = source
sourceCopy := source
files = append(files, parser.DiscoveredFile{
Path: path, Agent: parser.AgentCodex,
ProviderSource: &sourceCopy, ProviderProcess: true,
})
}
return engine, provider, files
}
func runVerifiedSourcePass(
t *testing.T,
engine *Engine,
files []parser.DiscoveredFile,
) {
t.Helper()
pass := engine.beginVerifiedSourcePass()
for _, file := range files {
res := engine.processFile(context.Background(), file)
require.NoError(t, res.err)
assert.True(t, res.skip)
}
engine.finishVerifiedSourcePass(pass, true)
}
func TestVerifiedSourceGateWarmFingerprintWorkIsCardinalityIndependent(
t *testing.T,
) {
for _, count := range []int{3, 40} {
t.Run(fmt.Sprintf("sources=%d", count), func(t *testing.T) {
engine, provider, files := newVerifiedSourceArchive(t, count)
runVerifiedSourcePass(t, engine, files)
assert.Equal(t, count, provider.fingerprintCalls,
"cold pass must deep-verify every source")
runVerifiedSourcePass(t, engine, files)
assert.Equal(t, count, provider.fingerprintCalls,
"warm pass must perform zero content fingerprints")
assert.Len(t, engine.verifiedSources, count)
})
}
}
func TestVerifiedSourceGateWarmTrustDoesNotMaskDatabaseRepair(t *testing.T) {
const sessionID = "codex:00000000-0000-0000-0000-000000000001"
tests := []struct {
name string
mutate func(*testing.T, *db.DB)
}{
{
name: "missing row",
mutate: func(t *testing.T, database *db.DB) {
t.Helper()
require.NoError(t, database.DeleteSession(sessionID))
},
},
{
name: "stale data version",
mutate: func(t *testing.T, database *db.DB) {
t.Helper()
require.NoError(t, database.SetSessionDataVersion(
sessionID, db.CurrentDataVersion()-1,
))
},
},
{
name: "project requires reparse",
mutate: func(t *testing.T, database *db.DB) {
t.Helper()
session, err := database.GetSessionFull(
context.Background(), sessionID,
)
require.NoError(t, err)
require.NotNil(t, session)
session.Project = "_tmp_workspace"
require.NoError(t, database.UpsertSession(*session))
},
},
{
name: "file mtimes reset",
mutate: func(t *testing.T, database *db.DB) {
t.Helper()
require.NoError(t, database.ResetAllMtimes())
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
engine, provider, files := newVerifiedSourceArchive(t, 1)
runVerifiedSourcePass(t, engine, files)
runVerifiedSourcePass(t, engine, files)
require.Equal(t, 1, provider.fingerprintCalls)
tt.mutate(t, engine.db)
res := engine.processFile(context.Background(), files[0])
require.ErrorContains(t, res.err,
"unexpected parse after seeding stored source state")
assert.Equal(t, 2, provider.fingerprintCalls,
"persisted state requiring repair must bypass warm trust")
})
}
}
func TestVerifiedSourceGateRechecksAfterStatAndWatcherInvalidation(t *testing.T) {
engine, provider, files := newVerifiedSourceArchive(t, 1)
file := files[0]
runVerifiedSourcePass(t, engine, files)
runVerifiedSourcePass(t, engine, files)
require.Equal(t, 1, provider.fingerprintCalls)
info, err := os.Stat(file.Path)
require.NoError(t, err)
require.NoError(t, os.WriteFile(file.Path, []byte("changed\n"), 0o600))
require.NoError(t, os.Chtimes(file.Path, info.ModTime(), info.ModTime()))
runVerifiedSourcePass(t, engine, files)
assert.Equal(t, 2, provider.fingerprintCalls,
"same-size rewrite with restored mtime must deep-verify")
classified := engine.classifyPaths([]string{file.Path})
require.Len(t, classified, 1)
res := engine.processFile(context.Background(), classified[0])
require.NoError(t, res.err)
assert.True(t, res.skip)
assert.Equal(t, 3, provider.fingerprintCalls,
"a watcher-classified source must invalidate warm trust")
engine.clearWatcherOverflowCaches()
res = engine.processFile(context.Background(), file)
require.NoError(t, res.err)
assert.True(t, res.skip)
assert.Equal(t, 4, provider.fingerprintCalls,
"watcher overflow must clear every verified-source trust record")
}
func TestVerifiedSourceGateBypassesPathRewrittenSources(t *testing.T) {
rewriter := func(path string) string { return "remote:" + path }
engine, provider, files := newVerifiedSourceArchiveWithRewriter(
t, 1, rewriter,
)
runVerifiedSourcePass(t, engine, files)
runVerifiedSourcePass(t, engine, files)
assert.Equal(t, 2, provider.fingerprintCalls,
"remote materializations must deep-verify on every pass")
assert.Empty(t, engine.verifiedSources)
}
func TestVerifiedSourceGateLegacyClaudeRowMustEstablishFingerprint(t *testing.T) {
root := t.TempDir()
path := filepath.Join(root, "legacy-session.jsonl")
require.NoError(t, os.WriteFile(path, []byte("session\n"), 0o600))
info, err := os.Stat(path)
require.NoError(t, err)
database := openTestDB(t)
fileSize := info.Size()
fileMtime := info.ModTime().UnixNano()
require.NoError(t, database.UpsertSession(db.Session{
ID: "claude:legacy-session", Project: "project", Machine: "host",
Agent: string(parser.AgentClaude), FilePath: &path,
FileSize: &fileSize, FileMtime: &fileMtime,
}))
require.NoError(t, database.SetSessionDataVersion(
"claude:legacy-session", db.CurrentDataVersion(),
))
provider := &verifiedSourceCountingProvider{
ProviderBase: parser.ProviderBase{
Def: parser.AgentDef{
Type: parser.AgentClaude, DisplayName: "Claude",
IDPrefix: "claude:", FileBased: true,
},
Caps: parser.Capabilities{Source: parser.SourceCapabilities{
IncrementalAppend: parser.CapabilitySupported,
VerifiedLocalStat: parser.CapabilitySupported,
}},
},
root: root,
sources: make(map[string]parser.SourceRef),
}
engine := NewEngine(database, EngineConfig{
AgentDirs: map[parser.AgentType][]string{
parser.AgentClaude: {root},
},
Machine: "host",
ProviderFactories: []parser.ProviderFactory{verifiedSourceCountingFactory{provider}},
ProviderMigrationModes: map[parser.AgentType]parser.ProviderMigrationMode{
parser.AgentClaude: parser.ProviderMigrationProviderAuthoritative,
},
})
source := parser.SourceRef{
Provider: parser.AgentClaude, Key: path,
DisplayPath: path, FingerprintKey: path,
ProjectHint: "project",
}
res := engine.processFile(context.Background(), parser.DiscoveredFile{
Path: path, Agent: parser.AgentClaude,
ProviderSource: &source, ProviderProcess: true,
})
require.ErrorContains(t, res.err,
"unexpected parse after seeding stored source state")
assert.Equal(t, 1, provider.fingerprintCalls,
"a legacy row without file_hash must not keep taking the stat-only skip")
record, ok := engine.verifiedSources[path]
require.True(t, ok)
assert.False(t, record.trusted,
"failed parsing must not promote source trust")
}