feat(test): rest of test suite

This commit is contained in:
Barrett Ruth 2025-09-19 13:19:22 -04:00
parent 093782330a
commit 21407be376
3 changed files with 439 additions and 57 deletions

View file

@ -2,41 +2,164 @@ describe('cp.diff', function()
local diff = require('cp.diff') local diff = require('cp.diff')
describe('get_available_backends', function() describe('get_available_backends', function()
it('returns vim and git backends') it('returns vim and git backends', function()
local backends = diff.get_available_backends()
assert.same({'vim', 'git'}, backends)
end)
end) end)
describe('get_backend', function() describe('get_backend', function()
it('returns vim backend by name') it('returns vim backend by name', function()
it('returns git backend by name') local backend = diff.get_backend('vim')
it('returns nil for invalid name') assert.is_not_nil(backend)
assert.equals('vim', backend.name)
end)
it('returns git backend by name', function()
local backend = diff.get_backend('git')
assert.is_not_nil(backend)
assert.equals('git', backend.name)
end)
it('returns nil for invalid name', function()
local backend = diff.get_backend('invalid')
assert.is_nil(backend)
end)
end) end)
describe('is_git_available', function() describe('is_git_available', function()
it('returns true when git command succeeds') it('returns true when git command succeeds', function()
it('returns false when git command fails') local mock_system = stub(vim, 'system')
mock_system.returns({ code = 0 })
local result = diff.is_git_available()
assert.is_true(result)
mock_system:revert()
end)
it('returns false when git command fails', function()
local mock_system = stub(vim, 'system')
mock_system.returns({ code = 1 })
local result = diff.is_git_available()
assert.is_false(result)
mock_system:revert()
end)
end) end)
describe('get_best_backend', function() describe('get_best_backend', function()
it('returns preferred backend when available') it('returns preferred backend when available', function()
it('falls back to vim when git unavailable') local mock_is_available = stub(diff, 'is_git_available')
it('defaults to vim backend') mock_is_available.returns(true)
local backend = diff.get_best_backend('git')
assert.equals('git', backend.name)
mock_is_available:revert()
end)
it('falls back to vim when git unavailable', function()
local mock_is_available = stub(diff, 'is_git_available')
mock_is_available.returns(false)
local backend = diff.get_best_backend('git')
assert.equals('vim', backend.name)
mock_is_available:revert()
end)
it('defaults to vim backend', function()
local backend = diff.get_best_backend()
assert.equals('vim', backend.name)
end)
end) end)
describe('vim backend', function() describe('vim backend', function()
it('returns content as-is') it('returns content as-is', function()
it('returns nil highlights') local backend = diff.get_backend('vim')
local result = backend.render('expected', 'actual')
assert.same({'actual'}, result.content)
assert.is_nil(result.highlights)
end)
end) end)
describe('git backend', function() describe('git backend', function()
it('creates temp files for diff') it('creates temp files for diff', function()
it('returns raw diff output') local mock_system = stub(vim, 'system')
it('cleans up temp files') local mock_tempname = stub(vim.fn, 'tempname')
it('handles no differences') local mock_writefile = stub(vim.fn, 'writefile')
it('handles git command failure') local mock_delete = stub(vim.fn, 'delete')
mock_tempname.returns('/tmp/expected', '/tmp/actual')
mock_system.returns({ code = 1, stdout = 'diff output' })
local backend = diff.get_backend('git')
backend.render('expected text', 'actual text')
assert.stub(mock_writefile).was_called(2)
assert.stub(mock_delete).was_called(2)
mock_system:revert()
mock_tempname:revert()
mock_writefile:revert()
mock_delete:revert()
end)
it('returns raw diff output', function()
local mock_system = stub(vim, 'system')
local mock_tempname = stub(vim.fn, 'tempname')
local mock_writefile = stub(vim.fn, 'writefile')
local mock_delete = stub(vim.fn, 'delete')
mock_tempname.returns('/tmp/expected', '/tmp/actual')
mock_system.returns({ code = 1, stdout = 'git diff output' })
local backend = diff.get_backend('git')
local result = backend.render('expected', 'actual')
assert.equals('git diff output', result.raw_diff)
mock_system:revert()
mock_tempname:revert()
mock_writefile:revert()
mock_delete:revert()
end)
it('handles no differences', function()
local mock_system = stub(vim, 'system')
local mock_tempname = stub(vim.fn, 'tempname')
local mock_writefile = stub(vim.fn, 'writefile')
local mock_delete = stub(vim.fn, 'delete')
mock_tempname.returns('/tmp/expected', '/tmp/actual')
mock_system.returns({ code = 0 })
local backend = diff.get_backend('git')
local result = backend.render('same', 'same')
assert.same({'same'}, result.content)
assert.same({}, result.highlights)
mock_system:revert()
mock_tempname:revert()
mock_writefile:revert()
mock_delete:revert()
end)
end) end)
describe('render_diff', function() describe('render_diff', function()
it('uses best available backend') it('uses best available backend', function()
it('passes parameters to backend') local mock_get_best = spy.on(diff, 'get_best_backend')
local mock_backend = { render = function() return {} end }
mock_get_best.returns(mock_backend)
diff.render_diff('expected', 'actual', 'vim')
assert.spy(mock_get_best).was_called_with('vim')
mock_get_best:revert()
end)
end) end)
end) end)

