Files
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

382 lines
11 KiB
Go

package parser
import (
"database/sql"
"path/filepath"
"testing"
_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// warpSchema matches the relevant tables from Warp's SQLite database.
const warpSchema = `
CREATE TABLE agent_conversations (
id INTEGER PRIMARY KEY NOT NULL,
conversation_id TEXT NOT NULL,
conversation_data TEXT NOT NULL,
last_modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX ux_agent_conversations_conversation_id
ON agent_conversations (conversation_id);
CREATE TABLE ai_queries (
id INTEGER PRIMARY KEY NOT NULL,
exchange_id TEXT NOT NULL,
conversation_id TEXT NOT NULL,
start_ts DATETIME NOT NULL,
input TEXT NOT NULL,
working_directory TEXT,
output_status TEXT NOT NULL,
model_id TEXT NOT NULL DEFAULT '',
planning_model_id TEXT NOT NULL DEFAULT '',
coding_model_id TEXT NOT NULL DEFAULT ''
);
CREATE UNIQUE INDEX ux_ai_queries_exchange_id
ON ai_queries(exchange_id);
`
type WarpSeeder struct {
db *sql.DB
t *testing.T
}
func (s *WarpSeeder) AddConversation(
conversationID, conversationData, lastModified string,
) {
s.t.Helper()
_, err := s.db.Exec(
`INSERT INTO agent_conversations
(conversation_id, conversation_data, last_modified_at)
VALUES (?, ?, ?)`,
conversationID, conversationData, lastModified,
)
require.NoError(s.t, err, "add conversation")
}
func (s *WarpSeeder) AddExchange(
exchangeID, conversationID, startTS, input,
workingDir, outputStatus, modelID string,
) {
s.t.Helper()
_, err := s.db.Exec(
`INSERT INTO ai_queries
(exchange_id, conversation_id, start_ts, input,
working_directory, output_status, model_id)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
exchangeID, conversationID, startTS, input,
workingDir, outputStatus, modelID,
)
require.NoError(s.t, err, "add exchange")
}
func newWarpTestDB(t *testing.T) (string, *WarpSeeder, *sql.DB) {
t.Helper()
dbPath := filepath.Join(t.TempDir(), "warp.sqlite")
db, err := sql.Open("sqlite3", dbPath)
require.NoError(t, err, "open test db")
_, err = db.Exec(warpSchema)
require.NoError(t, err, "create schema")
seeder := &WarpSeeder{db: db, t: t}
return dbPath, seeder, db
}
func seedWarpConversation(t *testing.T, seeder *WarpSeeder) {
t.Helper()
convData := `{
"conversation_usage_metadata":{
"token_usage":[
{"model_id":"Claude Opus 4","warp_tokens":100000,"byok_tokens":0}
],
"tool_usage_metadata":{
"run_command_stats":{"count":3,"commands_executed":3},
"read_files_stats":{"count":2},
"search_codebase_stats":{"count":0},
"grep_stats":{"count":1},
"file_glob_stats":{"count":0},
"apply_file_diff_stats":{"count":1,"lines_added":5,"lines_removed":2,"files_changed":1},
"write_to_long_running_shell_command_stats":{"count":0},
"read_mcp_resource_stats":{"count":0},
"call_mcp_tool_stats":{"count":0},
"suggest_plan_stats":{"count":0},
"suggest_create_plan_stats":{"count":0},
"read_shell_command_output_stats":{"count":0},
"use_computer_stats":{"count":0}
}
}
}`
seeder.AddConversation(
"conv-001", convData, "2026-04-07 10:00:00",
)
// User message with query text
seeder.AddExchange(
"ex-001", "conv-001",
"2026-04-07 09:50:00.000000",
`[{"Query":{"text":"Fix the JSON parsing bug in parser.go","context":[]}}]`,
"/Users/alice/code/myproject",
`"Completed"`, "auto-genius",
)
// Intermediate exchange (tool call, no user input)
seeder.AddExchange(
"ex-002", "conv-001",
"2026-04-07 09:50:05.000000",
`[]`,
"/Users/alice/code/myproject",
`"Completed"`, "auto-genius",
)
// Follow-up user message
seeder.AddExchange(
"ex-003", "conv-001",
"2026-04-07 09:51:00.000000",
`[{"Query":{"text":"Now add a test for that fix","context":[]}}]`,
"/Users/alice/code/myproject",
`"Completed"`, "auto-genius",
)
}
func TestParseWarpDB_StandardConversation(t *testing.T) {
dbPath, seeder, db := newWarpTestDB(t)
defer db.Close()
seedWarpConversation(t, seeder)
sessions, err := parseWarpAll(dbPath, "testmachine")
require.NoError(t, err, "ParseWarpDB")
require.Len(t, sessions, 1, "sessions len")
s := sessions[0]
assert.Equal(t, "warp:conv-001", s.Session.ID, "ID")
assert.Equal(t, AgentWarp, s.Session.Agent, "Agent")
assert.Equal(t, "testmachine", s.Session.Machine, "Machine")
assert.Equal(t, "myproject", s.Session.Project, "Project")
assert.Equal(t, 2, s.Session.UserMessageCount, "UserMessageCount")
assert.Equal(t, "Fix the JSON parsing bug in parser.go", s.Session.FirstMessage, "FirstMessage")
wantPath := dbPath + "#conv-001"
assert.Equal(t, wantPath, s.Session.File.Path, "File.Path")
// Token usage from conversation_data
assert.True(t, s.Session.HasTotalOutputTokens, "HasTotalOutputTokens")
assert.Equal(t, 100000, s.Session.TotalOutputTokens, "TotalOutputTokens")
// Check user messages
var userMsgs, toolMsgs int
for _, m := range s.Messages {
if m.Role == RoleUser {
userMsgs++
}
if m.HasToolUse {
toolMsgs++
}
}
assert.Equal(t, 2, userMsgs, "userMsgs")
// 3 run_command + 2 read_files + 1 grep + 1 apply_file_diff = 7
assert.Equal(t, 7, toolMsgs, "toolMsgs")
}
func TestParseWarpSession_SingleConversation(t *testing.T) {
dbPath, seeder, db := newWarpTestDB(t)
defer db.Close()
seedWarpConversation(t, seeder)
sess, msgs, err := parseWarpSession(
dbPath, "conv-001", "testmachine",
)
require.NoError(t, err, "parseWarpSession")
require.NotNil(t, sess, "expected non-nil session")
assert.Equal(t, "warp:conv-001", sess.ID, "ID")
assert.Equal(t, AgentWarp, sess.Agent, "Agent")
// First user message
assert.Equal(t, RoleUser, msgs[0].Role, "msgs[0].Role")
assert.Equal(t, "Fix the JSON parsing bug in parser.go", msgs[0].Content, "msgs[0].Content")
// Second user message
assert.Equal(t, RoleUser, msgs[1].Role, "msgs[1].Role")
assert.Equal(t, "Now add a test for that fix", msgs[1].Content, "msgs[1].Content")
}
func TestListWarpSessionMeta(t *testing.T) {
dbPath, seeder, db := newWarpTestDB(t)
defer db.Close()
seedWarpConversation(t, seeder)
metas, err := ListWarpSessionMeta(dbPath)
require.NoError(t, err, "ListWarpSessionMeta")
require.Len(t, metas, 1, "metas len")
assert.Equal(t, "conv-001", metas[0].SessionID, "SessionID")
assert.Equal(t, dbPath+"#conv-001", metas[0].VirtualPath, "VirtualPath")
assert.NotZero(t, metas[0].FileMtime, "expected non-zero FileMtime")
}
func TestParseWarpDB_EmptyConversation(t *testing.T) {
dbPath, seeder, db := newWarpTestDB(t)
defer db.Close()
seeder.AddConversation(
"conv-empty", "{}", "2026-04-07 10:00:00",
)
sessions, err := parseWarpAll(dbPath, "m")
require.NoError(t, err, "ParseWarpDB")
assert.Empty(t, sessions, "sessions len")
}
func TestParseWarpDB_NoQueryText(t *testing.T) {
dbPath, seeder, db := newWarpTestDB(t)
defer db.Close()
seeder.AddConversation(
"conv-notext", "{}", "2026-04-07 10:00:00",
)
// Only empty exchanges
seeder.AddExchange(
"ex-x1", "conv-notext",
"2026-04-07 09:50:00",
`[]`, "/tmp", `"Completed"`, "auto",
)
sessions, err := parseWarpAll(dbPath, "m")
require.NoError(t, err, "ParseWarpDB")
assert.Empty(t, sessions, "sessions len")
}
func TestParseWarpDB_NonExistent(t *testing.T) {
sessions, err := parseWarpAll(
"/nonexistent/warp.sqlite", "m",
)
require.NoError(t, err)
assert.Nil(t, sessions, "expected nil sessions for non-existent db")
}
func TestExtractWarpQueryText(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{"empty", "", ""},
{"empty array", "[]", ""},
{"with text", `[{"Query":{"text":"hello world","context":[]}}]`, "hello world"},
{"no query key", `[{"Other":{}}]`, ""},
{"invalid json", `not json`, ""},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := extractWarpQueryText(tc.input)
assert.Equal(t, tc.want, got, "text")
})
}
}
func TestParseWarpTimestamp(t *testing.T) {
tests := []struct {
input string
year int
}{
{"2026-04-07 08:55:40", 2026},
{"2026-04-07 08:55:40.412505", 2026},
{"", 0},
}
for _, tc := range tests {
ts := parseWarpTimestamp(tc.input)
if tc.year == 0 {
assert.True(t, ts.IsZero(), "expected zero time for %q", tc.input)
} else {
assert.Equal(t, tc.year, ts.Year(), "year for %q", tc.input)
}
}
}
func TestWarpDBPath(t *testing.T) {
// Create a temp dir with warp.sqlite
dir := t.TempDir()
dbPath := filepath.Join(dir, "warp.sqlite")
// Before creating the file
assert.Empty(t, warpDBPath(dir), "not found")
// Create the file (sql.Open is lazy; Ping forces creation)
db, err := sql.Open("sqlite3", dbPath)
require.NoError(t, err)
require.NoError(t, db.Ping())
db.Close()
assert.Equal(t, dbPath, warpDBPath(dir), "found")
}
func TestParseWarpConversationMeta(t *testing.T) {
data := `{
"conversation_usage_metadata":{
"token_usage":[
{"warp_tokens":1000,"byok_tokens":200},
{"warp_tokens":500,"byok_tokens":0}
],
"tool_usage_metadata":{
"run_command_stats":{"count":5},
"read_files_stats":{"count":3},
"grep_stats":{"count":2},
"apply_file_diff_stats":{"count":1},
"search_codebase_stats":{"count":0},
"file_glob_stats":{"count":0},
"write_to_long_running_shell_command_stats":{"count":0},
"read_mcp_resource_stats":{"count":0},
"call_mcp_tool_stats":{"count":0},
"suggest_plan_stats":{"count":0},
"suggest_create_plan_stats":{"count":0},
"read_shell_command_output_stats":{"count":0},
"use_computer_stats":{"count":0}
}
}
}`
meta := parseWarpConversationMeta(data)
assert.Equal(t, 1700, meta.totalTokens, "totalTokens")
assert.Equal(t, 5, meta.toolStats.RunCommand, "RunCommand")
assert.Equal(t, 3, meta.toolStats.ReadFiles, "ReadFiles")
assert.Equal(t, 2, meta.toolStats.Grep, "Grep")
assert.Equal(t, 1, meta.toolStats.ApplyFileDiff, "ApplyFileDiff")
}
func TestParseWarpConversationMeta_Empty(t *testing.T) {
meta := parseWarpConversationMeta("{}")
assert.Equal(t, 0, meta.totalTokens, "totalTokens")
assert.Equal(t, 0, meta.toolStats.RunCommand, "RunCommand")
}
func TestSynthesizeWarpToolMessages(t *testing.T) {
meta := warpConversationMeta{
toolStats: warpToolStats{
RunCommand: 2,
ReadFiles: 1,
},
}
ordinal := 0
msgs := synthesizeWarpToolMessages(
meta, parseWarpTimestamp("2026-04-07 10:00:00"),
"auto", &ordinal,
)
require.Len(t, msgs, 3, "msgs len") // 2 + 1
assert.Equal(t, 3, ordinal, "ordinal after")
// All should be assistant messages with tool use
for _, m := range msgs {
assert.Equal(t, RoleAssistant, m.Role, "Role")
assert.True(t, m.HasToolUse, "expected HasToolUse=true")
assert.Len(t, m.ToolCalls, 1)
}
// Check categories
assert.Equal(t, "Bash", msgs[0].ToolCalls[0].Category, "tc[0].Category")
assert.Equal(t, "Read", msgs[2].ToolCalls[0].Category, "tc[2].Category")
}