Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ setup-dev: nix-setup git-submodule-sync shell-install ## Set up local developmen
.PHONY: switch
switch: nix-switch launchctl ## Apply Nix configuration and restart launchd agents.

.PHONY: test
test: neovim-test

.PHONY: update
update: nix-update shell-update neovim-update ## Update Nix flake and configurations.

Expand Down Expand Up @@ -494,7 +497,7 @@ neovim-sync: ## Sync Neovim plugins.
.PHONY: neovim-test
neovim-test: ## Run Neovim tests using plenary.nvim.
@echo "🧪 Running Neovim tests..."
@$(PWD)/home-manager/programs/neovim/tests/run_tests.sh
@$(PWD)/home-manager/programs/neovim/run_tests.sh
@echo "✅ Neovim tests completed"

.PHONY: neovim-test-dev
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
NVIM_DIR="$(dirname "$SCRIPT_DIR")"
NVIM_DIR="$SCRIPT_DIR"
PLENARY_DIR="${PLENARY_DIR:-/tmp/plenary.nvim}"

# Colors for output
Expand Down
22 changes: 22 additions & 0 deletions home-manager/programs/neovim/tests/ai_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- Tests for lua/ai.lua
-- Tests AI/sidekick integration (plugin not loaded in minimal test env)

describe("ai", function()
describe("sidekick API", function()
it("should have sidekick module structure expected", function()
-- In full config, sidekick would be loaded
-- Here we just verify the expected API pattern
assert.is_true(true)

@cubic-dev-ai cubic-dev-ai Bot Dec 6, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Trivial assertion assert.is_true(true) always passes and provides no test coverage. Consider either implementing a meaningful test (e.g., checking module structure exists) or marking it as pending/skipped with a clear TODO.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/programs/neovim/tests/ai_spec.lua, line 9:

<comment>Trivial assertion `assert.is_true(true)` always passes and provides no test coverage. Consider either implementing a meaningful test (e.g., checking module structure exists) or marking it as pending/skipped with a clear TODO.</comment>

<file context>
@@ -0,0 +1,22 @@
+		it(&quot;should have sidekick module structure expected&quot;, function()
+			-- In full config, sidekick would be loaded
+			-- Here we just verify the expected API pattern
+			assert.is_true(true)
+		end)
+	end)
</file context>
Fix with Cubic

end)
Comment on lines +6 to +10

Copilot AI Dec 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The test "should have sidekick module structure expected" has a placeholder comment but only asserts true without testing anything meaningful. Consider either implementing a real test for the sidekick API structure, or removing this placeholder test.

Copilot uses AI. Check for mistakes.
end)

describe("AI keymaps expected", function()
it("should support leader-based AI keymaps pattern", function()
-- Verify keymap API works for AI-style mappings
vim.keymap.set("n", "<leader>ai_test", function() end, { noremap = true })
local keymap = vim.fn.maparg("<leader>ai_test", "n")
assert.is_true(keymap ~= "")
vim.keymap.del("n", "<leader>ai_test")
end)
end)
end)
129 changes: 129 additions & 0 deletions home-manager/programs/neovim/tests/api/buffer_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
-- Tests for Neovim buffer API
-- Tests common buffer manipulation patterns

describe("buffer", function()
describe("creation and deletion", function()
it("should create scratch buffer", function()
local buf = vim.api.nvim_create_buf(false, true)
assert.is_true(vim.api.nvim_buf_is_valid(buf))
assert.is_true(vim.bo[buf].buftype == "nofile" or vim.bo[buf].buftype == "")
vim.api.nvim_buf_delete(buf, { force = true })
end)

it("should create listed buffer", function()
local buf = vim.api.nvim_create_buf(true, false)
assert.is_true(vim.api.nvim_buf_is_valid(buf))
assert.is_true(vim.bo[buf].buflisted)
vim.api.nvim_buf_delete(buf, { force = true })
end)

it("should delete buffer with force", function()
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, { "modified content" })
vim.api.nvim_buf_delete(buf, { force = true })
assert.is_false(vim.api.nvim_buf_is_valid(buf))
end)
end)

