---@diagnostic disable: duplicate-set-field, need-check-nil, param-type-mismatch, assign-type-mismatch, redundant-parameter local tool = require 'bugabinga.pivi.tool' -- Shapes recorded from the installed Pi implementation: edit always reports a -- change, bash reports truncation and a full output path, the search tools -- report the limit they hit, and write reports nothing. local EDIT_DIFF = table.concat( { ' 1 local function greet(name)', '-2 return name', '+2 return "hello " .. name', '+3 -- added', ' 4 end', }, '\n' ) describe( 'pivi tool', function() describe( 'subject', function() it( 'names a path relative to the session directory', function() assert.equal( 'lua/deep/file.lua', tool.subject { tool = 'edit', args = { path = '/work/lua/deep/file.lua', }, cwd = '/work', } ) end ) it( 'falls back to the basename of a foreign path', function() assert.equal( 'file.lua', tool.subject { tool = 'read', args = { path = '/elsewhere/file.lua', }, cwd = '/work', } ) end ) it( 'names a command, a pattern, a tag or a title', function() assert.equal( 'ls -la', tool.subject { args = { command = 'ls -la', }, } ) assert.equal( 'needle', tool.subject { args = { pattern = 'needle', }, } ) assert.equal( 'quickfix', tool.subject { args = { tag = 'quickfix', }, } ) assert.equal( 'review', tool.subject { args = { title = 'review', }, } ) end ) it( 'collapses whitespace and bounds the length', function() local long = tool.subject { args = { command = string.rep( 'x', 200 ), }, } assert.is_true( #long <= tool.max_subject + 2 ) assert.equal( 'a b c', tool.subject { args = { command = 'a b\tc', }, } ) end ) it( 'names nothing when the arguments name nothing', function() assert.is_nil( tool.subject { tool = 'nvim_state', args = {}, } ) assert.is_nil( tool.subject( nil ) ) end ) end ) describe( 'outcome', function() it( 'counts what an edit added and removed', function() assert.equal( '+2 -1', tool.outcome { tool = 'edit', result = { details = { diff = EDIT_DIFF, }, }, } ) end ) it( 'reports a truncated result', function() assert.equal( 'truncated', tool.outcome { tool = 'bash', result = { details = { truncation = { truncated = true, }, fullOutputPath = '/tmp/out.log', }, }, } ) end ) it( 'reports the limit a search tool hit', function() assert.equal( 'match limit', tool.outcome { tool = 'grep', result = { details = { matchLimitReached = 100, }, }, } ) assert.equal( 'result limit', tool.outcome { tool = 'find', result = { details = { resultLimitReached = 100, }, }, } ) assert.equal( 'entry limit', tool.outcome { tool = 'ls', result = { details = { entryLimitReached = 500, }, }, } ) end ) it( 'reports a failure whatever the tool reported', function() assert.equal( 'failed', tool.outcome { tool = 'bash', failed = true, } ) assert.equal( 'truncated failed', tool.outcome { tool = 'bash', failed = true, result = { details = { truncation = { truncated = true, }, }, }, } ) end ) it( 'reports nothing for a tool that reported nothing', function() assert.is_nil( tool.outcome { tool = 'write', result = { content = {}, }, } ) assert.is_nil( tool.outcome { tool = 'read', result = { content = {}, details = nil, }, } ) assert.is_nil( tool.outcome( nil ) ) end ) it( 'ignores a truncation that did not happen', function() assert.is_nil( tool.outcome { tool = 'read', result = { details = { truncation = { truncated = false, }, }, }, } ) end ) end ) describe( 'the change an edit reported', function() it( 'keeps the change as the tool formatted it', function() local change = tool.diff { tool = 'edit', result = { details = { diff = EDIT_DIFF .. '\n\n', }, }, } assert.equal( EDIT_DIFF, change ) end ) it( 'reports no change when none was reported', function() assert.is_nil( tool.diff { tool = 'write', result = { content = {}, }, } ) assert.is_nil( tool.diff { tool = 'edit', result = { details = { diff = '', }, }, } ) end ) it( 'distinguishes added from removed lines', function() assert.equal( 'DiffAdd', tool.diff_highlight '+2 return "hello"' ) assert.equal( 'DiffDelete', tool.diff_highlight '-2 return name' ) assert.is_nil( tool.diff_highlight ' 1 local function greet(name)' ) assert.is_nil( tool.diff_highlight( nil ) ) end ) it( 'shows the change rather than a sentence about it', function() local shown = tool.body { tool = 'edit', result = { content = { { type = 'text', text = 'Successfully replaced 1 block(s) in a.lua.', }, }, details = { diff = EDIT_DIFF, }, }, } assert.same( vim.split( EDIT_DIFF, '\n', { plain = true, } ), shown ) end ) it( 'shows what a tool produced when it reported no change', function() assert.same( { 'alpha', 'beta', }, tool.body { tool = 'read', result = { content = { { type = 'text', text = 'alpha\nbeta\n', }, }, }, } ) end ) it( 'shows a failure when a tool produced nothing at all', function() assert.same( { 'failed', }, tool.body { tool = 'bash', failed = true, result = { content = {}, }, } ) assert.same( {}, tool.body { tool = 'write', result = { content = {}, }, } ) end ) end ) describe( 'icons', function() it( 'shows the kind of work rather than the name of the tool', function() local glyph = require 'std.icon' assert.equal( glyph.edit, tool.icon { tool = 'edit', } ) assert.equal( glyph.edit, tool.icon { tool = 'write', } ) assert.equal( glyph.file, tool.icon { tool = 'read', } ) assert.equal( glyph.shell, tool.icon { tool = 'bash', } ) assert.equal( glyph.search, tool.icon { tool = 'grep', } ) assert.equal( glyph.search, tool.icon { tool = 'find', } ) assert.equal( glyph.folder, tool.icon { tool = 'ls', } ) assert.equal( glyph.help, tool.icon { tool = 'nvim_help', } ) end ) it( 'falls back for a tool it has never heard of', function() local glyph = require 'std.icon' assert.equal( glyph.command, tool.icon { tool = 'something_new', } ) assert.equal( glyph.command, tool.icon( nil ) ) end ) end ) describe( 'truncation', function() it( 'knows a result was kept only in part', function() assert.is_true( tool.truncated { tool = 'bash', result = { details = { truncation = { truncated = true, }, }, }, } ) assert.is_false( tool.truncated { tool = 'bash', result = { details = { truncation = { truncated = false, }, }, }, } ) assert.is_false( tool.truncated { tool = 'write', result = { content = {}, }, } ) end ) it( 'knows where the full output still lives', function() assert.equal( '/tmp/pi-bash-abc.log', tool.full_output { tool = 'bash', result = { details = { truncation = { truncated = true, }, fullOutputPath = '/tmp/pi-bash-abc.log', }, }, } ) assert.is_nil( tool.full_output { tool = 'read', result = { details = { truncation = {}, }, }, } ) end ) end ) describe( 'lines', function() it( 'opens an entry with the tool and what it acted on', function() assert.equal( 'edit a.lua', tool.headline { tool = 'edit', args = { path = 'a.lua', }, } ) assert.equal( 'nvim_state', tool.headline { tool = 'nvim_state', args = {}, } ) end ) it( 'states subject and outcome together for a collapsed entry', function() local collapsed = tool.summary { tool = 'edit', args = { path = 'a.lua', }, result = { details = { diff = EDIT_DIFF, }, }, } assert.is_true( collapsed:find( 'edit', 1, true ) ~= nil ) assert.is_true( collapsed:find( 'a.lua', 1, true ) ~= nil ) assert.is_true( collapsed:find( '+2 -1', 1, true ) ~= nil ) end ) it( 'degrades to the subject alone when nothing was reported', function() assert.equal( 'write a.lua', tool.summary { tool = 'write', args = { path = 'a.lua', }, } ) end ) end ) end )