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

415 lines
13 KiB
Go

package main
import (
"context"
"database/sql"
"os"
"path/filepath"
"testing"
_ "github.com/mattn/go-sqlite3"
"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"
)
// newTestDB opens a fresh SQLite DB for a single test.
func newTestDB(t *testing.T) *db.DB {
t.Helper()
return dbtest.OpenTestDB(t)
}
// upsertSession inserts a session with minimal required fields.
func upsertSession(
t *testing.T, d *db.DB, id, agent, startedAt string,
) {
t.Helper()
s := db.Session{
ID: id,
Project: "test-project",
Machine: "local",
Agent: agent,
MessageCount: 1,
}
if startedAt != "" {
s.StartedAt = &startedAt
}
require.NoError(t, d.UpsertSession(s), "upsert %s", id)
}
func TestResolveSessionID_PrefixedInput_NoEvidence_UnchangedNotKnown(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// A prefixed input with no DB row and no disk evidence is
// returned unchanged so downstream lookup/error messages
// use what the caller typed, but known=false so the caller
// skips the on-demand sync that would only warn about a
// missing source file.
input := "codex:019d5490-fe31-7e62-838c-8ba4193f245d"
got, known := resolveRawSessionID(ctx, d, nil, input)
assert.Equal(t, input, got)
assert.False(t, known, "known should be false (no evidence)")
}
func TestResolveSessionID_HostPrefixedInput_ReturnedUnchanged(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// Host-prefixed IDs are unambiguously canonical remote IDs;
// resolution short-circuits without touching DB or disk.
input := "other-host~codex:abc-123"
got, known := resolveRawSessionID(ctx, d, nil, input)
assert.Equal(t, input, got)
assert.True(t, known, "known should be true (host-prefixed)")
}
func TestResolveSessionID_BareClaudeUUID_ExactMatch(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// Claude sessions have no prefix; the bare UUID is the
// canonical ID stored in sessions.id.
id := "11111111-1111-1111-1111-111111111111"
upsertSession(t, d, id, "claude", "2026-04-17T10:00:00Z")
got, known := resolveRawSessionID(ctx, d, nil, id)
assert.Equal(t, id, got)
assert.True(t, known, "known should be true (DB match)")
}
func TestResolveSessionID_BareCodexUUID_ResolvesToPrefixed(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
bare := "019d5490-fe31-7e62-838c-8ba4193f245d"
stored := "codex:" + bare
upsertSession(t, d, stored, "codex", "2026-04-17T10:00:00Z")
got, known := resolveRawSessionID(ctx, d, nil, bare)
assert.Equal(t, stored, got)
assert.True(t, known, "known should be true (DB match)")
}
func TestResolveSessionID_Ambiguous_MostRecentWins(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
bare := "22222222-2222-2222-2222-222222222222"
// Older codex session.
upsertSession(t, d, "codex:"+bare, "codex", "2026-04-16T10:00:00Z")
// Newer amp session with same raw UUID.
upsertSession(t, d, "amp:"+bare, "amp", "2026-04-17T10:00:00Z")
got, known := resolveRawSessionID(ctx, d, nil, bare)
assert.Equal(t, "amp:"+bare, got, "most recent should win")
assert.True(t, known)
}
func TestResolveSessionID_NotInDB_FoundOnDisk(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// Create a codex session file on disk: the probe path
// should resolve a bare raw UUID to the prefixed form.
codexDir := filepath.Join(t.TempDir(), "codex-sessions")
bare := "33333333-3333-3333-3333-333333333333"
dayDir := filepath.Join(codexDir, "2026", "04", "17")
require.NoError(t, os.MkdirAll(dayDir, 0o755), "mkdir")
fname := "rollout-2026-04-17T10-00-00-" + bare + ".jsonl"
fpath := filepath.Join(dayDir, fname)
require.NoError(t, os.WriteFile(fpath, []byte("{}\n"), 0o644), "write")
agentDirs := map[parser.AgentType][]string{
parser.AgentCodex: {codexDir},
}
got, known := resolveRawSessionID(ctx, d, agentDirs, bare)
assert.Equal(t, "codex:"+bare, got, "disk probe")
assert.True(t, known, "disk probe found match")
}
func TestResolveSessionID_NotFoundAnywhere_PassThrough(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
bare := "44444444-4444-4444-4444-444444444444"
got, known := resolveRawSessionID(ctx, d, nil, bare)
assert.Equal(t, bare, got, "pass-through")
assert.False(t, known, "known should be false (nothing found)")
}
func TestResolveSessionID_BareClaudeAndPrefixedSameUUID_ClaudeExactWins(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// Edge: a bare Claude UUID that ALSO exists as a prefixed
// session (e.g. codex:<same-uuid>). The Claude row is an
// exact match and should win over the suffix match.
bare := "55555555-5555-5555-5555-555555555555"
upsertSession(t, d, bare, "claude", "2026-04-16T10:00:00Z")
upsertSession(t, d, "codex:"+bare, "codex", "2026-04-17T10:00:00Z")
got, known := resolveRawSessionID(ctx, d, nil, bare)
assert.Equal(t, bare, got, "exact claude match")
assert.True(t, known)
}
func TestResolveSessionID_ExactMatchWinsOverNewerCollisions(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// Bare Claude session is the exact match but older than
// multiple prefixed sessions sharing the same suffix. The
// exact row must always win, even if a LIMIT on the suffix
// query would exclude it by recency.
bare := "88888888-8888-8888-8888-888888888888"
upsertSession(t, d, bare, "claude", "2026-04-10T10:00:00Z")
upsertSession(t, d, "codex:"+bare, "codex",
"2026-04-15T10:00:00Z")
upsertSession(t, d, "amp:"+bare, "amp",
"2026-04-17T10:00:00Z")
got, known := resolveRawSessionID(ctx, d, nil, bare)
assert.Equal(t, bare, got,
"exact match must beat newer suffix collisions")
assert.True(t, known)
}
func TestResolveSessionID_KimiRawID_ResolvesToPrefixed(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// Kimi raw IDs have the shape "<project-hash>:<session-uuid>".
// The stored canonical form prepends "kimi:".
raw := "proj-hash-abc:66666666-6666-6666-6666-666666666666"
stored := "kimi:" + raw
upsertSession(t, d, stored, "kimi", "2026-04-17T10:00:00Z")
got, known := resolveRawSessionID(ctx, d, nil, raw)
assert.Equal(t, stored, got, "kimi raw ID resolves")
assert.True(t, known)
}
func TestResolveSessionID_OpenClawRawID_ResolvesToPrefixed(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// OpenClaw raw IDs have the shape "<agentId>:<sessionId>".
raw := "main:abc-123"
stored := "openclaw:" + raw
upsertSession(t, d, stored, "openclaw", "2026-04-17T10:00:00Z")
got, known := resolveRawSessionID(ctx, d, nil, raw)
assert.Equal(t, stored, got, "openclaw raw ID resolves")
assert.True(t, known)
}
func TestResolveSessionID_CanonicalKimiID_ResolvesWhenInDB(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// A canonical Kimi ID already in the DB resolves via the
// exact-match branch. A canonical ID with no DB row and no
// disk evidence falls through to known=false so no
// misleading sync warning is emitted.
input := "kimi:proj-abc:77777777-7777-7777-7777-777777777777"
upsertSession(t, d, input, "kimi", "2026-04-17T10:00:00Z")
got, known := resolveRawSessionID(ctx, d, nil, input)
assert.Equal(t, input, got, "exact DB match")
assert.True(t, known, "exact DB match")
}
func TestResolveSessionID_CanonicalCodexID_OnDiskNotInDB(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// Canonical "codex:<uuid>" not yet synced but present on
// disk must resolve via the canonical disk probe, which strips
// the prefix before asking the agent source lookup.
codexDir := filepath.Join(t.TempDir(), "codex-sessions")
uuid := "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
dayDir := filepath.Join(codexDir, "2026", "04", "17")
require.NoError(t, os.MkdirAll(dayDir, 0o755), "mkdir")
fname := "rollout-2026-04-17T10-00-00-" + uuid + ".jsonl"
require.NoError(t, os.WriteFile(
filepath.Join(dayDir, fname), []byte("{}\n"), 0o644,
), "write")
agentDirs := map[parser.AgentType][]string{
parser.AgentCodex: {codexDir},
}
input := "codex:" + uuid
got, known := resolveRawSessionID(ctx, d, agentDirs, input)
assert.Equal(t, input, got, "canonical on disk")
assert.True(t, known, "canonical disk probe")
}
func TestResolveSessionID_ProviderAuthoritativeCursorOnDiskNotInDB(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
cursorDir := t.TempDir()
rawID := "provider-cursor"
transcriptPath := filepath.Join(
cursorDir,
"Users-fiona-Documents-demo",
"agent-transcripts",
rawID+".jsonl",
)
require.NoError(t, os.MkdirAll(filepath.Dir(transcriptPath), 0o755))
require.NoError(t, os.WriteFile(
transcriptPath,
[]byte(`{"role":"user","content":"hi"}`+"\n"),
0o644,
))
agentDirs := map[parser.AgentType][]string{
parser.AgentCursor: {cursorDir},
}
got, known := resolveRawSessionID(ctx, d, agentDirs, rawID)
assert.Equal(t, "cursor:"+rawID, got,
"provider FindSource should resolve unsynced raw cursor IDs")
assert.True(t, known, "provider disk probe")
got, known = resolveRawSessionID(ctx, d, agentDirs, "cursor:"+rawID)
assert.Equal(t, "cursor:"+rawID, got,
"canonical provider ID should resolve via provider FindSource")
assert.True(t, known, "canonical provider disk probe")
}
func TestResolveSessionID_DevinCanonicalID_OnDiskNotInDB(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
root := t.TempDir()
cliDir := filepath.Join(root, "cli")
transcriptsDir := filepath.Join(cliDir, "transcripts")
require.NoError(t, os.MkdirAll(transcriptsDir, 0o755))
dbPath := filepath.Join(cliDir, "sessions.db")
devinDB, err := sql.Open("sqlite3", dbPath)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, devinDB.Close()) })
_, err = devinDB.Exec(`
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
title TEXT,
working_directory TEXT,
model TEXT,
created_at INTEGER,
last_activity_at INTEGER,
hidden INTEGER NOT NULL DEFAULT 0
);
INSERT INTO sessions
(id, title, working_directory, model, created_at, last_activity_at, hidden)
VALUES
('session-123', 'Devin session', '/cwd/devin', 'devin-1', 1700000000000, 1700000001000, 0);
`)
require.NoError(t, err)
require.NoError(t, os.WriteFile(
filepath.Join(transcriptsDir, "session-123.json"),
[]byte(`{"messages":[]}`+"\n"),
0o644,
))
agentDirs := map[parser.AgentType][]string{
parser.AgentDevin: {root},
}
got, known := resolveRawSessionID(ctx, d, agentDirs, "devin:session-123")
assert.Equal(t, "devin:session-123", got)
assert.True(t, known,
"provider-backed Devin IDs should resolve via FindSource even though FileBased is false")
}
func TestResolveSessionID_RawOpenClawCollidesWithCodexPrefix(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// OpenClaw permits arbitrary alphanumeric-dash-underscore
// agent IDs, so a user may have one literally named "codex".
// The raw OpenClaw ID "codex:abc-123" is stored as
// "openclaw:codex:abc-123". Passing the raw form must not
// be short-circuited as a canonical Codex ID — DB suffix
// resolution must take precedence.
raw := "codex:abc-123"
stored := "openclaw:" + raw
upsertSession(t, d, stored, "openclaw",
"2026-04-17T10:00:00Z")
got, known := resolveRawSessionID(ctx, d, nil, raw)
assert.Equal(t, stored, got,
"raw openclaw must beat canonical-prefix short-circuit")
assert.True(t, known)
}
func TestResolveSessionID_UnderscoreID_NoFalseMatch(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
// Underscore is a LIKE wildcard in SQLite. If the query
// uses LIKE naively, a raw id "20260403_aaa" would match
// rows whose id ends with ":20260403Xaaa" (X = any char).
// Insert a decoy that would only match under naive LIKE
// semantics, plus a true match, and assert the true match
// wins.
raw := "20260403_aaa"
decoy := "codex:20260403Xaaa"
real := "codex:" + raw
upsertSession(t, d, decoy, "codex", "2026-04-16T10:00:00Z")
upsertSession(t, d, real, "codex", "2026-04-17T10:00:00Z")
got, known := resolveRawSessionID(ctx, d, nil, raw)
assert.Equal(t, real, got, "underscore is literal")
assert.True(t, known)
}
func TestAgentHasDiskSourceLookupIncludesFileBackedAgentsAndDevin(t *testing.T) {
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,
} {
def, ok := parser.AgentByType(agent)
require.True(t, ok, "agent %s", agent)
assert.True(t, agentHasDiskSourceLookup(def),
"token-use source probe must include %s", agent)
}
warpDef, ok := parser.AgentByType(parser.AgentWarp)
require.True(t, ok, "agent %s", parser.AgentWarp)
assert.False(t, agentHasDiskSourceLookup(warpDef),
"token-use source probe must exclude non-Devin non-file-backed agents")
}
func TestUsageExitCode_TokenData(t *testing.T) {
u := &db.SessionUsage{HasTokenData: true}
assert.Equal(t, tokenUseExitOK, usageExitCode(u))
}
func TestUsageExitCode_CostOnly(t *testing.T) {
u := &db.SessionUsage{HasTokenData: false, HasCost: true}
assert.Equal(t, tokenUseExitOK, usageExitCode(u),
"cost-only must not be exit 3")
}
func TestUsageExitCode_NoData(t *testing.T) {
u := &db.SessionUsage{}
assert.Equal(t, tokenUseExitNoTokenData, usageExitCode(u))
}
func TestUsageExitCode_NotFound(t *testing.T) {
assert.Equal(t, tokenUseExitNotFound, usageExitCode(nil))
}