Luigit
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 )