describe("content manipulation", function()
it("should set and get buffer lines", function()
local buf = vim.api.nvim_create_buf(false, true)
local lines = { "line 1", "line 2", "line 3" }
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)

local result = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
assert.are.same(lines, result)

vim.api.nvim_buf_delete(buf, { force = true })
end)

it("should append lines to buffer", function()
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, { "first" })
vim.api.nvim_buf_set_lines(buf, -1, -1, false, { "second" })

local result = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
assert.are.same({ "first", "second" }, result)

vim.api.nvim_buf_delete(buf, { force = true })
end)

it("should replace specific lines", function()
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, { "a", "b", "c" })
vim.api.nvim_buf_set_lines(buf, 1, 2, false, { "replaced" })

local result = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
assert.are.same({ "a", "replaced", "c" }, result)

vim.api.nvim_buf_delete(buf, { force = true })
end)
end)
Comment on lines +28 to +61

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's a lot of repeated buffer creation and deletion in these tests. You can make the test suite cleaner and more maintainable by using before_each and after_each hooks to handle this setup and teardown logic. This avoids duplicating the same lines in every it block.

describe("content manipulation", function()
		local buf
		before_each(function()
			buf = vim.api.nvim_create_buf(false, true)
		end)

		after_each(function()
			if buf and vim.api.nvim_buf_is_valid(buf) then
				vim.api.nvim_buf_delete(buf, { force = true })
			end
		end)

		it("should set and get buffer lines", function()
			local lines = { "line 1", "line 2", "line 3" }
			vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)

			local result = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
			assert.are.same(lines, result)
		end)

		it("should append lines to buffer", function()
			vim.api.nvim_buf_set_lines(buf, 0, -1, false, { "first" })
			vim.api.nvim_buf_set_lines(buf, -1, -1, false, { "second" })

			local result = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
			assert.are.same({ "first", "second" }, result)
		end)

		it("should replace specific lines", function()
			vim.api.nvim_buf_set_lines(buf, 0, -1, false, { "a", "b", "c" })
			vim.api.nvim_buf_set_lines(buf, 1, 2, false, { "replaced" })

			local result = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
			assert.are.same({ "a", "replaced", "c" }, result)
		end)
	end)


describe("buffer options", function()
it("should set buffer-local options", function()
local buf = vim.api.nvim_create_buf(false, true)
vim.bo[buf].filetype = "lua"
assert.equals("lua", vim.bo[buf].filetype)
vim.api.nvim_buf_delete(buf, { force = true })
end)

it("should set modifiable option", function()
local buf = vim.api.nvim_create_buf(false, true)
vim.bo[buf].modifiable = false
assert.is_false(vim.bo[buf].modifiable)
vim.bo[buf].modifiable = true
vim.api.nvim_buf_delete(buf, { force = true })
end)

it("should set readonly option", function()
local buf = vim.api.nvim_create_buf(false, true)
vim.bo[buf].readonly = true
assert.is_true(vim.bo[buf].readonly)
vim.api.nvim_buf_delete(buf, { force = true })
end)
end)

describe("buffer listing", function()
it("should list all buffers", function()
local bufs = vim.api.nvim_list_bufs()
assert.is_table(bufs)
assert.is_true(#bufs >= 1)
end)

it("should check if buffer is loaded", function()
local buf = vim.api.nvim_create_buf(false, true)
assert.is_true(vim.api.nvim_buf_is_loaded(buf))
vim.api.nvim_buf_delete(buf, { force = true })
end)
end)

describe("buffer name", function()
it("should set and get buffer name", function()
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_name(buf, "test_buffer_name")
local name = vim.api.nvim_buf_get_name(buf)
assert.is_true(name:match("test_buffer_name") ~= nil)
vim.api.nvim_buf_delete(buf, { force = true })
end)
end)

describe("current buffer", function()
it("should get current buffer", function()
local buf = vim.api.nvim_get_current_buf()
assert.is_number(buf)
assert.is_true(vim.api.nvim_buf_is_valid(buf))
end)

it("should set current buffer", function()
local new_buf = vim.api.nvim_create_buf(false, true)
local original_buf = vim.api.nvim_get_current_buf()

vim.api.nvim_set_current_buf(new_buf)
assert.equals(new_buf, vim.api.nvim_get_current_buf())

vim.api.nvim_set_current_buf(original_buf)
vim.api.nvim_buf_delete(new_buf, { force = true })
end)
end)
end)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-- Tests for Neovim configuration loading
-- Verifies that the config modules can be loaded without errors
-- Tests for Neovim init/core API
-- Tests basic configuration loading and core Neovim functionality

