Files
tutti/packages/agent/store-sqlite/activity_turn_pages_test.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

138 lines
5.4 KiB
Go

package storesqlite
import (
"context"
"strings"
"testing"
)
func TestListSessionTurnSummariesPagesStableMetadataOnlyHistory(t *testing.T) {
store := openTestStore(t, testOptions(&staticProjectPaths{}))
ctx := context.Background()
for _, session := range []struct{ workspaceID, sessionID string }{
{workspaceID: "ws-1", sessionID: "session-1"},
{workspaceID: "ws-1", sessionID: "session-2"},
{workspaceID: "ws-2", sessionID: "session-1"},
} {
seedTurnTestSession(t, store, session.workspaceID, session.sessionID)
}
insertSummaryTestTurn := func(workspaceID, sessionID, turnID string, startedAt, updatedAt int64) {
t.Helper()
_, err := store.db.ExecContext(ctx, `
INSERT INTO workspace_agent_turns (
workspace_id, agent_session_id, turn_id, phase, outcome,
error_json, file_changes_json, completed_command_json,
started_at_unix_ms, settled_at_unix_ms, created_at_unix_ms, updated_at_unix_ms,
turn_origin
) VALUES (?, ?, ?, 'settled', 'completed', '{"message":"not part of the summary"}', '{"files":["not part of the summary"]}', ?, ?, ?, ?, ?, 'user_prompt')
`, workspaceID, sessionID, turnID, "{\"finalAssistantMessageId\":\"final-"+turnID+"\"}", startedAt, startedAt+1, startedAt, updatedAt)
if err != nil {
t.Fatalf("insert turn %s/%s/%s: %v", workspaceID, sessionID, turnID, err)
}
}
insertSummaryTestTurn("ws-1", "session-1", "turn-c", 30, 30)
insertSummaryTestTurn("ws-1", "session-1", "turn-z", 20, 20)
insertSummaryTestTurn("ws-1", "session-1", "turn-a", 20, 20)
// A late update must not move an older Turn into the recent-start order.
insertSummaryTestTurn("ws-1", "session-1", "turn-old", 10, 999)
insertSummaryTestTurn("ws-1", "session-2", "turn-other-session", 100, 100)
insertSummaryTestTurn("ws-2", "session-1", "turn-other-workspace", 100, 100)
first, err := store.ListSessionTurnSummaries(ctx, ListSessionTurnSummariesInput{
WorkspaceID: "ws-1", AgentSessionID: "session-1", Limit: 2,
})
if err != nil {
t.Fatalf("first page: %v", err)
}
assertSummaryTurnIDs(t, first.Turns, "turn-c", "turn-z")
if !first.HasMore || first.Turns[0].FinalAssistantMessageID != "final-turn-c" || first.Turns[0].Origin != "user_prompt" {
t.Fatalf("first page = %#v", first)
}
second, err := store.ListSessionTurnSummaries(ctx, ListSessionTurnSummariesInput{
WorkspaceID: "ws-1", AgentSessionID: "session-1", Limit: 2,
Before: &SessionTurnCursor{StartedAtUnixMS: first.Turns[1].StartedAtUnixMS, TurnID: first.Turns[1].TurnID},
})
if err != nil {
t.Fatalf("second page: %v", err)
}
assertSummaryTurnIDs(t, second.Turns, "turn-a", "turn-old")
if second.HasMore {
t.Fatalf("second page hasMore = true, want false: %#v", second)
}
}
func TestListSessionTurnSummariesValidatesBoundsAndUsesPagingIndex(t *testing.T) {
store := openTestStore(t, testOptions(&staticProjectPaths{}))
ctx := context.Background()
for _, input := range []ListSessionTurnSummariesInput{
{WorkspaceID: "ws-1", AgentSessionID: "session-1", Limit: 0},
{WorkspaceID: "ws-1", AgentSessionID: "session-1", Limit: maxSessionTurnSummaryPageSize + 1},
{WorkspaceID: "ws-1", AgentSessionID: "session-1", Limit: 1, Before: &SessionTurnCursor{}},
} {
if _, err := store.ListSessionTurnSummaries(ctx, input); err == nil {
t.Fatalf("ListSessionTurnSummaries(%#v) error = nil", input)
}
}
rows, err := store.db.QueryContext(ctx, `EXPLAIN QUERY PLAN`+sessionTurnSummarySelectSQL+`
WHERE workspace_id = ? AND agent_session_id = ?
ORDER BY started_at_unix_ms DESC, turn_id DESC
LIMIT ?
`, "ws-1", "session-1", 4)
if err != nil {
t.Fatalf("explain session Turn page: %v", err)
}
defer rows.Close()
var plan strings.Builder
for rows.Next() {
var id, parent, unused int
var detail string
if err := rows.Scan(&id, &parent, &unused, &detail); err != nil {
t.Fatalf("scan query plan: %v", err)
}
plan.WriteString(detail)
}
if err := rows.Err(); err != nil {
t.Fatalf("iterate query plan: %v", err)
}
if !strings.Contains(plan.String(), "idx_workspace_agent_turns_session_started_desc") || strings.Contains(plan.String(), "USE TEMP B-TREE") {
t.Fatalf("query plan = %q, want paging index without temporary sort", plan.String())
}
}
func TestSessionTurnPageIndexMigrationRepairsMissingIndex(t *testing.T) {
store := openTestStore(t, testOptions(&staticProjectPaths{}))
ctx := context.Background()
if _, err := store.db.ExecContext(ctx, `DROP INDEX idx_workspace_agent_turns_session_started_desc`); err != nil {
t.Fatalf("drop session Turn page index: %v", err)
}
if _, err := store.db.ExecContext(ctx, `DELETE FROM agent_store_schema_migrations WHERE id = ?`, schemaMigrationWorkspaceAgentSessionTurnPageIndexV1); err != nil {
t.Fatalf("reset session Turn page migration: %v", err)
}
if err := store.Migrate(ctx); err != nil {
t.Fatalf("Migrate(): %v", err)
}
var count int
if err := store.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' AND name = 'idx_workspace_agent_turns_session_started_desc'`).Scan(&count); err != nil {
t.Fatalf("count session Turn page index: %v", err)
}
if count != 1 {
t.Fatalf("session Turn page index count = %d, want 1", count)
}
}
func assertSummaryTurnIDs(t *testing.T, turns []SessionTurnSummary, want ...string) {
t.Helper()
if len(turns) != len(want) {
t.Fatalf("turns = %#v, want ids %#v", turns, want)
}
for index, turn := range turns {
if turn.TurnID != want[index] {
t.Fatalf("turn %d = %#v, want id %q", index, turn, want[index])
}
}
}