返回 DeepSeek-Reasonix
scratch_scope_test.go
根目录 / internal / agent / scratch_scope_test.go
1 package agent
2
3 import (
4 "encoding/json"
5 "path/filepath"
6 "testing"
7
8 "reasonix/internal/checkpoint"
9 "reasonix/internal/event"
10 "reasonix/internal/evidence"
11 "reasonix/internal/provider"
12 "reasonix/internal/sessiontemp"
13 "reasonix/internal/tool"
14 )
15
16 func TestScratchScriptExecutionKeepsCheckpointCoverageFailClosed(t *testing.T) {
17 workspace := t.TempDir()
18 scratch := t.TempDir()
19 script := filepath.Join(scratch, "probe.py")
20 store := checkpoint.New("", workspace)
21 store.Begin(1, "probe", 0)
22 observer := checkpoint.NewMutationObserver(checkpoint.ObserverOptions{Store: store})
23 a := New(nil, tool.NewRegistry(), NewSession(""), Options{
24 WriteWorkspaceRoot: workspace,
25 MutationObserver: observer,
26 }, event.Discard)
27 args, err := json.Marshal(map[string]string{"command": "python " + script})
28 if err != nil {
29 t.Fatal(err)
30 }
31 plan := &toolCallPlan{
32 call: provider.ToolCall{Name: "bash", Arguments: string(args)},
33 tool: fakeTool{name: "bash", readOnly: false},
34 evidenceName: "bash",
35 evidenceArgs: args,
36 }
37 a.observeBeforeMutation(t.Context(), plan)
38 meta := store.List()
39 if len(meta) != 1 || len(meta[0].CoverageGaps) != 1 || meta[0].CoverageGaps[0].Reason != checkpoint.GapBashSideEffect {
40 t.Fatalf("coverage gaps = %+v, want bash_side_effect", meta)
41 }
42 }
43
44 func TestRecordToolReceiptsKeepsScratchScriptExecutionFailClosed(t *testing.T) {
45 workspace := t.TempDir()
46 manager := sessiontemp.NewWithRoot(t.TempDir())
47 manager.Retain()
48 defer manager.Release()
49 lease, err := manager.Acquire()
50 if err != nil {
51 t.Fatal(err)
52 }
53 script := filepath.Join(lease.Dir(), "probe.py")
54 defer lease.Release()
55
56 a := New(nil, tool.NewRegistry(), NewSession(""), Options{
57 WriteWorkspaceRoot: workspace,
58 SessionTemp: manager,
59 }, event.Discard)
60 for _, background := range []bool{false, true} {
61 args, err := json.Marshal(map[string]any{
62 "command": "python " + script,
63 "run_in_background": background,
64 })
65 if err != nil {
66 t.Fatal(err)
67 }
68 plan := &toolCallPlan{
69 call: provider.ToolCall{Name: "bash", Arguments: string(args)},
70 tool: fakeTool{name: "bash", readOnly: false},
71 evidenceName: "bash",
72 evidenceArgs: args,
73 effects: evidence.ClassifyToolCall("bash", args, false),
74 }
75 a.recordToolReceipts(plan, "ok", nil, nil)
76 receipts := a.task.ledger.Receipts()
77 got := receipts[len(receipts)-1]
78 if got.DeliveryScope == evidence.WriteScopeScratch {
79 t.Fatalf("background=%v receipt = %+v, script location cannot prove its side effects", background, got)
80 }
81 }
82 }
83
83 lines GO