| 1 | package control |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "os" |
| 6 | "path/filepath" |
| 7 | "testing" |
| 8 | "time" |
| 9 | |
| 10 | "reasonix/internal/agent" |
| 11 | "reasonix/internal/event" |
| 12 | "reasonix/internal/provider" |
| 13 | "reasonix/internal/sessioninbox" |
| 14 | "reasonix/internal/tool" |
| 15 | ) |
| 16 | |
| 17 | func TestAttachmentEndpointsDeliverFrozenBytes(t *testing.T) { |
| 18 | for _, endpoint := range []string{"http", "edit", "run", "queue", "prepared"} { |
| 19 | t.Run(endpoint, func(t *testing.T) { |
| 20 | root := t.TempDir() |
| 21 | writeVisionTestConfig(t, root) |
| 22 | p := &reviewImageProvider{requests: make(chan provider.Request, 4)} |
| 23 | ag := agent.New(p, tool.NewRegistry(), agent.NewSession("sys"), agent.Options{}, event.Discard) |
| 24 | native := true |
| 25 | c := newOwnedTestController(t, Options{WorkspaceRoot: root, SessionPath: filepath.Join(root, "session.jsonl"), Runner: ag, Executor: ag, ModelRef: "custom/vision-pro", FrozenImageInput: &native}) |
| 26 | path, err := SaveImageDataURLInRoot(root, "data:image/png;base64,"+tinyPNG) |
| 27 | if err != nil { |
| 28 | t.Fatal(err) |
| 29 | } |
| 30 | input := "inspect @" + path |
| 31 | ctx, cancel := context.WithTimeout(t.Context(), 15*time.Second) |
| 32 | defer cancel() |
| 33 | switch endpoint { |
| 34 | case "http": |
| 35 | _, err = c.SubmitIdentifiedContext(ctx, SubmissionRequest{Input: input, HTTP: true}) |
| 36 | case "edit": |
| 37 | _, err = c.SubmitIdentifiedContext(ctx, SubmissionRequest{Input: input, Original: "old question"}) |
| 38 | case "run": |
| 39 | err = c.RunTurn(ctx, input) |
| 40 | case "queue": |
| 41 | var receipt sessioninbox.InboxReceipt |
| 42 | receipt, err = c.EnqueueInbox(InboxRequest{Submit: input}) |
| 43 | if err == nil { |
| 44 | err = os.Remove(filepath.Join(root, path)) |
| 45 | } |
| 46 | if err == nil { |
| 47 | err = c.RunInboxTurn(ctx, receipt.ItemID) |
| 48 | } |
| 49 | case "prepared": |
| 50 | var prepared *PreparedSubmission |
| 51 | prepared, err = c.PrepareSubmission(ctx, SubmissionRequest{Input: input}) |
| 52 | if err == nil { |
| 53 | err = os.Remove(filepath.Join(root, path)) |
| 54 | } |
| 55 | if err == nil { |
| 56 | _, err = c.SubmitPreparedWithSetup(ctx, prepared, nil) |
| 57 | } |
| 58 | } |
| 59 | if err != nil { |
| 60 | t.Fatal(err) |
| 61 | } |
| 62 | select { |
| 63 | case request := <-p.requests: |
| 64 | var images []string |
| 65 | for _, message := range request.Messages { |
| 66 | if message.Role == provider.RoleUser { |
| 67 | images = append(images, message.Images...) |
| 68 | } |
| 69 | } |
| 70 | if len(images) != 1 || images[0] != "data:image/png;base64,"+tinyPNG { |
| 71 | t.Fatalf("provider did not receive exact admitted bytes: %q", images) |
| 72 | } |
| 73 | case <-ctx.Done(): |
| 74 | t.Fatal(ctx.Err()) |
| 75 | } |
| 76 | }) |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | func TestAttachmentQueueEditPreservesStructuredAndAdditionalPath(t *testing.T) { |
| 81 | root := t.TempDir() |
| 82 | c := newOwnedTestController(t, Options{WorkspaceRoot: root}) |
| 83 | draft, err := c.StageImage(t.Context(), "first.png", "image/png", "data:image/png;base64,"+tinyPNG) |
| 84 | if err != nil { |
| 85 | t.Fatal(err) |
| 86 | } |
| 87 | env := sessioninbox.PromptEnvelope{AttachmentIdentities: []string{"first"}} |
| 88 | if err := c.freezeInboxEnvelopeReferences(t.Context(), &env, "inspect", nil, SubmissionAttachment{ClientAttachmentID: "first", DraftID: draft.ID}); err != nil { |
| 89 | t.Fatal(err) |
| 90 | } |
| 91 | c.ReleaseDraftImage(draft.ID) |
| 92 | path, err := SaveImageDataURLInRoot(root, "data:image/png;base64,"+tinyPNG) |
| 93 | if err != nil { |
| 94 | t.Fatal(err) |
| 95 | } |
| 96 | if err := c.freezeInboxEnvelopeReferences(t.Context(), &env, "inspect @"+path, nil); err != nil { |
| 97 | t.Fatal(err) |
| 98 | } |
| 99 | if len(env.ImageInputs) != 2 { |
| 100 | t.Fatalf("image count = %d", len(env.ImageInputs)) |
| 101 | } |
| 102 | } |
| 103 |