Skip to content

Commit 1f3d97f

Browse files
committed
add more acp tests
1 parent 96ef78d commit 1f3d97f

File tree

1 file changed

+186
-2
lines changed

1 file changed

+186
-2
lines changed

tests/acp/test_acp.lua

Lines changed: 186 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,25 @@ T["ACP Responses"]["handles partial JSON messages correctly"] = function()
303303
h.eq(result.third_message, '{"jsonrpc":"2.0","id":3,"result":{}}')
304304
end
305305

306+
T["ACP Responses"]["handles CRLF line endings in stdout"] = function()
307+
local result = child.lua([[
308+
local connection = ACP.new({
309+
adapter = test_adapter,
310+
opts = { schedule_wrap = function(fn) return fn end }
311+
})
312+
local processed = {}
313+
function connection:_process_json_message(line)
314+
table.insert(processed, line)
315+
end
316+
connection:_process_output('{"jsonrpc":"2.0","id":10,"result":{}}\r\n{"jsonrpc":"2.0","id":11,"result":{}}\n')
317+
return processed
318+
]])
319+
320+
h.eq(#result, 2)
321+
h.eq(result[1], '{"jsonrpc":"2.0","id":10,"result":{}}')
322+
h.eq(result[2], '{"jsonrpc":"2.0","id":11,"result":{}}')
323+
end
324+
306325
T["ACP Responses"]["processes real streaming prompt responses"] = function()
307326
local result = child.lua([[
308327
local connection = ACP.new({
@@ -341,7 +360,90 @@ T["ACP Responses"]["processes real streaming prompt responses"] = function()
341360
h.eq(result.message_chunks[1].content.text, "Expressive, elegant.")
342361
end
343362

344-
T["ACP Responses"]["handles fs/read_text_file and returns content"] = function()
363+
T["ACP Responses"]["dispatches session/request_permission to active prompt"] = function()
364+
local result = child.lua([[
365+
local connection = ACP.new({
366+
adapter = test_adapter,
367+
opts = { schedule_wrap = function(fn) return fn end }
368+
})
369+
connection.session_id = "sess-123"
370+
local captured = {}
371+
connection._active_prompt = {
372+
_handle_permission_request = function(_, id, params)
373+
captured.id = id
374+
captured.sid = params.sessionId
375+
end
376+
}
377+
local req = vim.json.encode({
378+
jsonrpc = "2.0",
379+
id = 99,
380+
method = "session/request_permission",
381+
params = { sessionId = "sess-123", options = {}, toolCall = { toolCallId = "tc1" } }
382+
})
383+
connection:_process_output(req .. "\r\n")
384+
return captured
385+
]])
386+
h.eq(result.id, 99)
387+
h.eq(result.sid, "sess-123")
388+
end
389+
390+
T["ACP Responses"]["_handle_done when stopReason present"] = function()
391+
local result = child.lua([[
392+
local connection = ACP.new({
393+
adapter = test_adapter,
394+
opts = { schedule_wrap = function(fn) return fn end }
395+
})
396+
local seen
397+
connection._active_prompt = {
398+
_handle_done = function(_, sr) seen = sr end
399+
}
400+
connection:_process_json_message('{"jsonrpc":"2.0","id":1,"result":{"stopReason":"end_turn"}}')
401+
return seen
402+
]])
403+
h.eq(result, "end_turn")
404+
end
405+
406+
T["ACP Connection"]["_handle_exit resets state, calls on_exit and prompts done(canceled)"] = function()
407+
local result = child.lua([[
408+
local connection = ACP.new({
409+
adapter = test_adapter,
410+
opts = { schedule_wrap = function(fn) return fn end }
411+
})
412+
connection.adapter_modified = {
413+
handlers = {
414+
on_exit = function(_, code) _G.__on_exit_code = code end
415+
}
416+
}
417+
local seen_done
418+
connection._active_prompt = {
419+
_handle_done = function(_, sr) seen_done = sr end
420+
}
421+
connection._initialized = true
422+
connection._authenticated = true
423+
connection.session_id = "sid"
424+
connection.pending_responses = { x = 1 }
425+
426+
connection:_handle_exit(123, 0)
427+
428+
return {
429+
init = connection._initialized,
430+
authed = connection._authenticated,
431+
sid = connection.session_id,
432+
pending = vim.tbl_count(connection.pending_responses),
433+
on_exit_code = _G.__on_exit_code,
434+
done_reason = seen_done,
435+
}
436+
]])
437+
438+
h.eq(result.init, false)
439+
h.eq(result.authed, false)
440+
h.eq(result.sid, nil)
441+
h.eq(result.pending, 0)
442+
h.eq(result.on_exit_code, 123)
443+
h.eq(result.done_reason, "canceled")
444+
end
445+
446+
T["ACP Responses"]["fs/read_text_file and returns content"] = function()
345447
local result = child.lua([[
346448
-- Create a temp file
347449
local tmp = vim.fn.tempname()
@@ -376,7 +478,60 @@ T["ACP Responses"]["handles fs/read_text_file and returns content"] = function()
376478
h.eq(true, result.result.content:find("line1") ~= nil)
377479
end
378480

379-
T["ACP Responses"]["handles fs/write_text_file and responds with null"] = function()
481+
T["ACP Responses"]["fs/read_text_file ENOENT returns empty content"] = function()
482+
local result = child.lua([[
483+
package.loaded["codecompanion.strategies.chat.acp.fs"] = {
484+
read_text_file = function(path) return false, "ENOENT: " .. path end
485+
}
486+
local connection = ACP.new({
487+
adapter = test_adapter,
488+
opts = { schedule_wrap = function(fn) return fn end }
489+
})
490+
connection.session_id = "s1"
491+
local sent = {}
492+
function connection:_write_to_process(data)
493+
table.insert(sent, vim.trim(data))
494+
return true
495+
end
496+
local req = vim.json.encode({
497+
jsonrpc = "2.0", id = 5, method = "fs/read_text_file",
498+
params = { sessionId = "s1", path = "/tmp/missing.txt" }
499+
})
500+
connection:_process_output(req .. "\n")
501+
return vim.json.decode(sent[#sent])
502+
]])
503+
h.eq(result.id, 5)
504+
h.eq(result.result.content, "")
505+
end
506+
507+
T["ACP Responses"]["fs/read_text_file rejects invalid sessionId"] = function()
508+
local result = child.lua([[
509+
package.loaded["codecompanion.strategies.chat.acp.fs"] = {
510+
read_text_file = function(path) return true, "should_not_be_called" end
511+
}
512+
local connection = ACP.new({
513+
adapter = test_adapter,
514+
opts = { schedule_wrap = function(fn) return fn end }
515+
})
516+
connection.session_id = "correct-session"
517+
local sent = {}
518+
function connection:_write_to_process(data)
519+
table.insert(sent, vim.trim(data))
520+
return true
521+
end
522+
local req = vim.json.encode({
523+
jsonrpc = "2.0", id = 6, method = "fs/read_text_file",
524+
params = { sessionId = "wrong-session", path = "/tmp/x" }
525+
})
526+
connection:_process_output(req .. "\n")
527+
return vim.json.decode(sent[#sent])
528+
]])
529+
530+
h.eq(result.id, 6)
531+
h.eq(result.error.code, -32602)
532+
end
533+
534+
T["ACP Responses"]["fs/write_text_file and responds with null"] = function()
380535
local result = child.lua([[
381536
-- Stub the fs module to capture writes
382537
local writes = {}
@@ -466,11 +621,40 @@ T["ACP Responses"]["fs/write_text_file rejects invalid sessionId"] = function()
466621

467622
h.eq(#result, 1)
468623
h.eq(result[1].id, 7)
624+
469625
-- JSON-RPC error response expected
470626
h.eq(type(result[1].error), "table")
471627
h.eq(result[1].error.code, -32602)
472628
end
473629

630+
T["ACP Responses"]["fs/write_text_file failure returns JSON-RPC error"] = function()
631+
local result = child.lua([[
632+
package.loaded["codecompanion.strategies.chat.acp.fs"] = {
633+
write_text_file = function(_) return nil, "EACCES" end
634+
}
635+
local connection = ACP.new({
636+
adapter = test_adapter,
637+
opts = { schedule_wrap = function(fn) return fn end }
638+
})
639+
connection.session_id = "s1"
640+
local sent = {}
641+
function connection:_write_to_process(data)
642+
table.insert(sent, vim.trim(data))
643+
return true
644+
end
645+
local req = vim.json.encode({
646+
jsonrpc = "2.0", id = 8, method = "fs/write_text_file",
647+
params = { sessionId = "s1", path = "/root/forbidden", content = "x" }
648+
})
649+
connection:_process_output(req .. "\n")
650+
return vim.json.decode(sent[#sent])
651+
]])
652+
653+
h.eq(result.id, 8)
654+
h.eq(type(result.error), "table")
655+
h.is_true(result.error.message:find("fs/write_text_file failed") ~= nil)
656+
end
657+
474658
T["ACP Responses"]["ignores notifications for other sessions"] = function()
475659
local result = child.lua([[
476660
local connection = ACP.new({

0 commit comments

Comments
 (0)