12 "github.com/stretchr/testify/require"
15 func TestTerminalImageLineSendsAnAbsolutePathAndCaption(t *testing.T) {
16 client, daemon := net.Pipe()
18 require.NoError(t, client.Close())
19 require.NoError(t, daemon.Close())
22 done := make(chan error, 1)
24 done <- terminalSendLines(t.Context(), client, strings.NewReader("/image 'image sample.png' Read the label.\n"))
27 var request SocketRequest
29 require.NoError(t, json.NewDecoder(daemon).Decode(&request))
30 require.NoError(t, <-done)
32 expected, err := filepath.Abs("image sample.png")
33 require.NoError(t, err)
34 require.Equal(t, SocketRequest{
37 Text: "Read the label.",
41 func TestTerminalImagePathsAndCaptionsHaveNoShellExpansion(t *testing.T) {
42 testCases := []struct {
50 name: "accepts paths without captions",
51 line: "/image sample.png",
56 name: "keeps spaces in double quoted paths",
57 line: `/image "a b.png" caption`,
63 name: "caption stays literal",
64 line: "/image sample.png $(not-a-command) ",
66 caption: "$(not-a-command) ",
70 name: "missing path refused",
75 name: "unclosed quote refused",
76 line: `/image "sample.png`,
80 name: "joined caption refused",
81 line: `/image "sample.png"caption`,
85 name: "other command stays text",
86 line: "/images sample.png",
90 for _, tc := range testCases {
91 t.Run(tc.name, func(t *testing.T) {
92 request, command := terminalImageRequest(tc.line)
93 require.Equal(t, tc.command, command)
94 require.Equal(t, tc.path, request.Path)
95 require.Equal(t, tc.caption, request.Text)
100 func TestTerminalImageIsStoredBeforeItReachesTheModel(t *testing.T) {
101 server := newModelFake(t)
102 socket := daemonStartTest(t, server)
103 terminal := terminalStartTestClient(t, socket)
104 terminal.WaitFor(t, "[connected to OneChat]")
106 path := filepath.Join(t.TempDir(), "sample.png")
107 data := imageTestPNG(t, 2, 2)
108 require.NoError(t, os.WriteFile(path, data, 0o600))
109 terminal.Type("/image " + path + " What is this?")
110 terminal.WaitFor(t, "[done]")
111 require.Equal(t, 1, server.Calls())
112 require.Contains(t, string(server.Call(0).raw), "input_image")
113 require.Contains(t, string(server.Call(0).raw), "What is this?")
114 require.NotContains(t, string(server.Call(0).raw), path)
116 files, err := os.ReadDir(filepath.Join(filepath.Dir(socket), imageDirName))
117 require.NoError(t, err)
118 require.Len(t, files, 1)
120 stored, err := os.ReadFile(filepath.Join(filepath.Dir(socket), imageDirName, files[0].Name()))
121 require.NoError(t, err)
122 require.Equal(t, data, stored)
123 require.NotContains(t, terminal.Output(), "base64")
126 func TestTerminalRefusesLargeAndUnsupportedImagesWithoutAModelCall(t *testing.T) {
127 server := newModelFake(t)
128 test := newTestTurnLoop(t, server)
129 path := filepath.Join(t.TempDir(), "sample.png")
130 require.NoError(t, os.WriteFile(path, bytes.Repeat([]byte{0}, imageMaxBytes+1), 0o600))
132 request := SocketRequest{
136 require.ErrorContains(t, daemonRunRequest(test.loop, nil, request, func(string) {
138 require.NoError(t, os.WriteFile(path, []byte("not an image"), 0o600))
139 require.ErrorContains(t, daemonRunRequest(test.loop, nil, request, func(string) {
140 }), "JPEG, PNG, or WebP")
141 require.Equal(t, 0, test.memory.Len())
142 require.Equal(t, 0, server.Calls())
144 _, err := os.Stat(test.agent.Images().dir)
145 require.ErrorIs(t, err, os.ErrNotExist)
148 func TestImageCommandErrorDoesNotEndTheTerminalClient(t *testing.T) {
149 socket := daemonStartTest(t, newModelFake(t))
150 terminal := terminalStartTestClient(t, socket)
151 terminal.WaitFor(t, "[connected to OneChat]")
152 terminal.Type("/image")
153 terminal.WaitFor(t, "use /image PATH [caption]")
154 terminal.Type("Continue.")
155 terminal.WaitFor(t, "[done]")
156 require.NoError(t, terminal.Close())