Files
tutti/packages/agent/store-sqlite/goal_generation_fences_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

123 lines
5.4 KiB
Go

package storesqlite
import (
"context"
"errors"
"testing"
)
func TestGoalGenerationFencePersistsExactGenerationAndLeaseRecovery(t *testing.T) {
t.Parallel()
store := openTestStore(t, testOptions(&staticProjectPaths{}))
ctx := context.Background()
if _, err := store.ReportSessionState(ctx, SessionStateReport{
WorkspaceID: "ws", AgentSessionID: "session", Provider: "claude-code", OccurredAtUnixMS: 10,
}); err != nil {
t.Fatal(err)
}
target, _, created, err := store.PrepareGoalControlOperation(ctx, GoalControlOperationPrepare{
OperationID: "goal-op", WorkspaceID: "ws", AgentSessionID: "session",
Action: "set", Objective: "keep working", ClientSubmitID: "goal-submit", OccurredAtUnixMS: 20,
})
if err != nil || !created {
t.Fatalf("prepare target created=%v error=%v", created, err)
}
fence, created, err := store.PrepareGoalGenerationFence(ctx, GoalGenerationFencePrepare{
FenceID: "fence-1", WorkspaceID: "ws", AgentSessionID: "session",
TargetOperationID: "goal-op", ClientSubmitID: "fence-submit", Reason: "binding_revoked",
OccurredAtUnixMS: 30,
})
if err != nil || !created {
t.Fatalf("prepare fence=%#v created=%v error=%v", fence, created, err)
}
if fence.TargetRevision != target.GoalRevision || fence.TargetRepairEpoch != target.RepairEpoch ||
fence.Status != GoalGenerationFenceStatusPending || fence.Reason != "binding_revoked" {
t.Fatalf("prepared fence=%#v target=%#v", fence, target)
}
replayed, created, err := store.PrepareGoalGenerationFence(ctx, GoalGenerationFencePrepare{
FenceID: "fence-1", WorkspaceID: "ws", AgentSessionID: "session",
TargetOperationID: "goal-op", ClientSubmitID: "fence-submit", Reason: "binding_revoked",
OccurredAtUnixMS: 31,
})
if err != nil || created || replayed.FenceID != fence.FenceID {
t.Fatalf("replay fence=%#v created=%v error=%v", replayed, created, err)
}
claimable, err := store.ListClaimableGoalGenerationFences(ctx, ListClaimableGoalGenerationFencesInput{
NowUnixMS: 40, Limit: 10,
})
if err != nil || len(claimable) != 1 || claimable[0].FenceID != "fence-1" {
t.Fatalf("claimable=%#v error=%v", claimable, err)
}
leased, claimed, err := store.ClaimGoalGenerationFence(ctx, ClaimGoalGenerationFenceInput{
FenceID: "fence-1", LeaseOwner: "worker-a", NowUnixMS: 40, LeaseExpiresAtMS: 80,
})
if err != nil || !claimed || leased.Status != GoalGenerationFenceStatusProcessing || leased.Attempt != 1 {
t.Fatalf("leased=%#v claimed=%v error=%v", leased, claimed, err)
}
if _, claimed, err := store.ClaimGoalGenerationFence(ctx, ClaimGoalGenerationFenceInput{
FenceID: "fence-1", LeaseOwner: "worker-b", NowUnixMS: 50, LeaseExpiresAtMS: 90,
}); err != nil || claimed {
t.Fatalf("second claim claimed=%v error=%v", claimed, err)
}
if count, err := store.RequeueLeasedGoalGenerationFencesOnStartup(ctx, 60); err != nil || count != 1 {
t.Fatalf("startup requeue count=%d error=%v", count, err)
}
leased, claimed, err = store.ClaimGoalGenerationFence(ctx, ClaimGoalGenerationFenceInput{
FenceID: "fence-1", LeaseOwner: "worker-b", NowUnixMS: 60, LeaseExpiresAtMS: 100,
})
if err != nil || !claimed || leased.Attempt != 2 {
t.Fatalf("reclaimed=%#v claimed=%v error=%v", leased, claimed, err)
}
completed, changed, err := store.CompleteGoalGenerationFence(ctx, CompleteGoalGenerationFenceInput{
FenceID: "fence-1", LeaseOwner: "worker-b", ClearOperationID: "clear-op", OccurredAtUnixMS: 70,
})
if err != nil || !changed || completed.Status != GoalGenerationFenceStatusCompleted ||
completed.ClearOperationID != "clear-op" || completed.CompletedAtUnixMS != 70 {
t.Fatalf("completed=%#v changed=%v error=%v", completed, changed, err)
}
persisted, err := store.ListGoalGenerationFencesForSession(ctx, "ws", "session")
if err != nil || len(persisted) != 1 || persisted[0].TargetOperationID != "goal-op" {
t.Fatalf("persisted=%#v error=%v", persisted, err)
}
}
func TestGoalGenerationFenceRejectsCrossSessionAndIdentityReuse(t *testing.T) {
t.Parallel()
store := openTestStore(t, testOptions(&staticProjectPaths{}))
ctx := context.Background()
for _, sessionID := range []string{"session-a", "session-b"} {
if _, err := store.ReportSessionState(ctx, SessionStateReport{
WorkspaceID: "ws", AgentSessionID: sessionID, Provider: "codex", OccurredAtUnixMS: 10,
}); err != nil {
t.Fatal(err)
}
}
if _, _, _, err := store.PrepareGoalControlOperation(ctx, GoalControlOperationPrepare{
OperationID: "goal-op", WorkspaceID: "ws", AgentSessionID: "session-a",
Action: "set", Objective: "work", OccurredAtUnixMS: 20,
}); err != nil {
t.Fatal(err)
}
if _, _, err := store.PrepareGoalGenerationFence(ctx, GoalGenerationFencePrepare{
FenceID: "cross-session", WorkspaceID: "ws", AgentSessionID: "session-b",
TargetOperationID: "goal-op", ClientSubmitID: "cross-submit", OccurredAtUnixMS: 30,
}); !errors.Is(err, ErrGoalGenerationFenceConflict) {
t.Fatalf("cross-session error=%v", err)
}
if _, _, err := store.PrepareGoalGenerationFence(ctx, GoalGenerationFencePrepare{
FenceID: "fence", WorkspaceID: "ws", AgentSessionID: "session-a",
TargetOperationID: "goal-op", ClientSubmitID: "submit-1", OccurredAtUnixMS: 40,
}); err != nil {
t.Fatal(err)
}
if _, _, err := store.PrepareGoalGenerationFence(ctx, GoalGenerationFencePrepare{
FenceID: "fence", WorkspaceID: "ws", AgentSessionID: "session-a",
TargetOperationID: "goal-op", ClientSubmitID: "submit-2", OccurredAtUnixMS: 50,
}); !errors.Is(err, ErrGoalGenerationFenceConflict) {
t.Fatalf("identity reuse error=%v", err)
}
}