Files
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

255 lines
9.3 KiB
Go

package sync
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.kenn.io/agentsview/internal/parser"
)
func TestEngineClassifyQoderPaths(t *testing.T) {
db := openTestDB(t)
root := t.TempDir()
engine := NewEngine(db, EngineConfig{
AgentDirs: map[parser.AgentType][]string{
parser.AgentQoder: {root},
},
Machine: "local",
})
mainPath := filepath.Join(root, "-Users-alice-project", "11111111-1111-4111-8111-111111111111.jsonl")
subPath := filepath.Join(root, "-Users-alice-project", "11111111-1111-4111-8111-111111111111", "subagents", "agent-123.jsonl")
sidecarPath := filepath.Join(root, "-Users-alice-project", "11111111-1111-4111-8111-111111111111-session.json")
statsPath := filepath.Join(root, "ai-stats", "usage.json")
rootAgentPath := filepath.Join(root, "-Users-alice-project", "agent-123.jsonl")
nestedPath := filepath.Join(root, "-Users-alice-project", "notes", "stray.jsonl")
for _, path := range []string{mainPath, subPath, sidecarPath, statsPath, rootAgentPath, nestedPath} {
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
require.NoError(t, os.WriteFile(path, []byte("{}\n"), 0o644))
}
files := engine.classifyPaths([]string{mainPath})
require.Len(t, files, 1, "main path did not classify")
got := files[0]
assert.Equal(t, mainPath, got.Path)
assert.Equal(t, "project", got.Project)
assert.Equal(t, parser.AgentQoder, got.Agent)
files = engine.classifyPaths([]string{subPath})
require.Len(t, files, 1, "subagent path did not classify")
got = files[0]
assert.Equal(t, subPath, got.Path)
assert.Equal(t, "project", got.Project)
assert.Equal(t, parser.AgentQoder, got.Agent)
files = engine.classifyPaths([]string{sidecarPath})
require.Len(t, files, 1, "sidecar path did not map to transcript")
got = files[0]
assert.Equal(t, mainPath, got.Path)
assert.Equal(t, "project", got.Project)
assert.Equal(t, parser.AgentQoder, got.Agent)
for _, path := range []string{statsPath, rootAgentPath, nestedPath} {
files = engine.classifyPaths([]string{path})
assert.Emptyf(t, files, "%s classified as %+v", path, files)
}
}
func TestEngineClassifyQoderProjectNamedSubagentsAsMainSession(t *testing.T) {
db := openTestDB(t)
root := t.TempDir()
engine := NewEngine(db, EngineConfig{
AgentDirs: map[parser.AgentType][]string{
parser.AgentQoder: {root},
},
Machine: "local",
})
path := filepath.Join(root, "subagents", "11111111-1111-4111-8111-111111111111.jsonl")
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
require.NoError(t, os.WriteFile(path, []byte("{}\n"), 0o644))
files := engine.classifyPaths([]string{path})
require.Len(t, files, 1, "path did not classify")
got := files[0]
assert.Equal(t, path, got.Path)
assert.Equal(t, "subagents", got.Project)
assert.Equal(t, parser.AgentQoder, got.Agent)
}
func TestEngineSyncQoderSameMessageIDAppendForceReplaces(t *testing.T) {
database := openTestDB(t)
root := t.TempDir()
engine := NewEngine(database, EngineConfig{
AgentDirs: map[parser.AgentType][]string{
parser.AgentQoder: {root},
},
Machine: "local",
})
rawID := "11111111-1111-4111-8111-111111111111"
sessionID := "qoder:" + rawID
path := filepath.Join(root, "-Users-alice-project", rawID+".jsonl")
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
initial := `{"type":"user","uuid":"u1","timestamp":"2026-06-04T09:47:20.000Z","message":{"role":"user","content":"hello"},"sessionId":"11111111-1111-4111-8111-111111111111"}
{"type":"assistant","uuid":"a1","parentUuid":"u1","timestamp":"2026-06-04T09:47:21.000Z","message":{"id":"msg_split","role":"assistant","model":"auto","stop_reason":"tool_use","content":[{"type":"text","text":"Hello"}]},"sessionId":"11111111-1111-4111-8111-111111111111"}
`
require.NoError(t, os.WriteFile(path, []byte(initial), 0o644))
engine.SyncAll(context.Background(), nil)
msgs, err := database.GetAllMessages(context.Background(), sessionID)
require.NoError(t, err)
require.Len(t, msgs, 2)
assert.Equal(t, "Hello", msgs[1].Content)
appended := `{"type":"assistant","uuid":"a2","parentUuid":"a1","timestamp":"2026-06-04T09:47:22.000Z","message":{"id":"msg_split","role":"assistant","model":"auto","stop_reason":"end_turn","content":[{"type":"text","text":"Hello world"}]},"sessionId":"11111111-1111-4111-8111-111111111111"}
`
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0o644)
require.NoError(t, err)
_, writeErr := f.WriteString(appended)
require.NoError(t, writeErr)
require.NoError(t, f.Close())
engine.SyncPaths([]string{path})
msgs, err = database.GetAllMessages(context.Background(), sessionID)
require.NoError(t, err)
require.Len(t, msgs, 2)
assert.Equal(t, "Hello world", msgs[1].Content)
}
func TestProcessFileQoderSameSizeSameMtimeSidecarRewriteReparses(t *testing.T) {
tests := []struct {
name string
seedCache bool
freshSync bool
}{
{name: "skip cache", seedCache: true},
{name: "db freshness", freshSync: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
database := openTestDB(t)
root := t.TempDir()
engine := NewEngine(database, EngineConfig{
AgentDirs: map[parser.AgentType][]string{
parser.AgentQoder: {root},
},
Machine: "local",
})
rawID := "11111111-1111-4111-8111-111111111111"
path := filepath.Join(root, "-Users-alice-project", rawID+".jsonl")
sidecarPath := strings.TrimSuffix(path, ".jsonl") + "-session.json"
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
require.NoError(t, os.WriteFile(path, []byte(`{"type":"user","uuid":"u1","timestamp":"2026-06-04T09:47:27.966Z","message":{"role":"user","content":"hello"},"sessionId":"11111111-1111-4111-8111-111111111111"}
`), 0o644))
initialSidecar := []byte(`{"title":"Initial title","working_dir":"/tmp/qoder-a"}`)
changedSidecar := []byte(`{"title":"Changed title","working_dir":"/tmp/qoder-a"}`)
require.Len(t, changedSidecar, len(initialSidecar),
"test must keep size stable so hash is the only freshness signal")
require.NoError(t, os.WriteFile(sidecarPath, initialSidecar, 0o644))
initialTime := time.Date(2026, time.June, 4, 10, 0, 0, 0, time.UTC)
require.NoError(t, os.Chtimes(path, initialTime, initialTime))
require.NoError(t, os.Chtimes(sidecarPath, initialTime, initialTime))
file := parser.DiscoveredFile{
Path: path,
Agent: parser.AgentQoder,
ProviderProcess: true,
}
first := engine.processFile(context.Background(), file)
require.NoError(t, first.err)
require.Len(t, first.results, 1)
initialMtime := first.results[0].Session.File.Mtime
initialHash := first.results[0].Session.File.Hash
require.Equal(t, initialTime.UnixNano(), initialMtime)
require.NotEmpty(t, initialHash)
require.Contains(t, first.cacheKey, "?source_hash="+initialHash)
writeProcessQoderResult(t, engine, first)
require.NoError(t, os.WriteFile(sidecarPath, changedSidecar, 0o644))
require.NoError(t, os.Chtimes(sidecarPath, initialTime, initialTime))
if tt.seedCache {
engine.cacheSkip(first.cacheKey, initialMtime)
}
if tt.freshSync {
engine = NewEngine(database, EngineConfig{
AgentDirs: map[parser.AgentType][]string{
parser.AgentQoder: {root},
},
Machine: "local",
})
}
second := engine.processFile(context.Background(), file)
require.NoError(t, second.err)
assert.False(t, second.skip)
require.Len(t, second.results, 1)
assert.Equal(t, "Changed title", second.results[0].Session.SessionName)
assert.Equal(t, "/tmp/qoder-a", second.results[0].Session.Cwd)
changedHash := second.results[0].Session.File.Hash
require.NotEmpty(t, changedHash)
require.NotEqual(t, initialHash, changedHash)
require.Contains(t, second.cacheKey, "?source_hash="+changedHash)
})
}
}
func TestSourceMtimeQoderIncludesSidecarMtime(t *testing.T) {
database := openTestDB(t)
root := t.TempDir()
engine := NewEngine(database, EngineConfig{
AgentDirs: map[parser.AgentType][]string{
parser.AgentQoder: {root},
},
Machine: "local",
})
rawID := "11111111-1111-4111-8111-111111111111"
path := filepath.Join(root, "-Users-alice-project", rawID+".jsonl")
sidecarPath := strings.TrimSuffix(path, ".jsonl") + "-session.json"
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
require.NoError(t, os.WriteFile(path, []byte(`{"type":"user","uuid":"u1","timestamp":"2026-06-04T09:47:27.966Z","message":{"role":"user","content":"hello"},"sessionId":"11111111-1111-4111-8111-111111111111"}
`), 0o644))
require.NoError(t, os.WriteFile(
sidecarPath,
[]byte(`{"title":"Sidecar title","working_dir":"/tmp/qoder"}`),
0o644,
))
transcriptTime := time.Date(2026, time.June, 4, 10, 0, 0, 0, time.UTC)
sidecarTime := transcriptTime.Add(5 * time.Minute)
require.NoError(t, os.Chtimes(path, transcriptTime, transcriptTime))
require.NoError(t, os.Chtimes(sidecarPath, sidecarTime, sidecarTime))
assert.Equal(t, sidecarTime.UnixNano(), engine.SourceMtime("qoder:"+rawID))
}
func writeProcessQoderResult(
t *testing.T,
engine *Engine,
result processResult,
) {
t.Helper()
written, _, failed, _ := engine.writeBatch(
[]pendingWrite{{
sess: result.results[0].Session,
msgs: result.results[0].Messages,
usageEvents: result.results[0].UsageEvents,
forceReplace: result.forceReplace,
}},
syncWriteDefault,
false,
)
require.Equal(t, 0, failed)
require.Equal(t, 1, written)
}