repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
neovim/tests/contract/pi_rpc_spec.lua
Raw---@diagnostic disable: need-check-nil, undefined-field
-- Guards the Pi assumptions pivi is built on.
-- The child runs with the user's own Pi configuration, because pivi's promise
-- is bring your own Pi: extensions, skills, prompts, and providers keep loading
-- under Pi's normal rules.
-- These run a real `pi --mode rpc` child, so they are opt in:
-- PIVI_CONTRACT=1 just test tests/contract
-- They make no model call, so they cost nothing beyond process startup.
local jsonl = require 'bugabinga.pivi.jsonl'
local rpc = require 'bugabinga.pivi.rpc'
local help = require 'bugabinga.pivi.help'
local runtime = require 'bugabinga.pivi.runtime'
local enabled = vim.env.PIVI_CONTRACT == '1' and vim.fn.executable 'pi' == 1
-- Starting Pi with the user's full configuration is the expensive part, so one
-- child per command line serves every question asked of it.
local children = {}
local child_for = function( label, command_line )
if children[label] then return children[label] end
local child = { frames = {}, }
child.client = rpc.start {
command = command_line,
cwd = vim.uv.cwd(),
on_event = function( frame ) table.insert( child.frames, frame ) end,
on_response = function( frame ) table.insert( child.frames, frame ) end,
}
children[label] = child
return child
end
local ask = function( commands, predicate, command_line )
local child = child_for(
command_line and 'help' or 'default',
command_line or { 'pi', '--mode', 'rpc', '--no-session', }
)
local first = #child.frames + 1
for _, command in ipairs( commands ) do
child.client.request( command )
end
local since = function()
return vim.list_slice( child.frames, first, #child.frames )
end
vim.wait( 120000, function() return predicate( since() ) end, 50 )
return since()
end
local shutdown = function()
for _, child in pairs( children ) do
child.client.stop()
-- A child that outlives the run keeps the whole suite from exiting.
vim.wait( 15000, function() return not child.client.is_running() end, 50 )
end
children = {}
end
local response_for = function( frames, name )
for _, frame in ipairs( frames ) do
if frame.type == 'response' and frame.command == name then return frame end
end
return nil
end
describe( 'pi rpc contract', function()
if not enabled then
it( 'is skipped without PIVI_CONTRACT=1 and pi on PATH', function()
assert.is_true( true )
end )
return
end
it( 'answers get_state with the fields pivi caches', function()
local frames = ask( { { type = 'get_state', }, }, function( seen )
return response_for( seen, 'get_state' ) ~= nil
end )
local answer = response_for( frames, 'get_state' )
assert.is_true( answer.success )
assert.is_boolean( answer.data.isStreaming )
assert.is_boolean( answer.data.isCompacting )
assert.is_string( answer.data.thinkingLevel )
end )
it( 'lists only commands a non interactive client can invoke', function()
local frames = ask( { { type = 'get_commands', }, }, function( seen )
return response_for( seen, 'get_commands' ) ~= nil
end )
local answer = response_for( frames, 'get_commands' )
assert.is_true( answer.success )
for _, entry in ipairs( answer.data.commands ) do
assert.is_string( entry.name )
assert.is_true( entry.source == 'extension' or entry.source == 'prompt' or entry.source == 'skill' )
assert.is_not.equal( 'settings', entry.name )
assert.is_not.equal( 'hotkeys', entry.name )
end
end )
it( 'reports a failed command instead of closing the channel', function()
local frames = ask( {
{ type = 'set_model', provider = 'nope', modelId = 'nope', },
{ type = 'get_available_thinking_levels', },
}, function( seen )
return response_for( seen, 'get_available_thinking_levels' ) ~= nil
end )
local failed = response_for( frames, 'set_model' )
assert.is_false( failed.success )
assert.is_string( failed.error )
assert.is_true( response_for( frames, 'get_available_thinking_levels' ).success )
end )
it( 'keeps the user configuration loaded', function()
local frames = ask( { { type = 'get_commands', }, }, function( seen )
return response_for( seen, 'get_commands' ) ~= nil
end )
local sources = {}
for _, entry in ipairs( response_for( frames, 'get_commands' ).data.commands ) do
sources[entry.source] = true
end
assert.is_true( next( sources ) ~= nil )
end )
it( 'accepts the flags a help run uses and loads the pivi skill', function()
local frames = ask(
{ { type = 'get_commands', }, },
function( seen ) return response_for( seen, 'get_commands' ) ~= nil end,
help.build_command {}
)
local answer = response_for( frames, 'get_commands' )
assert.is_true( answer.success )
local names = {}
for _, entry in ipairs( answer.data.commands ) do
names[entry.name] = true
end
assert.is_true( names['skill:pivi'] )
for _, frame in ipairs( frames ) do
assert.is_not.equal( 'extension_error', frame.type )
end
end )
it( 'loads the companion extension without error', function()
assert.is_string( runtime.extension_path() )
assert.is_string( runtime.skill_path() )
end )
after_each( function()
-- Every question must be answerable by an already running child.
for _, child in pairs( children ) do
assert.is_true( child.client.is_running() )
end
end )
it( 'frames every record as strict line feed delimited JSON', function()
local frames = ask( { { type = 'get_state', }, }, function( seen )
return response_for( seen, 'get_state' ) ~= nil
end )
assert.is_true( #frames > 0 )
for _, frame in ipairs( frames ) do
local encoded = vim.json.encode( frame )
assert.is_nil( encoded:find( '\n', 1, true ) )
assert.is_table( jsonl.parse( encoded ) )
end
end )
it( 'ends every child it started', function()
shutdown()
assert.same( {}, children )
end )
end )