View file

@ -1,39 +1,174 @@
describe('cp.highlight', function() describe('cp.highlight', function()
local highlight = require('cp.highlight') local highlight = require('cp.highlight')
describe('parse_diff_line', function()
it('parses added text markers {+text+}')
it('removes removed text markers [-text-]')
it('handles mixed add/remove markers')
it('calculates correct highlight positions')
it('handles text without markers')
it('handles empty text')
end)
describe('parse_git_diff', function() describe('parse_git_diff', function()
it('skips git diff headers') it('skips git diff headers', function()
it('processes added lines') local diff_output = [[
it('ignores removed lines') diff --git a/test b/test
it('handles unchanged lines') index 1234567..abcdefg 100644
it('sets correct line numbers') --- a/test
it('handles empty diff output') +++ b/test
@@ -1,3 +1,3 @@
hello
+world
-goodbye
]]
local result = highlight.parse_git_diff(diff_output)
assert.same({'hello', 'world'}, result.content)
end)
it('processes added lines', function()
local diff_output = '+hello w{+o+}rld'
local result = highlight.parse_git_diff(diff_output)
assert.same({'hello world'}, result.content)
assert.equals(1, #result.highlights)
assert.equals('CpDiffAdded', result.highlights[1].highlight_group)
end)
it('ignores removed lines', function()
local diff_output = 'hello\n-removed line\n+kept line'
local result = highlight.parse_git_diff(diff_output)
assert.same({'hello', 'kept line'}, result.content)
end)
it('handles unchanged lines', function()
local diff_output = 'unchanged line\n+added line'
local result = highlight.parse_git_diff(diff_output)
assert.same({'unchanged line', 'added line'}, result.content)
end)
it('sets correct line numbers', function()
local diff_output = '+first {+added+}\n+second {+text+}'
local result = highlight.parse_git_diff(diff_output)
assert.equals(0, result.highlights[1].line)
assert.equals(1, result.highlights[2].line)
end)
it('handles empty diff output', function()
local result = highlight.parse_git_diff('')
assert.same({}, result.content)
assert.same({}, result.highlights)
end)
end) end)
describe('apply_highlights', function() describe('apply_highlights', function()
it('clears existing highlights') it('clears existing highlights', function()
it('applies extmarks with correct positions') local mock_clear = spy.on(vim.api, 'nvim_buf_clear_namespace')
it('uses correct highlight groups') local bufnr = 1
it('handles empty highlights') local namespace = 100
highlight.apply_highlights(bufnr, {}, namespace)
assert.spy(mock_clear).was_called_with(bufnr, namespace, 0, -1)
mock_clear:revert()
end)
it('applies extmarks with correct positions', function()
local mock_extmark = spy.on(vim.api, 'nvim_buf_set_extmark')
local bufnr = 1
local namespace = 100
local highlights = {
{
line = 0,
col_start = 5,
col_end = 10,
highlight_group = 'CpDiffAdded'
}
}
highlight.apply_highlights(bufnr, highlights, namespace)
assert.spy(mock_extmark).was_called_with(
bufnr, namespace, 0, 5,
{
end_col = 10,
hl_group = 'CpDiffAdded',
priority = 100
}
)
mock_extmark:revert()
end)
it('uses correct highlight groups', function()
local mock_extmark = spy.on(vim.api, 'nvim_buf_set_extmark')
local highlights = {
{
line = 0,
col_start = 0,
col_end = 5,
highlight_group = 'CpDiffAdded'
}
}
highlight.apply_highlights(1, highlights, 100)
local call_args = mock_extmark.calls[1].vals
assert.equals('CpDiffAdded', call_args[4].hl_group)
mock_extmark:revert()
end)
it('handles empty highlights', function()
local mock_extmark = spy.on(vim.api, 'nvim_buf_set_extmark')
highlight.apply_highlights(1, {}, 100)
assert.spy(mock_extmark).was_not_called()
mock_extmark:revert()
end)
end) end)
describe('create_namespace', function() describe('create_namespace', function()
it('creates unique namespace') it('creates unique namespace', function()
local mock_create = stub(vim.api, 'nvim_create_namespace')
mock_create.returns(42)
local result = highlight.create_namespace()
assert.equals(42, result)
assert.stub(mock_create).was_called_with('cp_diff_highlights')
mock_create:revert()
end)
end) end)
describe('parse_and_apply_diff', function() describe('parse_and_apply_diff', function()
it('parses diff and applies to buffer') it('parses diff and applies to buffer', function()
it('sets buffer content') local mock_set_lines = spy.on(vim.api, 'nvim_buf_set_lines')
it('applies highlights') local mock_apply = spy.on(highlight, 'apply_highlights')
it('returns content lines') local bufnr = 1
local namespace = 100
local diff_output = '+hello {+world+}'
local result = highlight.parse_and_apply_diff(bufnr, diff_output, namespace)
assert.same({'hello world'}, result)
assert.spy(mock_set_lines).was_called_with(bufnr, 0, -1, false, {'hello world'})
assert.spy(mock_apply).was_called()
mock_set_lines:revert()
mock_apply:revert()
end)
it('sets buffer content', function()
local mock_set_lines = spy.on(vim.api, 'nvim_buf_set_lines')
highlight.parse_and_apply_diff(1, '+test line', 100)
assert.spy(mock_set_lines).was_called_with(1, 0, -1, false, {'test line'})
mock_set_lines:revert()
end)
it('applies highlights', function()
local mock_apply = spy.on(highlight, 'apply_highlights')
highlight.parse_and_apply_diff(1, '+hello {+world+}', 100)
assert.spy(mock_apply).was_called()
mock_apply:revert()
end)
it('returns content lines', function()
local result = highlight.parse_and_apply_diff(1, '+first\n+second', 100)
assert.same({'first', 'second'}, result)
end)
end) end)
end) end)

View file

@ -2,29 +2,153 @@ describe('cp.test_render', function()
local test_render = require('cp.test_render') local test_render = require('cp.test_render')
describe('get_status_info', function() describe('get_status_info', function()
it('returns AC for pass status') it('returns AC for pass status', function()
it('returns WA for fail status with normal exit codes') local test_case = { status = 'pass' }
it('returns TLE for timeout status') local result = test_render.get_status_info(test_case)
it('returns RTE for fail with signal codes (>= 128)') assert.equals('AC', result.text)
it('returns empty for pending status') assert.equals('CpTestAC', result.highlight_group)
end)
it('returns WA for fail status with normal exit codes', function()
local test_case = { status = 'fail', code = 1 }
local result = test_render.get_status_info(test_case)
assert.equals('WA', result.text)
assert.equals('CpTestError', result.highlight_group)
end)
it('returns TLE for timeout status', function()
local test_case = { status = 'timeout' }
local result = test_render.get_status_info(test_case)
assert.equals('TLE', result.text)
assert.equals('CpTestError', result.highlight_group)
end)
it('returns TLE for timed out fail status', function()
local test_case = { status = 'fail', timed_out = true }
local result = test_render.get_status_info(test_case)
assert.equals('TLE', result.text)
assert.equals('CpTestError', result.highlight_group)
end)
it('returns RTE for fail with signal codes (>= 128)', function()
local test_case = { status = 'fail', code = 139 }
local result = test_render.get_status_info(test_case)
assert.equals('RTE', result.text)
assert.equals('CpTestError', result.highlight_group)
end)
it('returns empty for pending status', function()
local test_case = { status = 'pending' }
local result = test_render.get_status_info(test_case)
assert.equals('', result.text)
assert.equals('CpTestPending', result.highlight_group)
end)
it('returns running indicator for running status', function()
local test_case = { status = 'running' }
local result = test_render.get_status_info(test_case)
assert.equals('...', result.text)
assert.equals('CpTestPending', result.highlight_group)
end)
end) end)
describe('render_test_list', function() describe('render_test_list', function()
it('renders test cases with CP terminology') it('renders test cases with CP terminology', function()
it('shows current test with > prefix') local test_state = {
it('displays input only for current test') test_cases = {
it('handles empty test cases') { status = 'pass', input = '5' },
it('preserves input line breaks') { status = 'fail', code = 1, input = '3' },
},
current_index = 1,
}
local result = test_render.render_test_list(test_state)
assert.equals('> 1. AC', result[1])
assert.equals(' 2. WA', result[3])
end)
it('shows current test with > prefix', function()
local test_state = {
test_cases = {
{ status = 'pass', input = '' },
{ status = 'pass', input = '' },
},
current_index = 2,
}
local result = test_render.render_test_list(test_state)
assert.equals(' 1. AC', result[1])
assert.equals('> 2. AC', result[2])
end)
it('displays input only for current test', function()
local test_state = {
test_cases = {
{ status = 'pass', input = '5 3' },
{ status = 'pass', input = '2 4' },
},
current_index = 1,
}
local result = test_render.render_test_list(test_state)
assert.equals('> 1. AC', result[1])
assert.equals(' 5 3', result[2])
assert.equals(' 2. AC', result[3])
end)
it('handles empty test cases', function()
local test_state = { test_cases = {}, current_index = 1 }
local result = test_render.render_test_list(test_state)
assert.equals(0, #result)
end)
it('preserves input line breaks', function()
local test_state = {
test_cases = {
{ status = 'pass', input = '5\n3\n1' },
},
current_index = 1,
}
local result = test_render.render_test_list(test_state)
assert.equals('> 1. AC', result[1])
assert.equals(' 5', result[2])
assert.equals(' 3', result[3])
assert.equals(' 1', result[4])
end)
end) end)
describe('render_status_bar', function() describe('render_status_bar', function()
it('formats time and exit code') it('formats time and exit code', function()
it('handles missing time') local test_case = { time_ms = 45.7, code = 0 }
it('handles missing exit code') local result = test_render.render_status_bar(test_case)
it('returns empty for nil test case') assert.equals('46ms │ Exit: 0', result)
end)
it('handles missing time', function()
local test_case = { code = 0 }
local result = test_render.render_status_bar(test_case)
assert.equals('Exit: 0', result)
end)
it('handles missing exit code', function()
local test_case = { time_ms = 123 }
local result = test_render.render_status_bar(test_case)
assert.equals('123ms', result)
end)
it('returns empty for nil test case', function()
local result = test_render.render_status_bar(nil)
assert.equals('', result)
end)
end) end)
describe('setup_highlights', function() describe('setup_highlights', function()
it('sets up all highlight groups') it('sets up all highlight groups', function()
local mock_set_hl = spy.on(vim.api, 'nvim_set_hl')
test_render.setup_highlights()
assert.spy(mock_set_hl).was_called(5)
assert.spy(mock_set_hl).was_called_with(0, 'CpTestAC', { fg = '#10b981', bold = true })
assert.spy(mock_set_hl).was_called_with(0, 'CpTestError', { fg = '#ef4444', bold = true })
mock_set_hl:revert()
end)
end) end)
end) end)