kenn-io--agentsview
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
380 行
10 KiB
Go
380 行
10 KiB
Go
package sync
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.kenn.io/agentsview/internal/dbtest"
|
|
"go.kenn.io/agentsview/internal/parser"
|
|
)
|
|
|
|
func TestParseDiffDiscoversProviderSources(t *testing.T) {
|
|
root := t.TempDir()
|
|
sourcePath := filepath.Join(root, "provider-only.jsonl")
|
|
require.NoError(t, os.WriteFile(sourcePath, []byte("{}\n"), 0o644))
|
|
info, err := os.Stat(sourcePath)
|
|
require.NoError(t, err)
|
|
|
|
provider := parseDiffProvider{
|
|
sourcePath: sourcePath,
|
|
mtime: info.ModTime(),
|
|
size: info.Size(),
|
|
}
|
|
engine := NewDiffEngine(dbtest.OpenTestDB(t), EngineConfig{
|
|
AgentDirs: map[parser.AgentType][]string{
|
|
parser.AgentClaude: {root},
|
|
},
|
|
Machine: "devbox",
|
|
ProviderFactories: []parser.ProviderFactory{
|
|
parseDiffProviderFactory{provider: provider},
|
|
},
|
|
ProviderMigrationModes: map[parser.AgentType]parser.ProviderMigrationMode{
|
|
parser.AgentClaude: parser.ProviderMigrationProviderAuthoritative,
|
|
},
|
|
})
|
|
|
|
report, err := engine.ParseDiff(context.Background(), ParseDiffOptions{
|
|
Agents: []parser.AgentType{parser.AgentClaude},
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, report)
|
|
assert.Equal(t, 1, report.FilesExamined)
|
|
assert.Equal(t, ParseDiffTotals{NewOnDisk: 1, Examined: 0}, report.Totals)
|
|
if assert.Len(t, report.Sessions, 1) {
|
|
assert.Equal(t, DiffNewOnDisk, report.Sessions[0].Class)
|
|
assert.Equal(t, "provider-discovered", report.Sessions[0].SessionID)
|
|
assert.Equal(t, sourcePath, report.Sessions[0].FilePath)
|
|
}
|
|
}
|
|
|
|
func TestParseDiffSupportedAgentsAreDiscoverable(t *testing.T) {
|
|
engine := NewDiffEngine(dbtest.OpenTestDB(t), EngineConfig{})
|
|
for _, agent := range []parser.AgentType{
|
|
parser.AgentGptme,
|
|
parser.AgentPi,
|
|
parser.AgentOMP,
|
|
parser.AgentWorkBuddy,
|
|
parser.AgentCortex,
|
|
parser.AgentKimi,
|
|
parser.AgentQwenPaw,
|
|
parser.AgentOpenHands,
|
|
parser.AgentCursor,
|
|
parser.AgentDevin,
|
|
parser.AgentVibe,
|
|
parser.AgentClaude,
|
|
parser.AgentCowork,
|
|
parser.AgentHermes,
|
|
// DB-backed provider-authoritative agents: discoverable through
|
|
// their providers even though FileBased is false.
|
|
parser.AgentForge,
|
|
parser.AgentPiebald,
|
|
parser.AgentWarp,
|
|
} {
|
|
def, ok := parser.AgentByType(agent)
|
|
require.True(t, ok, "agent %s", agent)
|
|
assert.True(t, engine.parseDiffAgentDiscoverable(def),
|
|
"parse-diff engine must include %s", agent)
|
|
}
|
|
}
|
|
|
|
// TestParseDiffDBBackedAgentsAreDiscoverable pins the specific contract this
|
|
// change adds: the DB-backed provider-authoritative agents are FileBased=false
|
|
// yet still admitted by the parse-diff discoverability gate, because the gate
|
|
// keys on the provider factory, not FileBased.
|
|
func TestParseDiffDBBackedAgentsAreDiscoverable(t *testing.T) {
|
|
engine := NewDiffEngine(dbtest.OpenTestDB(t), EngineConfig{})
|
|
for _, agent := range []parser.AgentType{
|
|
parser.AgentForge,
|
|
parser.AgentDevin,
|
|
parser.AgentPiebald,
|
|
parser.AgentWarp,
|
|
} {
|
|
def, ok := parser.AgentByType(agent)
|
|
require.True(t, ok, "agent %s", agent)
|
|
assert.False(t, def.FileBased,
|
|
"%s is expected to be DB-backed (FileBased=false)", agent)
|
|
assert.True(t, engine.parseDiffAgentDiscoverable(def),
|
|
"DB-backed %s must be discoverable by parse-diff", agent)
|
|
}
|
|
|
|
// Import-only agents are still rejected: they are not
|
|
// provider-authoritative and have no source to re-parse.
|
|
for _, agent := range []parser.AgentType{
|
|
parser.AgentClaudeAI,
|
|
parser.AgentChatGPT,
|
|
} {
|
|
def, ok := parser.AgentByType(agent)
|
|
require.True(t, ok, "agent %s", agent)
|
|
assert.False(t, engine.parseDiffAgentDiscoverable(def),
|
|
"import-only %s must not be discoverable by parse-diff", agent)
|
|
}
|
|
}
|
|
|
|
func TestSyncAllDiscoversProviderSources(t *testing.T) {
|
|
root := t.TempDir()
|
|
sourcePath := filepath.Join(root, "provider-only-sync.jsonl")
|
|
require.NoError(t, os.WriteFile(sourcePath, []byte("{}\n"), 0o644))
|
|
info, err := os.Stat(sourcePath)
|
|
require.NoError(t, err)
|
|
|
|
provider := parseDiffProvider{
|
|
sourcePath: sourcePath,
|
|
mtime: info.ModTime(),
|
|
size: info.Size(),
|
|
}
|
|
database := dbtest.OpenTestDB(t)
|
|
engine := NewEngine(database, EngineConfig{
|
|
AgentDirs: map[parser.AgentType][]string{
|
|
parser.AgentClaude: {root},
|
|
},
|
|
Machine: "devbox",
|
|
ProviderFactories: []parser.ProviderFactory{
|
|
parseDiffProviderFactory{provider: provider},
|
|
},
|
|
ProviderMigrationModes: map[parser.AgentType]parser.ProviderMigrationMode{
|
|
parser.AgentClaude: parser.ProviderMigrationProviderAuthoritative,
|
|
},
|
|
})
|
|
|
|
stats := engine.SyncAll(context.Background(), nil)
|
|
|
|
assert.Equal(t, 1, stats.TotalSessions)
|
|
assert.Equal(t, 1, stats.Synced)
|
|
session, err := database.GetSession(context.Background(), "provider-discovered")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, session)
|
|
assert.Equal(t, sourcePath, database.GetSessionFilePath("provider-discovered"))
|
|
}
|
|
|
|
type parseDiffProviderFactory struct {
|
|
provider parseDiffProvider
|
|
}
|
|
|
|
func (f parseDiffProviderFactory) Definition() parser.AgentDef {
|
|
return parser.AgentDef{
|
|
Type: parser.AgentClaude,
|
|
DisplayName: "Claude Code",
|
|
FileBased: true,
|
|
}
|
|
}
|
|
|
|
func (f parseDiffProviderFactory) Capabilities() parser.Capabilities {
|
|
return parser.Capabilities{
|
|
Source: parser.SourceCapabilities{
|
|
DiscoverSources: parser.CapabilitySupported,
|
|
FindSource: parser.CapabilitySupported,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (f parseDiffProviderFactory) NewProvider(
|
|
parser.ProviderConfig,
|
|
) parser.Provider {
|
|
p := f.provider
|
|
p.ProviderBase = parser.ProviderBase{
|
|
Def: f.Definition(),
|
|
Caps: f.Capabilities(),
|
|
}
|
|
return p
|
|
}
|
|
|
|
type parseDiffProvider struct {
|
|
parser.ProviderBase
|
|
sourcePath string
|
|
mtime time.Time
|
|
size int64
|
|
}
|
|
|
|
func (p parseDiffProvider) Discover(context.Context) ([]parser.SourceRef, error) {
|
|
return []parser.SourceRef{p.source()}, nil
|
|
}
|
|
|
|
func (p parseDiffProvider) FindSource(
|
|
context.Context,
|
|
parser.FindSourceRequest,
|
|
) (parser.SourceRef, bool, error) {
|
|
return p.source(), true, nil
|
|
}
|
|
|
|
func (p parseDiffProvider) Fingerprint(
|
|
context.Context,
|
|
parser.SourceRef,
|
|
) (parser.SourceFingerprint, error) {
|
|
return parser.SourceFingerprint{
|
|
Key: p.sourcePath,
|
|
Size: p.size,
|
|
MTimeNS: p.mtime.UnixNano(),
|
|
Hash: "provider-hash",
|
|
}, nil
|
|
}
|
|
|
|
func (p parseDiffProvider) Parse(
|
|
context.Context,
|
|
parser.ParseRequest,
|
|
) (parser.ParseOutcome, error) {
|
|
return parser.ParseOutcome{
|
|
Results: []parser.ParseResultOutcome{{
|
|
Result: parser.ParseResult{
|
|
Session: parser.ParsedSession{
|
|
ID: "provider-discovered",
|
|
Agent: parser.AgentClaude,
|
|
Machine: "devbox",
|
|
Project: "provider",
|
|
StartedAt: p.mtime,
|
|
EndedAt: p.mtime,
|
|
MessageCount: 1,
|
|
File: parser.FileInfo{
|
|
Path: p.sourcePath,
|
|
Size: p.size,
|
|
Mtime: p.mtime.UnixNano(),
|
|
Hash: "provider-hash",
|
|
},
|
|
},
|
|
Messages: []parser.ParsedMessage{{
|
|
Role: parser.RoleUser,
|
|
Content: "provider discovered",
|
|
Timestamp: p.mtime,
|
|
Ordinal: 0,
|
|
}},
|
|
},
|
|
DataVersion: parser.DataVersionCurrent,
|
|
}},
|
|
ResultSetComplete: true,
|
|
}, nil
|
|
}
|
|
|
|
func (p parseDiffProvider) source() parser.SourceRef {
|
|
return parser.SourceRef{
|
|
Provider: parser.AgentClaude,
|
|
Key: p.sourcePath,
|
|
DisplayPath: p.sourcePath,
|
|
FingerprintKey: p.sourcePath,
|
|
ProjectHint: "provider",
|
|
}
|
|
}
|
|
|
|
func TestParseDiffProviderSourcesThreadsS3Metadata(t *testing.T) {
|
|
const uri = "s3://bucket/host/raw/claude/proj/session.jsonl"
|
|
engine := NewDiffEngine(dbtest.OpenTestDB(t), EngineConfig{
|
|
AgentDirs: map[parser.AgentType][]string{
|
|
parser.AgentClaude: {"s3://bucket/host/raw/claude"},
|
|
},
|
|
Machine: "devbox",
|
|
ProviderFactories: []parser.ProviderFactory{
|
|
s3ParseDiffProviderFactory{uri: uri},
|
|
},
|
|
ProviderMigrationModes: map[parser.AgentType]parser.ProviderMigrationMode{
|
|
parser.AgentClaude: parser.ProviderMigrationProviderAuthoritative,
|
|
},
|
|
})
|
|
|
|
files, err := engine.parseDiffProviderSources(context.Background(), parser.AgentClaude)
|
|
require.NoError(t, err)
|
|
require.Len(t, files, 1)
|
|
|
|
// The S3 discovery metadata must be threaded onto the DiscoveredFile so
|
|
// parse-diff ordering uses the real mtime instead of a zero value (which
|
|
// would treat the S3 session as oldest and skew --limit selection).
|
|
f := files[0]
|
|
assert.Equal(t, uri, f.Path)
|
|
assert.Equal(t, "remote-box", f.Machine)
|
|
assert.Equal(t, int64(4096), f.SourceSize)
|
|
assert.Equal(t, int64(1779012030000)*1_000_000, f.SourceMtime)
|
|
assert.Equal(t, "s3-fingerprint", f.SourceFingerprint)
|
|
assert.Equal(t, "myproj", f.Project)
|
|
}
|
|
|
|
func TestParseDiffSourceReliableForRacedDevinVirtualPath(t *testing.T) {
|
|
engine := NewDiffEngine(dbtest.OpenTestDB(t), EngineConfig{})
|
|
assert.False(
|
|
t,
|
|
engine.parseDiffSourceReliableForRaced(
|
|
parser.AgentDevin,
|
|
filepath.Join("/tmp", "devin", "cli", "sessions.db")+"#session-001",
|
|
),
|
|
)
|
|
}
|
|
|
|
type s3ParseDiffProviderFactory struct {
|
|
uri string
|
|
}
|
|
|
|
func (f s3ParseDiffProviderFactory) Definition() parser.AgentDef {
|
|
return parser.AgentDef{
|
|
Type: parser.AgentClaude,
|
|
DisplayName: "Claude Code",
|
|
FileBased: true,
|
|
}
|
|
}
|
|
|
|
func (f s3ParseDiffProviderFactory) Capabilities() parser.Capabilities {
|
|
return parser.Capabilities{
|
|
Source: parser.SourceCapabilities{
|
|
DiscoverSources: parser.CapabilitySupported,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (f s3ParseDiffProviderFactory) NewProvider(
|
|
parser.ProviderConfig,
|
|
) parser.Provider {
|
|
return s3ParseDiffProvider{
|
|
ProviderBase: parser.ProviderBase{
|
|
Def: f.Definition(),
|
|
Caps: f.Capabilities(),
|
|
},
|
|
uri: f.uri,
|
|
}
|
|
}
|
|
|
|
type s3ParseDiffProvider struct {
|
|
parser.ProviderBase
|
|
uri string
|
|
}
|
|
|
|
func (p s3ParseDiffProvider) Discover(
|
|
context.Context,
|
|
) ([]parser.SourceRef, error) {
|
|
return []parser.SourceRef{{
|
|
Provider: parser.AgentClaude,
|
|
Key: p.uri,
|
|
DisplayPath: p.uri,
|
|
Opaque: parser.S3DiscoveredSource{
|
|
URI: p.uri,
|
|
Project: "myproj",
|
|
Machine: "remote-box",
|
|
Size: 4096,
|
|
MtimeNS: int64(1779012030000) * 1_000_000,
|
|
Fingerprint: "s3-fingerprint",
|
|
},
|
|
}}, nil
|
|
}
|
|
|
|
func (p s3ParseDiffProvider) FindSource(
|
|
context.Context,
|
|
parser.FindSourceRequest,
|
|
) (parser.SourceRef, bool, error) {
|
|
return parser.SourceRef{}, false, nil
|
|
}
|
|
|
|
func (p s3ParseDiffProvider) Fingerprint(
|
|
context.Context,
|
|
parser.SourceRef,
|
|
) (parser.SourceFingerprint, error) {
|
|
return parser.SourceFingerprint{}, nil
|
|
}
|
|
|
|
func (p s3ParseDiffProvider) Parse(
|
|
context.Context,
|
|
parser.ParseRequest,
|
|
) (parser.ParseOutcome, error) {
|
|
return parser.ParseOutcome{}, nil
|
|
}
|