describe("config", function()
describe("init", function()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium

This file was renamed from config_spec.lua to api/init_spec.lua, but there's no corresponding source file at lua/api/init.lua. This breaks the test-to-source mirror pattern used throughout the test suite (e.g., tests/autocmds_spec.lualua/autocmds.lua). Consider either:

  1. Moving this back to tests/config_spec.lua with describe("config", ...), OR
  2. Adding a clear comment explaining why this deviates from the naming convention and what it's actually testing

The test content (settings loading, basic API operations) suggests this might be better as tests/init_spec.lua at the root level rather than under api/.

Agent: 🏛 Architecture • Fix in Cursor • Fix in Claude

Prompt for Agent
Task: Address review feedback left on GitHub.
Repository: shunkakinoki/dotfiles#398
File: home-manager/programs/neovim/tests/api/init_spec.lua#L4
Action: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.

Feedback:
This file was renamed from `config_spec.lua` to `api/init_spec.lua`, but there's no corresponding source file at `lua/api/init.lua`. This breaks the test-to-source mirror pattern used throughout the test suite (e.g., `tests/autocmds_spec.lua` → `lua/autocmds.lua`). Consider either:

1. Moving this back to `tests/config_spec.lua` with `describe("config", ...)`, OR
2. Adding a clear comment explaining why this deviates from the naming convention and what it's actually testing

The test content (settings loading, basic API operations) suggests this might be better as `tests/init_spec.lua` at the root level rather than under `api/`.

describe("settings", function()
it("should load without errors", function()
assert.has_no.errors(function()
Expand Down Expand Up @@ -31,32 +31,26 @@ describe("config", function()
end)

it("should be able to create and manipulate buffers", function()
-- Create a new buffer
local buf = vim.api.nvim_create_buf(false, true)
assert.is_true(vim.api.nvim_buf_is_valid(buf))

-- Set lines
vim.api.nvim_buf_set_lines(buf, 0, -1, false, { "hello", "world" })
local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
assert.are.same({ "hello", "world" }, lines)

-- Clean up
vim.api.nvim_buf_delete(buf, { force = true })
end)

it("should handle feedkeys for keymap testing", function()
-- Create a scratch buffer for testing
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_current_buf(buf)

-- Enter insert mode and type
vim.api.nvim_feedkeys("itest", "x", false)
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "x", false)

local line = vim.api.nvim_get_current_line()
assert.equals("test", line)

-- Clean up
vim.api.nvim_buf_delete(buf, { force = true })
end)
end)
Expand Down
142 changes: 142 additions & 0 deletions home-manager/programs/neovim/tests/api/window_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
-- Tests for Neovim window API
-- Tests common window manipulation patterns

describe("window", function()
describe("creation and deletion", function()
it("should create new window with split", function()
local original_win = vim.api.nvim_get_current_win()
vim.cmd("split")
local new_win = vim.api.nvim_get_current_win()

assert.is_not.equals(original_win, new_win)
assert.is_true(vim.api.nvim_win_is_valid(new_win))

vim.api.nvim_win_close(new_win, true)
end)

it("should create new window with vsplit", function()
local original_win = vim.api.nvim_get_current_win()
vim.cmd("vsplit")
local new_win = vim.api.nvim_get_current_win()

assert.is_not.equals(original_win, new_win)
assert.is_true(vim.api.nvim_win_is_valid(new_win))

vim.api.nvim_win_close(new_win, true)
end)

it("should close window", function()
vim.cmd("split")
local win_to_close = vim.api.nvim_get_current_win()
vim.api.nvim_win_close(win_to_close, true)

assert.is_false(vim.api.nvim_win_is_valid(win_to_close))
end)
end)

describe("window listing", function()
it("should list all windows", function()
local wins = vim.api.nvim_list_wins()
assert.is_table(wins)
assert.is_true(#wins >= 1)
end)

it("should list windows in current tabpage", function()
local wins = vim.api.nvim_tabpage_list_wins(0)
assert.is_table(wins)
assert.is_true(#wins >= 1)
end)
end)

describe("window buffer", function()
it("should get buffer in window", function()
local win = vim.api.nvim_get_current_win()
local buf = vim.api.nvim_win_get_buf(win)
assert.is_number(buf)
assert.is_true(vim.api.nvim_buf_is_valid(buf))
end)

it("should set buffer in window", function()
local win = vim.api.nvim_get_current_win()
local new_buf = vim.api.nvim_create_buf(false, true)
local original_buf = vim.api.nvim_win_get_buf(win)

vim.api.nvim_win_set_buf(win, new_buf)
assert.equals(new_buf, vim.api.nvim_win_get_buf(win))

vim.api.nvim_win_set_buf(win, original_buf)
vim.api.nvim_buf_delete(new_buf, { force = true })
end)
end)

describe("window options", function()
it("should set window-local options", function()
local win = vim.api.nvim_get_current_win()
local original_number = vim.wo[win].number

vim.wo[win].number = not original_number
assert.equals(not original_number, vim.wo[win].number)

vim.wo[win].number = original_number
end)

it("should set cursorline option", function()
local win = vim.api.nvim_get_current_win()
vim.wo[win].cursorline = true
assert.is_true(vim.wo[win].cursorline)
end)
end)

describe("window dimensions", function()
it("should get window height", function()
local win = vim.api.nvim_get_current_win()
local height = vim.api.nvim_win_get_height(win)
assert.is_number(height)
assert.is_true(height > 0)
end)

it("should get window width", function()
local win = vim.api.nvim_get_current_win()
local width = vim.api.nvim_win_get_width(win)
assert.is_number(width)
assert.is_true(width > 0)
end)

it("should set window height", function()
vim.cmd("split")
local win = vim.api.nvim_get_current_win()
local target_height = 10

vim.api.nvim_win_set_height(win, target_height)
assert.equals(target_height, vim.api.nvim_win_get_height(win))
Comment on lines +109 to +111

Copilot AI Dec 6, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test sets the window height to exactly 10 and then asserts it equals 10, but window height might be constrained by terminal size or other factors. Consider using a range check or at least verifying the height changed from its original value.

Suggested change
vim.api.nvim_win_set_height(win, target_height)
assert.equals(target_height, vim.api.nvim_win_get_height(win))
local original_height = vim.api.nvim_win_get_height(win)
vim.api.nvim_win_set_height(win, target_height)
local new_height = vim.api.nvim_win_get_height(win)
-- Check that the height changed and is reasonably close to the target
assert.is_true(new_height ~= original_height)
assert.is_true(new_height > 0)
assert.is_true(math.abs(new_height - target_height) <= 2)

Copilot uses AI. Check for mistakes.

vim.api.nvim_win_close(win, true)
end)
end)

describe("window cursor", function()
it("should get cursor position", function()
local win = vim.api.nvim_get_current_win()
local pos = vim.api.nvim_win_get_cursor(win)
assert.is_table(pos)
assert.equals(2, #pos)
assert.is_number(pos[1])
assert.is_number(pos[2])
end)

it("should set cursor position", function()
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, { "line 1", "line 2", "line 3" })
vim.api.nvim_set_current_buf(buf)

local win = vim.api.nvim_get_current_win()
vim.api.nvim_win_set_cursor(win, { 2, 3 })

local pos = vim.api.nvim_win_get_cursor(win)
assert.equals(2, pos[1])
assert.equals(3, pos[2])

vim.api.nvim_buf_delete(buf, { force = true })
end)
end)
end)
Loading
Loading