返回 DeepSeek-Reasonix
goal_durable_test.go
根目录 / internal / control / goal_durable_test.go
1 package control
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 "reasonix/internal/agent"
9 "reasonix/internal/event"
10 )
11
12 func TestSetGoalDurableRollsBackAllRuntimeStateOnWriteFailure(t *testing.T) {
13 dir := t.TempDir()
14 path := filepath.Join(dir, "session.jsonl")
15 exec := agent.New(nil, nil, agent.NewSession("sys"), agent.Options{}, event.Discard)
16 c := newOwnedTestController(t, Options{Executor: exec, SessionDir: dir, SessionPath: path, Label: "test"})
17 c.SetGoal("keep the old goal")
18 c.goals.mu.Lock()
19 c.goals.turnsUsed = 7
20 c.goals.tokensUsed = 4321
21 c.goals.noProgressTurns = 2
22 c.goals.lastContinuationReason = "preserve this reason"
23 c.goals.budgetExtensions = 1
24 c.goals.progressEvidence = []string{"existing-read"}
25 c.goals.mu.Unlock()
26 want := c.GoalRuntime()
27
28 notDirectory := filepath.Join(dir, "not-a-directory")
29 if err := os.WriteFile(notDirectory, []byte("block nested writes"), 0o600); err != nil {
30 t.Fatal(err)
31 }
32 c.goals.setStatePath(filepath.Join(notDirectory, "goal.json"))
33 if err := c.SetGoalDurable("replace the goal"); err == nil {
34 t.Fatal("SetGoalDurable succeeded despite an invalid sidecar parent")
35 }
36 if got := c.GoalRuntime(); got != want {
37 t.Fatalf("GoalRuntime() after failed durable write = %+v, want %+v", got, want)
38 }
39 c.goals.mu.Lock()
40 defer c.goals.mu.Unlock()
41 if len(c.goals.progressEvidence) != 1 || c.goals.progressEvidence[0] != "existing-read" {
42 t.Fatalf("Goal evidence after rollback = %v, want preserved", c.goals.progressEvidence)
43 }
44 }
45
46 func TestApplyComposerProfileIsTransactionalAndPreservesMatchingGoal(t *testing.T) {
47 dir := t.TempDir()
48 c := newOwnedTestController(t, Options{SessionDir: dir, SessionPath: filepath.Join(dir, "session.jsonl"), Label: "test"})
49 if err := c.SetGoalDurable("keep the old goal"); err != nil {
50 t.Fatal(err)
51 }
52 c.SetPlanMode(true)
53 c.SetToolApprovalMode(ToolApprovalAuto)
54 notDirectory := filepath.Join(dir, "not-a-directory")
55 if err := os.WriteFile(notDirectory, []byte("block nested writes"), 0o600); err != nil {
56 t.Fatal(err)
57 }
58 c.goals.setStatePath(filepath.Join(notDirectory, "goal.json"))
59 if _, err := c.ApplyComposerProfile(false, ToolApprovalYolo, "replace the goal"); err == nil {
60 t.Fatal("ApplyComposerProfile succeeded despite an invalid Goal sidecar parent")
61 }
62 if got := c.Goal(); got != "keep the old goal" {
63 t.Fatalf("goal = %q, want prior value", got)
64 }
65 if !c.PlanMode() {
66 t.Fatal("failed profile transaction changed plan mode")
67 }
68 if got := c.ToolApprovalMode(); got != ToolApprovalAuto {
69 t.Fatalf("tool approval mode = %q, want %q", got, ToolApprovalAuto)
70 }
71 c.goals.setStatePath(filepath.Join(dir, "goal.json"))
72 if !c.PauseGoal() {
73 t.Fatal("failed to pause goal")
74 }
75 want := c.GoalRuntime()
76 if _, err := c.ApplyComposerProfile(false, ToolApprovalYolo, "keep the old goal"); err != nil {
77 t.Fatal(err)
78 }
79 if got := c.GoalRuntime(); got != want {
80 t.Fatalf("matching profile reset paused Goal runtime: got %+v, want %+v", got, want)
81 }
82 if got := c.ToolApprovalMode(); got != ToolApprovalYolo {
83 t.Fatalf("tool approval mode = %q, want yolo", got)
84 }
85 }
86
86 lines GO