Files
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

190 lines
7.6 KiB
Go

package storesqlite
import (
"context"
"errors"
"testing"
)
func TestBindTurnIdentityAnchorFlattensAndPreservesIdentity(t *testing.T) {
t.Parallel()
store := openTestStore(t, testOptions(&staticProjectPaths{}))
seedTurnTestSession(t, store, "ws-1", "session-1")
if _, err := store.db.Exec(`
INSERT INTO workspace_agent_turns (
workspace_id, agent_session_id, turn_id, identity_anchor_turn_id, phase,
started_at_unix_ms, created_at_unix_ms, updated_at_unix_ms
) VALUES
('ws-1', 'session-1', 'root-turn', NULL, 'submitted', 1, 1, 1),
('ws-1', 'session-1', 'intermediate-turn', 'root-turn', 'submitted', 2, 2, 2),
('ws-1', 'session-1', 'child-turn', NULL, 'submitted', 3, 3, 3),
('ws-1', 'session-1', 'other-turn', NULL, 'submitted', 4, 4, 4)
`); err != nil {
t.Fatal(err)
}
tx, err := store.db.BeginTx(context.Background(), nil)
if err != nil {
t.Fatal(err)
}
turn, changed, err := bindTurnIdentityAnchorTx(
context.Background(), tx, "ws-1", "session-1", "child-turn", "intermediate-turn", 10,
)
if err != nil || !changed || turn.IdentityAnchorTurnID != "root-turn" {
_ = tx.Rollback()
t.Fatalf("bind flattened identity anchor: turn=%#v changed=%v error=%v", turn, changed, err)
}
turn, changed, err = bindTurnIdentityAnchorTx(
context.Background(), tx, "ws-1", "session-1", "child-turn", "root-turn", 11,
)
if err != nil || changed || turn.IdentityAnchorTurnID != "root-turn" {
_ = tx.Rollback()
t.Fatalf("repeat identity anchor: turn=%#v changed=%v error=%v", turn, changed, err)
}
if _, _, err := bindTurnIdentityAnchorTx(
context.Background(), tx, "ws-1", "session-1", "child-turn", "other-turn", 12,
); !errors.Is(err, ErrTurnIdentityAnchorConflict) {
_ = tx.Rollback()
t.Fatalf("identity anchor rewrite error = %v", err)
}
if err := tx.Commit(); err != nil {
t.Fatal(err)
}
}
func TestTurnIdentityAnchorMigrationBackfillsOnlyProvenContinuation(t *testing.T) {
t.Parallel()
store := openTestStore(t, testOptions(&staticProjectPaths{}))
seedTurnTestSession(t, store, "ws-1", "session-1")
if _, err := store.db.Exec(`
INSERT INTO workspace_agent_turns (
workspace_id, agent_session_id, turn_id, phase,
started_at_unix_ms, created_at_unix_ms, updated_at_unix_ms
) VALUES
('ws-1', 'session-1', 'plan-turn', 'submitted', 1, 1, 1),
('ws-1', 'session-1', 'implementation-turn', 'submitted', 2, 2, 2),
('ws-1', 'session-1', 'unproven-turn', 'submitted', 3, 3, 3);
INSERT INTO workspace_agent_messages (
workspace_id, agent_session_id, message_id, version, turn_id, role, kind,
status, payload_json, occurred_at_unix_ms, created_at_unix_ms, updated_at_unix_ms
) VALUES (
'ws-1', 'session-1', 'implementation-submit', 1, 'implementation-turn',
'user', 'text', 'completed', '{"clientSubmitId":"plan-decision:operation-1"}', 4, 4, 4
);
INSERT INTO workspace_agent_runtime_operations (
operation_id, workspace_id, agent_session_id, kind, status, result,
turn_id, request_id, payload_json, attempt, version, last_error,
created_at_unix_ms, updated_at_unix_ms, completed_at_unix_ms
) VALUES (
'operation-1', 'ws-1', 'session-1', 'plan_decision', 'completed', 'applied',
'plan-turn', 'plan-turn',
'{"promptKind":"plan-implementation","action":"implement","idempotencyKey":"decision-1","step":"send_confirmed","clientSubmitId":"plan-decision:operation-1","confirmedTurnId":"implementation-turn"}',
1, 1, '', 5, 6, 6
);
DELETE FROM agent_store_schema_migrations
WHERE id = 'workspace_agent_turn_identity_anchor_v1';
`); err != nil {
t.Fatal(err)
}
if err := store.applyWorkspaceAgentTurnIdentityAnchorV1(context.Background()); err != nil {
t.Fatal(err)
}
var implementationAnchor, unprovenAnchor string
if err := store.db.QueryRow(`
SELECT COALESCE(identity_anchor_turn_id, '')
FROM workspace_agent_turns
WHERE workspace_id='ws-1' AND agent_session_id='session-1' AND turn_id='implementation-turn'
`).Scan(&implementationAnchor); err != nil {
t.Fatal(err)
}
if err := store.db.QueryRow(`
SELECT COALESCE(identity_anchor_turn_id, '')
FROM workspace_agent_turns
WHERE workspace_id='ws-1' AND agent_session_id='session-1' AND turn_id='unproven-turn'
`).Scan(&unprovenAnchor); err != nil {
t.Fatal(err)
}
if implementationAnchor != "plan-turn" || unprovenAnchor != "" {
t.Fatalf("migration anchors: implementation=%q unproven=%q", implementationAnchor, unprovenAnchor)
}
}
func TestTurnIdentityAnchorMigrationTopologicallyFlattensContinuationChain(t *testing.T) {
t.Parallel()
store := openTestStore(t, testOptions(&staticProjectPaths{}))
seedTurnTestSession(t, store, "ws-1", "session-1")
if _, err := store.db.Exec(`
INSERT INTO workspace_agent_turns (
workspace_id, agent_session_id, turn_id, phase,
started_at_unix_ms, created_at_unix_ms, updated_at_unix_ms
) VALUES
('ws-1', 'session-1', 'turn-a', 'submitted', 1, 1, 1),
('ws-1', 'session-1', 'turn-b', 'submitted', 2, 2, 2),
('ws-1', 'session-1', 'turn-c', 'submitted', 3, 3, 3);
INSERT INTO workspace_agent_messages (
workspace_id, agent_session_id, message_id, version, turn_id, role, kind,
status, payload_json, occurred_at_unix_ms, created_at_unix_ms, updated_at_unix_ms
) VALUES
('ws-1', 'session-1', 'submit-b', 1, 'turn-b', 'user', 'text', 'completed',
'{"clientSubmitId":"plan-decision:operation-z-parent"}', 4, 4, 4),
('ws-1', 'session-1', 'submit-c', 2, 'turn-c', 'user', 'text', 'completed',
'{"clientSubmitId":"plan-decision:operation-a-descendant"}', 5, 5, 5);
INSERT INTO workspace_agent_runtime_operations (
operation_id, workspace_id, agent_session_id, kind, status, result,
turn_id, request_id, payload_json, attempt, version, last_error,
created_at_unix_ms, updated_at_unix_ms, completed_at_unix_ms
) VALUES
('operation-z-parent', 'ws-1', 'session-1', 'plan_decision', 'completed', 'applied',
'turn-a', 'turn-a',
'{"promptKind":"plan-implementation","action":"implement","idempotencyKey":"decision-parent","step":"send_confirmed","clientSubmitId":"plan-decision:operation-z-parent","confirmedTurnId":"turn-b"}',
1, 1, '', 6, 7, 10),
('operation-a-descendant', 'ws-1', 'session-1', 'plan_decision', 'completed', 'applied',
'turn-b', 'turn-b',
'{"promptKind":"plan-implementation","action":"implement","idempotencyKey":"decision-descendant","step":"send_confirmed","clientSubmitId":"plan-decision:operation-a-descendant","confirmedTurnId":"turn-c"}',
1, 1, '', 8, 9, 10);
DELETE FROM agent_store_schema_migrations
WHERE id = 'workspace_agent_turn_identity_anchor_v1';
`); err != nil {
t.Fatal(err)
}
if err := store.applyWorkspaceAgentTurnIdentityAnchorV1(context.Background()); err != nil {
t.Fatal(err)
}
for _, expectation := range []struct {
turnID string
wantAnchor string
}{
{turnID: "turn-b", wantAnchor: "turn-a"},
{turnID: "turn-c", wantAnchor: "turn-a"},
} {
var anchor string
if err := store.db.QueryRow(`
SELECT COALESCE(identity_anchor_turn_id, '')
FROM workspace_agent_turns
WHERE workspace_id='ws-1' AND agent_session_id='session-1' AND turn_id=?
`, expectation.turnID).Scan(&anchor); err != nil {
t.Fatal(err)
}
if anchor != expectation.wantAnchor {
t.Fatalf("turn %s identity anchor = %q, want %q", expectation.turnID, anchor, expectation.wantAnchor)
}
}
}
func TestOrderHistoricalTurnIdentityAnchorRepairsRejectsCycle(t *testing.T) {
t.Parallel()
_, err := orderHistoricalTurnIdentityAnchorRepairs([]historicalTurnIdentityAnchor{
{workspaceID: "ws-1", agentSessionID: "session-1", turnID: "turn-a", anchorTurnID: "turn-b"},
{workspaceID: "ws-1", agentSessionID: "session-1", turnID: "turn-b", anchorTurnID: "turn-a"},
})
if !errors.Is(err, ErrTurnIdentityAnchorConflict) {
t.Fatalf("cycle error = %v, want %v", err, ErrTurnIdentityAnchorConflict)
}
}