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')
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)
describe('get_backend', function()
it('returns vim backend by name')
it('returns git backend by name')
it('returns nil for invalid name')
it('returns vim backend by name', function()
local backend = diff.get_backend('vim')
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)
describe('is_git_available', function()
it('returns true when git command succeeds')
it('returns false when git command fails')
it('returns true when git command succeeds', function()
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)
describe('get_best_backend', function()
it('returns preferred backend when available')
it('falls back to vim when git unavailable')
it('defaults to vim backend')
it('returns preferred backend when available', function()
local mock_is_available = stub(diff, 'is_git_available')
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)
describe('vim backend', function()
it('returns content as-is')
it('returns nil highlights')
it('returns content as-is', function()
local backend = diff.get_backend('vim')
local result = backend.render('expected', 'actual')
assert.same({'actual'}, result.content)
assert.is_nil(result.highlights)
end)
end)
describe('git backend', function()
it('creates temp files for diff')
it('returns raw diff output')
it('cleans up temp files')
it('handles no differences')
it('handles git command failure')
it('creates temp files for diff', 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 = '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)
describe('render_diff', function()
it('uses best available backend')
it('passes parameters to backend')
it('uses best available backend', function()
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)

View file

@ -1,39 +1,174 @@
describe('cp.highlight', function()
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()
it('skips git diff headers')
it('processes added lines')
it('ignores removed lines')
it('handles unchanged lines')
it('sets correct line numbers')
it('handles empty diff output')
it('skips git diff headers', function()
local diff_output = [[
diff --git a/test b/test
index 1234567..abcdefg 100644
--- a/test
+++ 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)
describe('apply_highlights', function()
it('clears existing highlights')
it('applies extmarks with correct positions')
it('uses correct highlight groups')
it('handles empty highlights')
it('clears existing highlights', function()
local mock_clear = spy.on(vim.api, 'nvim_buf_clear_namespace')
local bufnr = 1
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)
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)
describe('parse_and_apply_diff', function()
it('parses diff and applies to buffer')
it('sets buffer content')
it('applies highlights')
it('returns content lines')
it('parses diff and applies to buffer', function()
local mock_set_lines = spy.on(vim.api, 'nvim_buf_set_lines')
local mock_apply = spy.on(highlight, 'apply_highlights')
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)

View file

@ -2,29 +2,153 @@ describe('cp.test_render', function()
local test_render = require('cp.test_render')
describe('get_status_info', function()
it('returns AC for pass status')
it('returns WA for fail status with normal exit codes')
it('returns TLE for timeout status')
it('returns RTE for fail with signal codes (>= 128)')
it('returns empty for pending status')
it('returns AC for pass status', function()
local test_case = { status = 'pass' }
local result = test_render.get_status_info(test_case)
assert.equals('AC', result.text)
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)
describe('render_test_list', function()
it('renders test cases with CP terminology')
it('shows current test with > prefix')
it('displays input only for current test')
it('handles empty test cases')
it('preserves input line breaks')
it('renders test cases with CP terminology', function()
local test_state = {
test_cases = {
{ status = 'pass', input = '5' },
{ 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)
describe('render_status_bar', function()
it('formats time and exit code')
it('handles missing time')
it('handles missing exit code')
it('returns empty for nil test case')
it('formats time and exit code', function()
local test_case = { time_ms = 45.7, code = 0 }
local result = test_render.render_status_bar(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)
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)