repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
neovim/tests/bugabinga/pivi/rpc_spec.lua
Raw---@diagnostic disable: duplicate-set-field, need-check-nil, param-type-mismatch, assign-type-mismatch
local rpc = require 'bugabinga.pivi.rpc'
local fake = dofile 'tests/helpers/pivi_fake.lua'
local start = function( channel, options )
options = options or {}
options.command = { 'pi', '--mode', 'rpc', }
options.spawn = channel.spawn
return rpc.start( options )
end
describe( 'pivi rpc', function()
it( 'spawns with the given command and working directory', function()
local channel = fake.new()
start( channel, { cwd = '/tmp', } )
assert.same( { 'pi', '--mode', 'rpc', }, channel.command )
assert.equal( '/tmp', channel.options.cwd )
end )
it( 'writes commands as single line JSONL records', function()
local channel = fake.new()
local client = start( channel )
client.notify { type = 'abort', }
assert.equal( '{"type":"abort"}\n', channel.writes[1] )
end )
it( 'correlates a response with its request id', function()
local channel = fake.new()
local client = start( channel )
local answer
local id = client.request( { type = 'get_state', }, function( response ) answer = response end )
channel.frame { id = 'other', type = 'response', command = 'get_state', success = true, }
channel.frame { id = id, type = 'response', command = 'get_state', success = true, data = { messageCount = 3, }, }
assert.is_true( channel.settle( function() return answer ~= nil end ) )
assert.equal( id, answer.id )
assert.equal( 3, answer.data.messageCount )
end )
it( 'never delivers a frame from a fast event context', function()
local channel = fake.new()
local contexts = {}
start( channel, {
on_event = function() table.insert( contexts, vim.in_fast_event() ) end,
} )
channel.frame { type = 'agent_start', }
assert.is_true( channel.settle( function() return #contexts == 1 end ) )
assert.is_false( contexts[1] )
end )
it( 'delivers interleaved events and responses in arrival order', function()
local channel = fake.new()
local seen = {}
local client = start( channel, {
on_event = function( event ) table.insert( seen, event.type ) end,
} )
client.request( { type = 'prompt', message = 'hi', }, function() table.insert( seen, 'response' ) end )
local id = channel.commands[1].id
channel.emit(
vim.json.encode { id = id, type = 'response', command = 'prompt', success = true, } .. '\n' ..
vim.json.encode { type = 'agent_start', } .. '\n' ..
vim.json.encode { type = 'agent_settled', } .. '\n'
)
assert.is_true( channel.settle( function() return #seen == 3 end ) )
assert.same( { 'response', 'agent_start', 'agent_settled', }, seen )
end )
it( 'reassembles frames split at arbitrary byte boundaries', function()
local channel = fake.new()
local events = {}
start( channel, { on_event = function( event ) table.insert( events, event ) end, } )
local payload = vim.json.encode { type = 'message_update', assistantMessageEvent = { type = 'text_delta', delta = 'hello', }, } .. '\n'
for index = 1, #payload do
channel.emit( payload:sub( index, index ) )
end
assert.is_true( channel.settle( function() return #events == 1 end ) )
assert.equal( 'hello', events[1].assistantMessageEvent.delta )
end )
it( 'reports an undecodable record instead of crashing', function()
local channel = fake.new()
local failures = {}
start( channel, {
on_decode_error = function( record ) table.insert( failures, record ) end,
} )
channel.emit 'this is not json\n'
assert.is_true( channel.settle( function() return #failures == 1 end ) )
assert.equal( 'this is not json', failures[1] )
end )
it( 'keeps standard error separate from protocol output', function()
local channel = fake.new()
local events, noise = {}, {}
start( channel, {
on_event = function( event ) table.insert( events, event ) end,
on_stderr = function( data ) table.insert( noise, data ) end,
} )
channel.stderr_emit 'warning: something\n'
assert.is_true( channel.settle( function() return #noise == 1 end ) )
assert.equal( 0, #events )
end )
it( 'flushes a trailing record and fails pending requests when the child exits', function()
local channel = fake.new()
local events, answers, exits = {}, {}, {}
local client = start( channel, {
on_event = function( event ) table.insert( events, event ) end,
on_exit = function( code ) table.insert( exits, code ) end,
} )
client.request( { type = 'get_state', }, function( response ) table.insert( answers, response ) end )
channel.emit( vim.json.encode { type = 'agent_settled', } )
channel.close( 2 )
assert.is_true( channel.settle( function() return #exits == 1 end ) )
assert.equal( 2, exits[1] )
assert.equal( 1, #events )
assert.equal( 1, #answers )
assert.is_false( answers[1].success )
assert.is_false( client.is_running() )
end )
it( 'rejects a request sent after the child exited', function()
local channel = fake.new()
local client = start( channel )
channel.close( 0 )
assert.is_true( channel.settle( function() return not client.is_running() end ) )
local answer
client.request( { type = 'prompt', message = 'late', }, function( response ) answer = response end )
assert.is_false( answer.success )
assert.is_string( answer.error )
end )
it( 'reports an unspawnable executable instead of raising', function()
local exited = false
local client = rpc.start {
command = { 'definitely-not-a-real-program', },
spawn = function() error 'ENOENT: no such file or directory' end,
-- A process that never started reports no exit code.
on_exit = function( code )
exited = true
assert.is_nil( code )
end,
}
assert.is_false( client.is_running() )
assert.is_true( client.failure():find( 'ENOENT', 1, true ) ~= nil )
local answer
client.request( { type = 'prompt', message = 'hi', }, function( response ) answer = response end )
assert.is_false( answer.success )
-- Stopping a process that never started must be harmless.
client.stop()
assert.is_true( vim.wait( 2000, function() return exited end, 5 ) )
end )
it( 'reports a handler that throws instead of swallowing it', function()
local channel = fake.new()
local reported = {}
start( channel, {
on_event = function() error 'handler blew up' end,
on_failure = function( failure ) table.insert( reported, failure ) end,
} )
channel.frame { type = 'agent_start', }
assert.is_true( channel.settle( function() return #reported == 1 end ) )
assert.is_true( reported[1]:find( 'handler blew up', 1, true ) ~= nil )
end )
it( 'closes stdin first so the child can exit on its own', function()
local channel = fake.new()
local client = start( channel )
client.stop()
assert.is_true( channel.stdin_closed )
-- Killing straight away would orphan the runtime behind a launcher shim.
assert.is_false( channel.killed )
channel.close( 0 )
assert.is_true( channel.settle( function() return not client.is_running() end ) )
vim.wait( 2500, function() return channel.killed end, 20 )
assert.is_false( channel.killed )
end )
it( 'terminates a child that ignores a closed input', function()
local channel = fake.new()
local client = start( channel )
client.stop()
assert.is_true( vim.wait( 5000, function() return channel.killed end, 20 ) )
end )
end )