返回 DeepSeek-Reasonix
tool_recovery_test.go
根目录 / internal / serve / tool_recovery_test.go
1 package serve
2
3 import (
4 "bytes"
5 "encoding/json"
6 "net/http"
7 "net/http/httptest"
8 "testing"
9
10 "reasonix/internal/agent"
11 "reasonix/internal/config"
12 "reasonix/internal/control"
13 "reasonix/internal/tool"
14 )
15
16 func recoveryHTTPFixture(t *testing.T) (*httptest.Server, control.ToolRecoverySnapshot) {
17 t.Helper()
18 bc := NewBroadcaster()
19 a := agent.New(nil, tool.NewRegistry(), agent.NewSession("sys"), agent.Options{}, bc)
20 c := control.New(control.Options{Executor: a, Sink: bc})
21 t.Cleanup(c.Close)
22 s := httptest.NewServer(New(c, bc, config.ServeConfig{}).Handler())
23 t.Cleanup(s.Close)
24 return s, c.ToolRecoverySnapshot()
25 }
26
27 func TestToolRecoveryHTTPRoutesAndFences(t *testing.T) {
28 s, v := recoveryHTTPFixture(t)
29 response, err := http.Get(s.URL + "/tool-recovery")
30 if err != nil {
31 t.Fatal(err)
32 }
33 var got control.ToolRecoverySnapshot
34 if err := json.NewDecoder(response.Body).Decode(&got); err != nil {
35 t.Fatal(err)
36 }
37 response.Body.Close()
38 if response.StatusCode != http.StatusOK || got.Revision != v.Revision || got.Calls == nil {
39 t.Fatalf("snapshot=%+v status=%d", got, response.StatusCode)
40 }
41 valid := control.ToolRecoveryRequest{SessionPath: v.SessionPath, RuntimeEpoch: v.RuntimeEpoch, Revision: v.Revision, Action: "unsupported"}
42 raw, _ := json.Marshal(valid)
43 for _, tc := range []struct {
44 name string
45 body []byte
46 path string
47 status int
48 }{
49 {"unsupported action", raw, "", 409},
50 {"unknown field", []byte(`{"action":"inspect","unexpected":true}`), "", 400},
51 {"stale session", raw, "/different/session.jsonl", 409},
52 {"stale revision", []byte(`{"action":"confirm","revision":"stale"}`), "", 409},
53 } {
54 t.Run(tc.name, func(t *testing.T) {
55 req, _ := http.NewRequest(http.MethodPost, s.URL+"/tool-recovery", bytes.NewReader(tc.body))
56 req.Header.Set("Content-Type", "application/json")
57 req.Header.Set(expectedSessionPathHeader, tc.path)
58 resp, err := http.DefaultClient.Do(req)
59 if err != nil {
60 t.Fatal(err)
61 }
62 defer resp.Body.Close()
63 if resp.StatusCode != tc.status {
64 t.Fatalf("status=%d want=%d", resp.StatusCode, tc.status)
65 }
66 })
67 }
68 }
69
69 lines GO