返回 DeepSeek-Reasonix
attachment_identity_test.go
根目录 / internal / control / attachment_identity_test.go
1 package control_test
2
3 import (
4 "bytes"
5 "context"
6 "encoding/base64"
7 "errors"
8 "os"
9 "path/filepath"
10 "testing"
11
12 "reasonix/internal/control"
13 "reasonix/internal/event"
14 "reasonix/internal/permission"
15 )
16
17 const identityPNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
18
19 func newIdentityController(t *testing.T, workspace string) *control.Controller {
20 t.Helper()
21 c := control.New(control.Options{
22 WorkspaceRoot: workspace,
23 Sink: event.Discard,
24 Policy: permission.New("allow", nil, nil, nil),
25 })
26 t.Cleanup(func() { c.Close() })
27 return c
28 }
29
30 func TestIndependentAttachmentIdentityUsesBoundWorkspace(t *testing.T) {
31 processDir := t.TempDir()
32 first := t.TempDir()
33 second := t.TempDir()
34 t.Chdir(processDir)
35 raw, err := base64.StdEncoding.DecodeString(identityPNG)
36 if err != nil {
37 t.Fatal(err)
38 }
39 firstPath, err := control.SaveImageBytesInRoot(first, "image/png", raw)
40 if err != nil {
41 t.Fatal(err)
42 }
43 secondBytes := append([]byte(nil), raw...)
44 secondBytes[len(secondBytes)-1] ^= 1
45 secondPath, err := control.SaveImageBytesInRoot(second, "image/png", secondBytes)
46 if err != nil {
47 t.Fatal(err)
48 }
49 one, err := control.ImageDataURLInRoot(first, firstPath)
50 if err != nil {
51 t.Fatal(err)
52 }
53 two, err := control.ImageDataURLInRoot(second, secondPath)
54 if err != nil {
55 t.Fatal(err)
56 }
57 if one == two {
58 t.Fatal("same-named images in separate workspaces resolved to the same bytes")
59 }
60 if _, err := os.Stat(filepath.Join(processDir, ".reasonix")); !os.IsNotExist(err) {
61 t.Fatal("attachment write used the process working directory")
62 }
63 }
64
65 func TestIndependentAttachmentAdmissionKeepsDraftOnFailure(t *testing.T) {
66 workspace := t.TempDir()
67 c := newIdentityController(t, workspace)
68 draft, err := c.StageImage(context.Background(), "shot.png", "image/png", "data:image/png;base64,"+identityPNG)
69 if err != nil {
70 t.Fatal(err)
71 }
72 _, err = c.SubmitIdentified(control.SubmissionRequest{
73 ID: "sub-keep-draft",
74 Input: "inspect @.reasonix/attachments/missing.png",
75 })
76 var failures control.ImageReferenceFailures
77 if !errors.As(err, &failures) {
78 t.Fatalf("submit = %v, want image reference failure", err)
79 }
80 got, raw, err := c.ReadDraftImage(context.Background(), draft.ID)
81 if err != nil || got.ID != draft.ID || len(raw) == 0 {
82 t.Fatalf("draft was released after a rejected turn: %v %+v", err, got)
83 }
84 }
85
86 func TestIndependentAttachmentPermissionProfilesMatch(t *testing.T) {
87 workspace := t.TempDir()
88 raw, err := base64.StdEncoding.DecodeString(identityPNG)
89 if err != nil {
90 t.Fatal(err)
91 }
92 var want []byte
93 for _, mode := range []string{control.ToolApprovalWorkspaceWrite, control.ToolApprovalDangerFullAccess} {
94 c := newIdentityController(t, workspace)
95 c.SetToolApprovalMode(mode)
96 draft, err := c.StageImage(context.Background(), "shot.png", "image/png", "data:image/png;base64,"+base64.StdEncoding.EncodeToString(raw))
97 if err != nil {
98 t.Fatalf("mode %s stage: %v", mode, err)
99 }
100 _, body, err := c.ReadDraftImage(context.Background(), draft.ID)
101 if err != nil {
102 t.Fatalf("mode %s read: %v", mode, err)
103 }
104 if want == nil {
105 want = body
106 } else if !bytes.Equal(want, body) {
107 t.Fatal("permission profiles produced different originals")
108 }
109 }
110 }
111
111 lines GO