282 lines
9.5 KiB
Lua
282 lines
9.5 KiB
Lua
local runner = require("test-samurai-go-runner")
|
|
|
|
describe("test-samurai-go-runner", function()
|
|
it("detects Go test files by suffix", function()
|
|
local bufnr1 = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_set_name(bufnr1, "/tmp/go_suffix_test.go")
|
|
assert.is_true(runner.is_test_file(bufnr1))
|
|
|
|
local bufnr2 = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_set_name(bufnr2, "/tmp/go_main.go")
|
|
assert.is_false(runner.is_test_file(bufnr2))
|
|
end)
|
|
|
|
it("finds subtest when cursor is inside t.Run block", function()
|
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_set_name(bufnr, "/tmp/go_subtest_test.go")
|
|
local lines = {
|
|
"package main",
|
|
"import \"testing\"",
|
|
"",
|
|
"func TestFoo(t *testing.T) {",
|
|
" t.Run(\"first\", func(t *testing.T) {",
|
|
" -- inside first",
|
|
" })",
|
|
"",
|
|
" t.Run(\"second\", func(t *testing.T) {",
|
|
" -- inside second",
|
|
" })",
|
|
"}",
|
|
}
|
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
|
|
|
|
local orig_fs_find = vim.fs.find
|
|
vim.fs.find = function(_, _)
|
|
return { "/tmp/go.mod" }
|
|
end
|
|
|
|
local row_inside_first = 5
|
|
local spec, err = runner.find_nearest(bufnr, row_inside_first, 0)
|
|
|
|
vim.fs.find = orig_fs_find
|
|
|
|
assert.is_nil(err)
|
|
assert.is_not_nil(spec)
|
|
assert.equals("TestFoo/first", spec.test_path)
|
|
assert.equals("subtest", spec.scope)
|
|
assert.is_true(spec.file:match("go_subtest_test%.go$") ~= nil)
|
|
assert.is_true(spec.cwd:match("/tmp$") ~= nil)
|
|
end)
|
|
|
|
it("falls back to whole test function when between subtests", function()
|
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_set_name(bufnr, "/tmp/go_between_test.go")
|
|
local lines = {
|
|
"package main",
|
|
"import \"testing\"",
|
|
"",
|
|
"func TestFoo(t *testing.T) {",
|
|
" t.Run(\"first\", func(t *testing.T) {",
|
|
" -- inside first",
|
|
" })",
|
|
"",
|
|
" t.Run(\"second\", func(t *testing.T) {",
|
|
" -- inside second",
|
|
" })",
|
|
"}",
|
|
}
|
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
|
|
|
|
local orig_fs_find = vim.fs.find
|
|
vim.fs.find = function(_, _)
|
|
return { "/tmp/go.mod" }
|
|
end
|
|
|
|
local row_between = 7
|
|
local spec, err = runner.find_nearest(bufnr, row_between, 0)
|
|
|
|
vim.fs.find = orig_fs_find
|
|
|
|
assert.is_nil(err)
|
|
assert.is_not_nil(spec)
|
|
assert.equals("TestFoo", spec.test_path)
|
|
assert.equals("function", spec.scope)
|
|
assert.is_true(spec.file:match("go_between_test%.go$") ~= nil)
|
|
assert.is_true(spec.cwd:match("/tmp$") ~= nil)
|
|
end)
|
|
|
|
it("build_command uses current package and correct run pattern", function()
|
|
local spec_sub = {
|
|
file = "/tmp/project/pkg/foo_test.go",
|
|
cwd = "/tmp/project",
|
|
test_path = "TestFoo/first",
|
|
scope = "subtest",
|
|
}
|
|
|
|
local cmd_spec_sub = runner.build_command(spec_sub)
|
|
assert.are.same(
|
|
{ "go", "test", "-json", "-v", "./pkg", "-run", "^TestFoo$/^first$" },
|
|
cmd_spec_sub.cmd
|
|
)
|
|
|
|
local spec_func = {
|
|
file = "/tmp/project/foo_test.go",
|
|
cwd = "/tmp/project",
|
|
test_path = "TestFoo",
|
|
scope = "function",
|
|
}
|
|
|
|
local cmd_spec_func = runner.build_command(spec_func)
|
|
assert.are.same(
|
|
{ "go", "test", "-json", "-v", "./", "-run", "^TestFoo$" },
|
|
cmd_spec_func.cmd
|
|
)
|
|
end)
|
|
|
|
it("build_file_command uses exact test names from current file", function()
|
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_set_name(bufnr, "/tmp/project/get_test.go")
|
|
local lines = {
|
|
"package main",
|
|
"import \"testing\"",
|
|
"",
|
|
"func TestHandleGet(t *testing.T) {",
|
|
" t.Run(\"returns_200\", func(t *testing.T) {",
|
|
" -- inside test",
|
|
" })",
|
|
"}",
|
|
}
|
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
|
|
|
|
local orig_fs_find = vim.fs.find
|
|
vim.fs.find = function(_, _)
|
|
return { "/tmp/project/go.mod" }
|
|
end
|
|
|
|
local cmd_spec = runner.build_file_command(bufnr)
|
|
|
|
vim.fs.find = orig_fs_find
|
|
|
|
assert.are.same(
|
|
{ "go", "test", "-json", "-v", "./", "-run", "^(TestHandleGet)$" },
|
|
cmd_spec.cmd
|
|
)
|
|
assert.equals("/tmp/project", cmd_spec.cwd)
|
|
end)
|
|
|
|
it("parse_results reports subtests and display names", function()
|
|
local output = table.concat({
|
|
vim.json.encode({ Action = "run", Test = "TestFoo" }),
|
|
vim.json.encode({ Action = "pass", Test = "TestFoo/first" }),
|
|
vim.json.encode({ Action = "fail", Test = "TestFoo/second" }),
|
|
vim.json.encode({ Action = "skip", Test = "TestFoo/third" }),
|
|
}, "\n")
|
|
|
|
local results = runner.parse_results(output)
|
|
assert.are.same({ "TestFoo/first" }, results.passes)
|
|
assert.are.same({ "TestFoo/second" }, results.failures)
|
|
assert.are.same({ "TestFoo/third" }, results.skips)
|
|
assert.are.same({ "TestFoo/first" }, results.display.passes)
|
|
assert.are.same({ "TestFoo/second" }, results.display.failures)
|
|
assert.are.same({ "TestFoo/third" }, results.display.skips)
|
|
end)
|
|
|
|
it("orders parent tests before subtests within each status", function()
|
|
local output = table.concat({
|
|
vim.json.encode({ Action = "fail", Test = "TestHandleGet/returns_200" }),
|
|
vim.json.encode({ Action = "fail", Test = "TestHandleGet" }),
|
|
vim.json.encode({ Action = "pass", Test = "TestOther/alpha" }),
|
|
vim.json.encode({ Action = "pass", Test = "TestOther" }),
|
|
}, "\n")
|
|
|
|
local results = runner.parse_results(output)
|
|
assert.are.same({ "TestHandleGet", "TestHandleGet/returns_200" }, results.failures)
|
|
assert.are.same({ "TestOther", "TestOther/alpha" }, results.passes)
|
|
assert.are.same({ "TestHandleGet", "TestHandleGet/returns_200" }, results.display.failures)
|
|
assert.are.same({ "TestOther", "TestOther/alpha" }, results.display.passes)
|
|
end)
|
|
|
|
it("parse_test_output groups output per test", function()
|
|
local output = table.concat({
|
|
vim.json.encode({ Action = "output", Test = "TestFoo", Output = "line1\n" }),
|
|
vim.json.encode({ Action = "output", Test = "TestFoo", Output = "line2\n" }),
|
|
vim.json.encode({ Action = "output", Test = "TestFoo/first", Output = "sub1\n" }),
|
|
}, "\n")
|
|
|
|
local results = runner.parse_test_output(output)
|
|
assert.are.same({ "line1", "line2" }, results["TestFoo"])
|
|
assert.are.same({ "sub1" }, results["TestFoo/first"])
|
|
end)
|
|
|
|
it("build_failed_command narrows to failed tests", function()
|
|
local last_command = {
|
|
cmd = { "go", "test", "-json", "-v", "./", "-run", "^TestFoo($|/)" },
|
|
cwd = "/tmp/project",
|
|
}
|
|
local failures = { "TestFoo/first", "TestBar" }
|
|
|
|
local cmd_spec = runner.build_failed_command(last_command, failures, "file")
|
|
assert.are.same(
|
|
{ "go", "test", "-json", "-v", "./", "-run", "(^TestFoo$/^first$|^TestBar$)" },
|
|
cmd_spec.cmd
|
|
)
|
|
end)
|
|
|
|
it("collect_failed_locations finds subtest positions", function()
|
|
local temp_dir = vim.fn.tempname()
|
|
vim.fn.mkdir(temp_dir, "p")
|
|
local file = temp_dir .. "/sample_test.go"
|
|
local lines = {
|
|
"package main",
|
|
"import \"testing\"",
|
|
"",
|
|
"func TestFoo(t *testing.T) {",
|
|
" t.Run(\"first\", func(t *testing.T) {",
|
|
" -- inside test",
|
|
" })",
|
|
"}",
|
|
}
|
|
vim.fn.writefile(lines, file)
|
|
|
|
local items = runner.collect_failed_locations({ "TestFoo/first" }, { cwd = temp_dir }, "all")
|
|
assert.is_true(#items > 0)
|
|
assert.equals(file, items[1].filename)
|
|
assert.equals(5, items[1].lnum)
|
|
end)
|
|
|
|
it("filters parent tests for subtest scope", function()
|
|
-- This is a simpler test of the filtering logic
|
|
-- In real usage, parser_state.spec_scope = "subtest" is passed through core.lua
|
|
local passes = { "TestFoo", "TestFoo/first" }
|
|
local failures = {}
|
|
local skips = {}
|
|
|
|
-- Manually call what parse_results should do
|
|
local all_tests = {}
|
|
for _, n in ipairs(passes) do table.insert(all_tests, n) end
|
|
for _, n in ipairs(failures) do table.insert(all_tests, n) end
|
|
for _, n in ipairs(skips) do table.insert(all_tests, n) end
|
|
|
|
local has_children = {}
|
|
for _, n in ipairs(all_tests) do
|
|
local root = n:match("^([^/]+)/")
|
|
if root then has_children[root] = true end
|
|
end
|
|
|
|
-- TestFoo should have been marked as having children
|
|
assert.is_true(has_children["TestFoo"], "TestFoo should be marked as having children")
|
|
|
|
local filtered_passes = {}
|
|
for _, n in ipairs(passes) do
|
|
if not has_children[n] then
|
|
table.insert(filtered_passes, n)
|
|
end
|
|
end
|
|
|
|
assert.are.same({ "TestFoo/first" }, filtered_passes, "TestFoo should be filtered out")
|
|
end)
|
|
|
|
it("parse_results keeps parent test when spec_scope is function", function()
|
|
local output = table.concat({
|
|
vim.json.encode({ Action = "run", Test = "TestFoo" }),
|
|
vim.json.encode({ Action = "pass", Test = "TestFoo" }),
|
|
vim.json.encode({ Action = "pass", Test = "TestFoo/first" }),
|
|
}, "\n")
|
|
|
|
local parser_state = { spec_scope = "function" }
|
|
local results = runner.parse_results(output, parser_state)
|
|
assert.are.same({ "TestFoo", "TestFoo/first" }, results.passes)
|
|
end)
|
|
|
|
it("parse_results keeps parent test when setup fails even in subtest scope", function()
|
|
local output = table.concat({
|
|
vim.json.encode({ Action = "run", Test = "TestFoo" }),
|
|
vim.json.encode({ Action = "fail", Test = "TestFoo" }),
|
|
}, "\n")
|
|
|
|
local parser_state = { spec_scope = "subtest" }
|
|
local results = runner.parse_results(output, parser_state)
|
|
assert.are.same({ "TestFoo" }, results.failures)
|
|
end)
|
|
end)
|