Adding test cases
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
*.coder.*
|
||||
.coder/
|
||||
.claude/
|
||||
Makefile
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/lua
|
||||
|
||||
|
||||
48
tests/minimal_init.lua
Normal file
48
tests/minimal_init.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
-- Minimal init.lua for running tests
|
||||
-- This sets up the minimum Neovim environment needed for testing
|
||||
|
||||
-- Add the plugin to the runtimepath
|
||||
local plugin_root = vim.fn.fnamemodify(debug.getinfo(1, "S").source:sub(2), ":p:h:h")
|
||||
vim.opt.rtp:prepend(plugin_root)
|
||||
|
||||
-- Add plenary for testing (if available)
|
||||
local plenary_path = vim.fn.expand("~/.local/share/nvim/lazy/plenary.nvim")
|
||||
if vim.fn.isdirectory(plenary_path) == 1 then
|
||||
vim.opt.rtp:prepend(plenary_path)
|
||||
end
|
||||
|
||||
-- Alternative plenary paths
|
||||
local alt_plenary_paths = {
|
||||
vim.fn.expand("~/.local/share/nvim/site/pack/*/start/plenary.nvim"),
|
||||
vim.fn.expand("~/.config/nvim/plugged/plenary.nvim"),
|
||||
"/opt/homebrew/share/nvim/site/pack/packer/start/plenary.nvim",
|
||||
}
|
||||
|
||||
for _, path in ipairs(alt_plenary_paths) do
|
||||
local expanded = vim.fn.glob(path)
|
||||
if expanded ~= "" and vim.fn.isdirectory(expanded) == 1 then
|
||||
vim.opt.rtp:prepend(expanded)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Set up test environment
|
||||
vim.opt.swapfile = false
|
||||
vim.opt.backup = false
|
||||
vim.opt.writebackup = false
|
||||
|
||||
-- Initialize codetyper with test defaults
|
||||
require("codetyper").setup({
|
||||
llm = {
|
||||
provider = "ollama",
|
||||
ollama = {
|
||||
host = "http://localhost:11434",
|
||||
model = "test-model",
|
||||
},
|
||||
},
|
||||
scheduler = {
|
||||
enabled = false, -- Disable scheduler during tests
|
||||
},
|
||||
auto_gitignore = false,
|
||||
auto_open_ask = false,
|
||||
})
|
||||
62
tests/run_tests.sh
Executable file
62
tests/run_tests.sh
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
# Run codetyper.nvim tests using plenary.nvim
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${YELLOW}Running codetyper.nvim tests...${NC}"
|
||||
echo "Project root: $PROJECT_ROOT"
|
||||
echo ""
|
||||
|
||||
# Check if plenary is installed
|
||||
PLENARY_PATH=""
|
||||
POSSIBLE_PATHS=(
|
||||
"$HOME/.local/share/nvim/lazy/plenary.nvim"
|
||||
"$HOME/.local/share/nvim/site/pack/packer/start/plenary.nvim"
|
||||
"$HOME/.config/nvim/plugged/plenary.nvim"
|
||||
"/opt/homebrew/share/nvim/site/pack/packer/start/plenary.nvim"
|
||||
)
|
||||
|
||||
for path in "${POSSIBLE_PATHS[@]}"; do
|
||||
if [ -d "$path" ]; then
|
||||
PLENARY_PATH="$path"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$PLENARY_PATH" ]; then
|
||||
echo -e "${RED}Error: plenary.nvim not found!${NC}"
|
||||
echo "Please install plenary.nvim first:"
|
||||
echo " - With lazy.nvim: { 'nvim-lua/plenary.nvim' }"
|
||||
echo " - With packer: use 'nvim-lua/plenary.nvim'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found plenary at: $PLENARY_PATH"
|
||||
echo ""
|
||||
|
||||
# Run tests
|
||||
if [ "$1" == "--file" ] && [ -n "$2" ]; then
|
||||
# Run specific test file
|
||||
echo -e "${YELLOW}Running: $2${NC}"
|
||||
nvim --headless \
|
||||
-u "$SCRIPT_DIR/minimal_init.lua" \
|
||||
-c "PlenaryBustedFile $SCRIPT_DIR/spec/$2"
|
||||
else
|
||||
# Run all tests
|
||||
echo -e "${YELLOW}Running all tests in spec/ directory${NC}"
|
||||
nvim --headless \
|
||||
-u "$SCRIPT_DIR/minimal_init.lua" \
|
||||
-c "PlenaryBustedDirectory $SCRIPT_DIR/spec/ {minimal_init = '$SCRIPT_DIR/minimal_init.lua'}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Tests completed!${NC}"
|
||||
148
tests/spec/confidence_spec.lua
Normal file
148
tests/spec/confidence_spec.lua
Normal file
@@ -0,0 +1,148 @@
|
||||
---@diagnostic disable: undefined-global
|
||||
-- Tests for lua/codetyper/agent/confidence.lua
|
||||
|
||||
describe("confidence", function()
|
||||
local confidence = require("codetyper.agent.confidence")
|
||||
|
||||
describe("weights", function()
|
||||
it("should have weights that sum to 1.0", function()
|
||||
local total = 0
|
||||
for _, weight in pairs(confidence.weights) do
|
||||
total = total + weight
|
||||
end
|
||||
assert.is_near(1.0, total, 0.001)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("score", function()
|
||||
it("should return 0 for empty response", function()
|
||||
local score, breakdown = confidence.score("", "some prompt")
|
||||
|
||||
assert.equals(0, score)
|
||||
assert.equals(0, breakdown.weighted_total)
|
||||
end)
|
||||
|
||||
it("should return high score for good response", function()
|
||||
local good_response = [[
|
||||
function validateEmail(email)
|
||||
local pattern = "^[%w%.]+@[%w%.]+%.%w+$"
|
||||
return string.match(email, pattern) ~= nil
|
||||
end
|
||||
]]
|
||||
local score, breakdown = confidence.score(good_response, "create email validator")
|
||||
|
||||
assert.is_true(score > 0.7)
|
||||
assert.is_true(breakdown.syntax > 0.5)
|
||||
end)
|
||||
|
||||
it("should return lower score for response with uncertainty", function()
|
||||
local uncertain_response = [[
|
||||
-- I'm not sure if this is correct, maybe try:
|
||||
function doSomething()
|
||||
-- TODO: implement this
|
||||
-- placeholder code here
|
||||
end
|
||||
]]
|
||||
local score, _ = confidence.score(uncertain_response, "implement function")
|
||||
|
||||
assert.is_true(score < 0.7)
|
||||
end)
|
||||
|
||||
it("should penalize unbalanced brackets", function()
|
||||
local unbalanced = [[
|
||||
function test() {
|
||||
if (true) {
|
||||
console.log("missing bracket")
|
||||
]]
|
||||
local _, breakdown = confidence.score(unbalanced, "test")
|
||||
|
||||
assert.is_true(breakdown.syntax < 0.7)
|
||||
end)
|
||||
|
||||
it("should penalize short responses to long prompts", function()
|
||||
local long_prompt = "Create a comprehensive function that handles user authentication, " ..
|
||||
"validates credentials against the database, generates JWT tokens, " ..
|
||||
"handles refresh tokens, and logs all authentication attempts"
|
||||
local short_response = "done"
|
||||
|
||||
local score, breakdown = confidence.score(short_response, long_prompt)
|
||||
|
||||
assert.is_true(breakdown.length < 0.5)
|
||||
end)
|
||||
|
||||
it("should penalize repetitive code", function()
|
||||
local repetitive = [[
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
console.log("test");
|
||||
]]
|
||||
local _, breakdown = confidence.score(repetitive, "test")
|
||||
|
||||
assert.is_true(breakdown.repetition < 0.7)
|
||||
end)
|
||||
|
||||
it("should penalize truncated responses", function()
|
||||
local truncated = [[
|
||||
function process(data) {
|
||||
const result = data.map(item => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item...
|
||||
]]
|
||||
local _, breakdown = confidence.score(truncated, "test")
|
||||
|
||||
assert.is_true(breakdown.truncation < 1.0)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("needs_escalation", function()
|
||||
it("should return true for low confidence", function()
|
||||
assert.is_true(confidence.needs_escalation(0.5, 0.7))
|
||||
assert.is_true(confidence.needs_escalation(0.3, 0.7))
|
||||
end)
|
||||
|
||||
it("should return false for high confidence", function()
|
||||
assert.is_false(confidence.needs_escalation(0.8, 0.7))
|
||||
assert.is_false(confidence.needs_escalation(0.95, 0.7))
|
||||
end)
|
||||
|
||||
it("should use default threshold of 0.7", function()
|
||||
assert.is_true(confidence.needs_escalation(0.6))
|
||||
assert.is_false(confidence.needs_escalation(0.8))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("level_name", function()
|
||||
it("should return correct level names", function()
|
||||
assert.equals("excellent", confidence.level_name(0.95))
|
||||
assert.equals("good", confidence.level_name(0.85))
|
||||
assert.equals("acceptable", confidence.level_name(0.75))
|
||||
assert.equals("uncertain", confidence.level_name(0.6))
|
||||
assert.equals("poor", confidence.level_name(0.3))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("format_breakdown", function()
|
||||
it("should format breakdown correctly", function()
|
||||
local breakdown = {
|
||||
length = 0.8,
|
||||
uncertainty = 0.9,
|
||||
syntax = 1.0,
|
||||
repetition = 0.85,
|
||||
truncation = 0.95,
|
||||
weighted_total = 0.9,
|
||||
}
|
||||
|
||||
local formatted = confidence.format_breakdown(breakdown)
|
||||
|
||||
assert.is_true(formatted:match("len:0.80"))
|
||||
assert.is_true(formatted:match("unc:0.90"))
|
||||
assert.is_true(formatted:match("syn:1.00"))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
149
tests/spec/config_spec.lua
Normal file
149
tests/spec/config_spec.lua
Normal file
@@ -0,0 +1,149 @@
|
||||
---@diagnostic disable: undefined-global
|
||||
-- Tests for lua/codetyper/config.lua
|
||||
|
||||
describe("config", function()
|
||||
local config = require("codetyper.config")
|
||||
|
||||
describe("defaults", function()
|
||||
local defaults = config.defaults
|
||||
|
||||
it("should have llm configuration", function()
|
||||
assert.is_table(defaults.llm)
|
||||
assert.equals("claude", defaults.llm.provider)
|
||||
end)
|
||||
|
||||
it("should have window configuration", function()
|
||||
assert.is_table(defaults.window)
|
||||
assert.equals(25, defaults.window.width)
|
||||
assert.equals("left", defaults.window.position)
|
||||
end)
|
||||
|
||||
it("should have pattern configuration", function()
|
||||
assert.is_table(defaults.patterns)
|
||||
assert.equals("/@", defaults.patterns.open_tag)
|
||||
assert.equals("@/", defaults.patterns.close_tag)
|
||||
end)
|
||||
|
||||
it("should have scheduler configuration", function()
|
||||
assert.is_table(defaults.scheduler)
|
||||
assert.is_boolean(defaults.scheduler.enabled)
|
||||
assert.is_boolean(defaults.scheduler.ollama_scout)
|
||||
assert.is_number(defaults.scheduler.escalation_threshold)
|
||||
end)
|
||||
|
||||
it("should have claude configuration", function()
|
||||
assert.is_table(defaults.llm.claude)
|
||||
assert.is_truthy(defaults.llm.claude.model)
|
||||
end)
|
||||
|
||||
it("should have openai configuration", function()
|
||||
assert.is_table(defaults.llm.openai)
|
||||
assert.is_truthy(defaults.llm.openai.model)
|
||||
end)
|
||||
|
||||
it("should have gemini configuration", function()
|
||||
assert.is_table(defaults.llm.gemini)
|
||||
assert.is_truthy(defaults.llm.gemini.model)
|
||||
end)
|
||||
|
||||
it("should have ollama configuration", function()
|
||||
assert.is_table(defaults.llm.ollama)
|
||||
assert.is_truthy(defaults.llm.ollama.host)
|
||||
assert.is_truthy(defaults.llm.ollama.model)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("merge", function()
|
||||
it("should merge user config with defaults", function()
|
||||
local user_config = {
|
||||
llm = {
|
||||
provider = "openai",
|
||||
},
|
||||
}
|
||||
|
||||
local merged = config.merge(user_config)
|
||||
|
||||
-- User value should override
|
||||
assert.equals("openai", merged.llm.provider)
|
||||
-- Other defaults should be preserved
|
||||
assert.equals(25, merged.window.width)
|
||||
end)
|
||||
|
||||
it("should deep merge nested tables", function()
|
||||
local user_config = {
|
||||
llm = {
|
||||
claude = {
|
||||
model = "claude-opus-4",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local merged = config.merge(user_config)
|
||||
|
||||
-- User value should override
|
||||
assert.equals("claude-opus-4", merged.llm.claude.model)
|
||||
-- Provider default should be preserved
|
||||
assert.equals("claude", merged.llm.provider)
|
||||
end)
|
||||
|
||||
it("should handle empty user config", function()
|
||||
local merged = config.merge({})
|
||||
|
||||
assert.equals("claude", merged.llm.provider)
|
||||
assert.equals(25, merged.window.width)
|
||||
end)
|
||||
|
||||
it("should handle nil user config", function()
|
||||
local merged = config.merge(nil)
|
||||
|
||||
assert.equals("claude", merged.llm.provider)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("validate", function()
|
||||
it("should return true for valid config", function()
|
||||
local valid_config = config.defaults
|
||||
local is_valid, err = config.validate(valid_config)
|
||||
|
||||
assert.is_true(is_valid)
|
||||
assert.is_nil(err)
|
||||
end)
|
||||
|
||||
it("should validate provider value", function()
|
||||
local invalid_config = vim.tbl_deep_extend("force", {}, config.defaults)
|
||||
invalid_config.llm.provider = "invalid_provider"
|
||||
|
||||
local is_valid, err = config.validate(invalid_config)
|
||||
|
||||
assert.is_false(is_valid)
|
||||
assert.is_truthy(err)
|
||||
end)
|
||||
|
||||
it("should validate window width range", function()
|
||||
local invalid_config = vim.tbl_deep_extend("force", {}, config.defaults)
|
||||
invalid_config.window.width = 101 -- Over 100%
|
||||
|
||||
local is_valid, err = config.validate(invalid_config)
|
||||
|
||||
assert.is_false(is_valid)
|
||||
end)
|
||||
|
||||
it("should validate window position", function()
|
||||
local invalid_config = vim.tbl_deep_extend("force", {}, config.defaults)
|
||||
invalid_config.window.position = "center" -- Invalid
|
||||
|
||||
local is_valid, err = config.validate(invalid_config)
|
||||
|
||||
assert.is_false(is_valid)
|
||||
end)
|
||||
|
||||
it("should validate scheduler threshold range", function()
|
||||
local invalid_config = vim.tbl_deep_extend("force", {}, config.defaults)
|
||||
invalid_config.scheduler.escalation_threshold = 1.5 -- Over 1.0
|
||||
|
||||
local is_valid, err = config.validate(invalid_config)
|
||||
|
||||
assert.is_false(is_valid)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
286
tests/spec/intent_spec.lua
Normal file
286
tests/spec/intent_spec.lua
Normal file
@@ -0,0 +1,286 @@
|
||||
---@diagnostic disable: undefined-global
|
||||
-- Tests for lua/codetyper/agent/intent.lua
|
||||
|
||||
describe("intent", function()
|
||||
local intent = require("codetyper.agent.intent")
|
||||
|
||||
describe("detect", function()
|
||||
describe("complete intent", function()
|
||||
it("should detect 'complete' keyword", function()
|
||||
local result = intent.detect("complete this function")
|
||||
assert.equals("complete", result.type)
|
||||
assert.equals("replace", result.action)
|
||||
end)
|
||||
|
||||
it("should detect 'finish' keyword", function()
|
||||
local result = intent.detect("finish implementing this method")
|
||||
assert.equals("complete", result.type)
|
||||
end)
|
||||
|
||||
it("should detect 'implement' keyword", function()
|
||||
local result = intent.detect("implement the sorting algorithm")
|
||||
assert.equals("complete", result.type)
|
||||
end)
|
||||
|
||||
it("should detect 'todo' keyword", function()
|
||||
local result = intent.detect("fix the TODO here")
|
||||
assert.equals("complete", result.type)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("refactor intent", function()
|
||||
it("should detect 'refactor' keyword", function()
|
||||
local result = intent.detect("refactor this messy code")
|
||||
assert.equals("refactor", result.type)
|
||||
assert.equals("replace", result.action)
|
||||
end)
|
||||
|
||||
it("should detect 'rewrite' keyword", function()
|
||||
local result = intent.detect("rewrite using async/await")
|
||||
assert.equals("refactor", result.type)
|
||||
end)
|
||||
|
||||
it("should detect 'simplify' keyword", function()
|
||||
local result = intent.detect("simplify this logic")
|
||||
assert.equals("refactor", result.type)
|
||||
end)
|
||||
|
||||
it("should detect 'cleanup' keyword", function()
|
||||
local result = intent.detect("cleanup this code")
|
||||
assert.equals("refactor", result.type)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fix intent", function()
|
||||
it("should detect 'fix' keyword", function()
|
||||
local result = intent.detect("fix the bug in this function")
|
||||
assert.equals("fix", result.type)
|
||||
assert.equals("replace", result.action)
|
||||
end)
|
||||
|
||||
it("should detect 'debug' keyword", function()
|
||||
local result = intent.detect("debug this issue")
|
||||
assert.equals("fix", result.type)
|
||||
end)
|
||||
|
||||
it("should detect 'bug' keyword", function()
|
||||
local result = intent.detect("there's a bug here")
|
||||
assert.equals("fix", result.type)
|
||||
end)
|
||||
|
||||
it("should detect 'error' keyword", function()
|
||||
local result = intent.detect("getting an error with this code")
|
||||
assert.equals("fix", result.type)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("add intent", function()
|
||||
it("should detect 'add' keyword", function()
|
||||
local result = intent.detect("add input validation")
|
||||
assert.equals("add", result.type)
|
||||
assert.equals("insert", result.action)
|
||||
end)
|
||||
|
||||
it("should detect 'create' keyword", function()
|
||||
local result = intent.detect("create a new helper function")
|
||||
assert.equals("add", result.type)
|
||||
end)
|
||||
|
||||
it("should detect 'generate' keyword", function()
|
||||
local result = intent.detect("generate a utility function")
|
||||
assert.equals("add", result.type)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("document intent", function()
|
||||
it("should detect 'document' keyword", function()
|
||||
local result = intent.detect("document this function")
|
||||
assert.equals("document", result.type)
|
||||
assert.equals("replace", result.action)
|
||||
end)
|
||||
|
||||
it("should detect 'jsdoc' keyword", function()
|
||||
local result = intent.detect("add jsdoc comments")
|
||||
assert.equals("document", result.type)
|
||||
end)
|
||||
|
||||
it("should detect 'comment' keyword", function()
|
||||
local result = intent.detect("add comments to explain")
|
||||
assert.equals("document", result.type)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("test intent", function()
|
||||
it("should detect 'test' keyword", function()
|
||||
local result = intent.detect("write tests for this function")
|
||||
assert.equals("test", result.type)
|
||||
assert.equals("append", result.action)
|
||||
end)
|
||||
|
||||
it("should detect 'unit test' keyword", function()
|
||||
local result = intent.detect("create unit tests")
|
||||
assert.equals("test", result.type)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("optimize intent", function()
|
||||
it("should detect 'optimize' keyword", function()
|
||||
local result = intent.detect("optimize this loop")
|
||||
assert.equals("optimize", result.type)
|
||||
assert.equals("replace", result.action)
|
||||
end)
|
||||
|
||||
it("should detect 'performance' keyword", function()
|
||||
local result = intent.detect("improve performance of this function")
|
||||
assert.equals("optimize", result.type)
|
||||
end)
|
||||
|
||||
it("should detect 'faster' keyword", function()
|
||||
local result = intent.detect("make this faster")
|
||||
assert.equals("optimize", result.type)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("explain intent", function()
|
||||
it("should detect 'explain' keyword", function()
|
||||
local result = intent.detect("explain what this does")
|
||||
assert.equals("explain", result.type)
|
||||
assert.equals("none", result.action)
|
||||
end)
|
||||
|
||||
it("should detect 'what does' pattern", function()
|
||||
local result = intent.detect("what does this function do")
|
||||
assert.equals("explain", result.type)
|
||||
end)
|
||||
|
||||
it("should detect 'how does' pattern", function()
|
||||
local result = intent.detect("how does this algorithm work")
|
||||
assert.equals("explain", result.type)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("default intent", function()
|
||||
it("should default to 'add' for unknown prompts", function()
|
||||
local result = intent.detect("make it blue")
|
||||
assert.equals("add", result.type)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("scope hints", function()
|
||||
it("should detect 'this function' scope hint", function()
|
||||
local result = intent.detect("refactor this function")
|
||||
assert.equals("function", result.scope_hint)
|
||||
end)
|
||||
|
||||
it("should detect 'this class' scope hint", function()
|
||||
local result = intent.detect("document this class")
|
||||
assert.equals("class", result.scope_hint)
|
||||
end)
|
||||
|
||||
it("should detect 'this file' scope hint", function()
|
||||
local result = intent.detect("test this file")
|
||||
assert.equals("file", result.scope_hint)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("confidence", function()
|
||||
it("should have higher confidence with more keyword matches", function()
|
||||
local result1 = intent.detect("fix")
|
||||
local result2 = intent.detect("fix the bug error")
|
||||
|
||||
assert.is_true(result2.confidence >= result1.confidence)
|
||||
end)
|
||||
|
||||
it("should cap confidence at 1.0", function()
|
||||
local result = intent.detect("fix debug bug error issue solve")
|
||||
assert.is_true(result.confidence <= 1.0)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("modifies_code", function()
|
||||
it("should return true for replacement intents", function()
|
||||
assert.is_true(intent.modifies_code({ action = "replace" }))
|
||||
end)
|
||||
|
||||
it("should return true for insertion intents", function()
|
||||
assert.is_true(intent.modifies_code({ action = "insert" }))
|
||||
end)
|
||||
|
||||
it("should return false for explain intent", function()
|
||||
assert.is_false(intent.modifies_code({ action = "none" }))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("is_replacement", function()
|
||||
it("should return true for replace action", function()
|
||||
assert.is_true(intent.is_replacement({ action = "replace" }))
|
||||
end)
|
||||
|
||||
it("should return false for insert action", function()
|
||||
assert.is_false(intent.is_replacement({ action = "insert" }))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("is_insertion", function()
|
||||
it("should return true for insert action", function()
|
||||
assert.is_true(intent.is_insertion({ action = "insert" }))
|
||||
end)
|
||||
|
||||
it("should return true for append action", function()
|
||||
assert.is_true(intent.is_insertion({ action = "append" }))
|
||||
end)
|
||||
|
||||
it("should return false for replace action", function()
|
||||
assert.is_false(intent.is_insertion({ action = "replace" }))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("get_prompt_modifier", function()
|
||||
it("should return modifier for each intent type", function()
|
||||
local types = { "complete", "refactor", "fix", "add", "document", "test", "optimize", "explain" }
|
||||
|
||||
for _, type_name in ipairs(types) do
|
||||
local modifier = intent.get_prompt_modifier({ type = type_name })
|
||||
assert.is_truthy(modifier)
|
||||
assert.is_true(#modifier > 0)
|
||||
end
|
||||
end)
|
||||
|
||||
it("should return add modifier for unknown type", function()
|
||||
local modifier = intent.get_prompt_modifier({ type = "unknown" })
|
||||
assert.is_truthy(modifier)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("format", function()
|
||||
it("should format intent correctly", function()
|
||||
local i = {
|
||||
type = "refactor",
|
||||
scope_hint = "function",
|
||||
action = "replace",
|
||||
confidence = 0.85,
|
||||
}
|
||||
|
||||
local formatted = intent.format(i)
|
||||
|
||||
assert.is_true(formatted:match("refactor"))
|
||||
assert.is_true(formatted:match("function"))
|
||||
assert.is_true(formatted:match("replace"))
|
||||
assert.is_true(formatted:match("0.85"))
|
||||
end)
|
||||
|
||||
it("should handle nil scope_hint", function()
|
||||
local i = {
|
||||
type = "add",
|
||||
scope_hint = nil,
|
||||
action = "insert",
|
||||
confidence = 0.5,
|
||||
}
|
||||
|
||||
local formatted = intent.format(i)
|
||||
|
||||
assert.is_true(formatted:match("auto"))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
118
tests/spec/llm_spec.lua
Normal file
118
tests/spec/llm_spec.lua
Normal file
@@ -0,0 +1,118 @@
|
||||
---@diagnostic disable: undefined-global
|
||||
-- Tests for lua/codetyper/llm/init.lua
|
||||
|
||||
describe("llm", function()
|
||||
local llm = require("codetyper.llm")
|
||||
|
||||
describe("extract_code", function()
|
||||
it("should extract code from markdown code block", function()
|
||||
local response = [[
|
||||
Here is the code:
|
||||
|
||||
```lua
|
||||
function hello()
|
||||
print("Hello!")
|
||||
end
|
||||
```
|
||||
|
||||
That should work.
|
||||
]]
|
||||
local code = llm.extract_code(response)
|
||||
|
||||
assert.is_true(code:match("function hello"))
|
||||
assert.is_true(code:match('print%("Hello!"%)'))
|
||||
assert.is_false(code:match("```"))
|
||||
assert.is_false(code:match("Here is the code"))
|
||||
end)
|
||||
|
||||
it("should extract code from generic code block", function()
|
||||
local response = [[
|
||||
```
|
||||
const x = 1;
|
||||
const y = 2;
|
||||
```
|
||||
]]
|
||||
local code = llm.extract_code(response)
|
||||
|
||||
assert.is_true(code:match("const x = 1"))
|
||||
end)
|
||||
|
||||
it("should handle multiple code blocks (return first)", function()
|
||||
local response = [[
|
||||
```javascript
|
||||
const first = true;
|
||||
```
|
||||
|
||||
```javascript
|
||||
const second = true;
|
||||
```
|
||||
]]
|
||||
local code = llm.extract_code(response)
|
||||
|
||||
assert.is_true(code:match("first"))
|
||||
end)
|
||||
|
||||
it("should return original if no code blocks", function()
|
||||
local response = "function test() return true end"
|
||||
local code = llm.extract_code(response)
|
||||
|
||||
assert.equals(response, code)
|
||||
end)
|
||||
|
||||
it("should handle empty code blocks", function()
|
||||
local response = [[
|
||||
```
|
||||
```
|
||||
]]
|
||||
local code = llm.extract_code(response)
|
||||
|
||||
assert.equals("", vim.trim(code))
|
||||
end)
|
||||
|
||||
it("should preserve indentation in extracted code", function()
|
||||
local response = [[
|
||||
```lua
|
||||
function test()
|
||||
if true then
|
||||
print("nested")
|
||||
end
|
||||
end
|
||||
```
|
||||
]]
|
||||
local code = llm.extract_code(response)
|
||||
|
||||
assert.is_true(code:match(" if true then"))
|
||||
assert.is_true(code:match(" print"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("get_client", function()
|
||||
it("should return a client with generate function", function()
|
||||
-- This test depends on config, but verifies interface
|
||||
local client = llm.get_client()
|
||||
|
||||
assert.is_table(client)
|
||||
assert.is_function(client.generate)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("build_system_prompt", function()
|
||||
it("should include language context when provided", function()
|
||||
local context = {
|
||||
language = "typescript",
|
||||
file_path = "/test/file.ts",
|
||||
}
|
||||
|
||||
local prompt = llm.build_system_prompt(context)
|
||||
|
||||
assert.is_true(prompt:match("typescript") or prompt:match("TypeScript"))
|
||||
end)
|
||||
|
||||
it("should work with minimal context", function()
|
||||
local prompt = llm.build_system_prompt({})
|
||||
|
||||
assert.is_string(prompt)
|
||||
assert.is_true(#prompt > 0)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
280
tests/spec/logs_spec.lua
Normal file
280
tests/spec/logs_spec.lua
Normal file
@@ -0,0 +1,280 @@
|
||||
---@diagnostic disable: undefined-global
|
||||
-- Tests for lua/codetyper/agent/logs.lua
|
||||
|
||||
describe("logs", function()
|
||||
local logs
|
||||
|
||||
before_each(function()
|
||||
-- Reset module state before each test
|
||||
package.loaded["codetyper.agent.logs"] = nil
|
||||
logs = require("codetyper.agent.logs")
|
||||
end)
|
||||
|
||||
describe("log", function()
|
||||
it("should add entry to log", function()
|
||||
logs.log("info", "test message")
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals(1, #entries)
|
||||
assert.equals("info", entries[1].level)
|
||||
assert.equals("test message", entries[1].message)
|
||||
end)
|
||||
|
||||
it("should include timestamp", function()
|
||||
logs.log("info", "test")
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.is_truthy(entries[1].timestamp)
|
||||
assert.is_true(entries[1].timestamp:match("%d+:%d+:%d+"))
|
||||
end)
|
||||
|
||||
it("should include optional data", function()
|
||||
logs.log("info", "test", { key = "value" })
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals("value", entries[1].data.key)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("info", function()
|
||||
it("should log with info level", function()
|
||||
logs.info("info message")
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals("info", entries[1].level)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("debug", function()
|
||||
it("should log with debug level", function()
|
||||
logs.debug("debug message")
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals("debug", entries[1].level)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("error", function()
|
||||
it("should log with error level", function()
|
||||
logs.error("error message")
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals("error", entries[1].level)
|
||||
assert.is_true(entries[1].message:match("ERROR"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("warning", function()
|
||||
it("should log with warning level", function()
|
||||
logs.warning("warning message")
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals("warning", entries[1].level)
|
||||
assert.is_true(entries[1].message:match("WARN"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("request", function()
|
||||
it("should log API request", function()
|
||||
logs.request("claude", "claude-sonnet-4", 1000)
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals("request", entries[1].level)
|
||||
assert.is_true(entries[1].message:match("CLAUDE"))
|
||||
assert.is_true(entries[1].message:match("claude%-sonnet%-4"))
|
||||
end)
|
||||
|
||||
it("should store provider info", function()
|
||||
logs.request("openai", "gpt-4")
|
||||
|
||||
local provider, model = logs.get_provider_info()
|
||||
assert.equals("openai", provider)
|
||||
assert.equals("gpt-4", model)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("response", function()
|
||||
it("should log API response with token counts", function()
|
||||
logs.response(500, 200, "end_turn")
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals("response", entries[1].level)
|
||||
assert.is_true(entries[1].message:match("500"))
|
||||
assert.is_true(entries[1].message:match("200"))
|
||||
end)
|
||||
|
||||
it("should accumulate token totals", function()
|
||||
logs.response(100, 50)
|
||||
logs.response(200, 100)
|
||||
|
||||
local prompt_tokens, response_tokens = logs.get_token_totals()
|
||||
assert.equals(300, prompt_tokens)
|
||||
assert.equals(150, response_tokens)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("tool", function()
|
||||
it("should log tool execution", function()
|
||||
logs.tool("read_file", "start", "/path/to/file.lua")
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals("tool", entries[1].level)
|
||||
assert.is_true(entries[1].message:match("read_file"))
|
||||
end)
|
||||
|
||||
it("should show correct status icons", function()
|
||||
logs.tool("write_file", "success", "file created")
|
||||
local entries = logs.get_entries()
|
||||
assert.is_true(entries[1].message:match("OK"))
|
||||
|
||||
logs.tool("bash", "error", "command failed")
|
||||
entries = logs.get_entries()
|
||||
assert.is_true(entries[2].message:match("ERR"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("thinking", function()
|
||||
it("should log thinking step", function()
|
||||
logs.thinking("Analyzing code structure")
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals("debug", entries[1].level)
|
||||
assert.is_true(entries[1].message:match("> Analyzing"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("add", function()
|
||||
it("should add entry using type field", function()
|
||||
logs.add({ type = "info", message = "test message" })
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals(1, #entries)
|
||||
assert.equals("info", entries[1].level)
|
||||
end)
|
||||
|
||||
it("should handle clear type", function()
|
||||
logs.info("test")
|
||||
logs.add({ type = "clear" })
|
||||
|
||||
local entries = logs.get_entries()
|
||||
assert.equals(0, #entries)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("listeners", function()
|
||||
it("should notify listeners on new entries", function()
|
||||
local received = {}
|
||||
logs.add_listener(function(entry)
|
||||
table.insert(received, entry)
|
||||
end)
|
||||
|
||||
logs.info("test message")
|
||||
|
||||
assert.equals(1, #received)
|
||||
assert.equals("info", received[1].level)
|
||||
end)
|
||||
|
||||
it("should support multiple listeners", function()
|
||||
local count = 0
|
||||
logs.add_listener(function() count = count + 1 end)
|
||||
logs.add_listener(function() count = count + 1 end)
|
||||
|
||||
logs.info("test")
|
||||
|
||||
assert.equals(2, count)
|
||||
end)
|
||||
|
||||
it("should remove listener by ID", function()
|
||||
local count = 0
|
||||
local id = logs.add_listener(function() count = count + 1 end)
|
||||
|
||||
logs.info("test1")
|
||||
logs.remove_listener(id)
|
||||
logs.info("test2")
|
||||
|
||||
assert.equals(1, count)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("clear", function()
|
||||
it("should clear all entries", function()
|
||||
logs.info("test1")
|
||||
logs.info("test2")
|
||||
logs.clear()
|
||||
|
||||
assert.equals(0, #logs.get_entries())
|
||||
end)
|
||||
|
||||
it("should reset token totals", function()
|
||||
logs.response(100, 50)
|
||||
logs.clear()
|
||||
|
||||
local prompt, response = logs.get_token_totals()
|
||||
assert.equals(0, prompt)
|
||||
assert.equals(0, response)
|
||||
end)
|
||||
|
||||
it("should notify listeners of clear", function()
|
||||
local cleared = false
|
||||
logs.add_listener(function(entry)
|
||||
if entry.level == "clear" then
|
||||
cleared = true
|
||||
end
|
||||
end)
|
||||
|
||||
logs.clear()
|
||||
|
||||
assert.is_true(cleared)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("format_entry", function()
|
||||
it("should format entry for display", function()
|
||||
logs.info("test message")
|
||||
local entry = logs.get_entries()[1]
|
||||
|
||||
local formatted = logs.format_entry(entry)
|
||||
|
||||
assert.is_true(formatted:match("%[%d+:%d+:%d+%]"))
|
||||
assert.is_true(formatted:match("i")) -- info prefix
|
||||
assert.is_true(formatted:match("test message"))
|
||||
end)
|
||||
|
||||
it("should use correct level prefixes", function()
|
||||
local prefixes = {
|
||||
{ level = "info", prefix = "i" },
|
||||
{ level = "debug", prefix = "%." },
|
||||
{ level = "request", prefix = ">" },
|
||||
{ level = "response", prefix = "<" },
|
||||
{ level = "tool", prefix = "T" },
|
||||
{ level = "error", prefix = "!" },
|
||||
}
|
||||
|
||||
for _, test in ipairs(prefixes) do
|
||||
local entry = {
|
||||
timestamp = "12:00:00",
|
||||
level = test.level,
|
||||
message = "test",
|
||||
}
|
||||
local formatted = logs.format_entry(entry)
|
||||
assert.is_true(formatted:match(test.prefix), "Missing prefix for " .. test.level)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("estimate_tokens", function()
|
||||
it("should estimate tokens from text", function()
|
||||
local text = "This is a test string for token estimation."
|
||||
local tokens = logs.estimate_tokens(text)
|
||||
|
||||
-- Rough estimate: ~4 chars per token
|
||||
assert.is_true(tokens > 0)
|
||||
assert.is_true(tokens < #text) -- Should be less than character count
|
||||
end)
|
||||
|
||||
it("should handle empty string", function()
|
||||
local tokens = logs.estimate_tokens("")
|
||||
assert.equals(0, tokens)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
141
tests/spec/parser_spec.lua
Normal file
141
tests/spec/parser_spec.lua
Normal file
@@ -0,0 +1,141 @@
|
||||
---@diagnostic disable: undefined-global
|
||||
-- Tests for lua/codetyper/parser.lua
|
||||
|
||||
describe("parser", function()
|
||||
local parser = require("codetyper.parser")
|
||||
|
||||
describe("find_prompts", function()
|
||||
it("should find single-line prompt", function()
|
||||
local content = "/@ create a function @/"
|
||||
local prompts = parser.find_prompts(content, "/@", "@/")
|
||||
|
||||
assert.equals(1, #prompts)
|
||||
assert.equals(" create a function ", prompts[1].content)
|
||||
assert.equals(1, prompts[1].start_line)
|
||||
assert.equals(1, prompts[1].end_line)
|
||||
end)
|
||||
|
||||
it("should find multi-line prompt", function()
|
||||
local content = [[
|
||||
/@ create a function
|
||||
that validates email
|
||||
addresses @/
|
||||
]]
|
||||
local prompts = parser.find_prompts(content, "/@", "@/")
|
||||
|
||||
assert.equals(1, #prompts)
|
||||
assert.is_true(prompts[1].content:match("validates email"))
|
||||
assert.equals(2, prompts[1].start_line)
|
||||
assert.equals(4, prompts[1].end_line)
|
||||
end)
|
||||
|
||||
it("should find multiple prompts", function()
|
||||
local content = [[
|
||||
/@ first prompt @/
|
||||
some code here
|
||||
/@ second prompt @/
|
||||
more code
|
||||
/@ third prompt
|
||||
multiline @/
|
||||
]]
|
||||
local prompts = parser.find_prompts(content, "/@", "@/")
|
||||
|
||||
assert.equals(3, #prompts)
|
||||
assert.equals(" first prompt ", prompts[1].content)
|
||||
assert.equals(" second prompt ", prompts[2].content)
|
||||
assert.is_true(prompts[3].content:match("third prompt"))
|
||||
end)
|
||||
|
||||
it("should return empty table when no prompts found", function()
|
||||
local content = "just some regular code\nno prompts here"
|
||||
local prompts = parser.find_prompts(content, "/@", "@/")
|
||||
|
||||
assert.equals(0, #prompts)
|
||||
end)
|
||||
|
||||
it("should handle prompts with special characters", function()
|
||||
local content = "/@ add (function) with [brackets] @/"
|
||||
local prompts = parser.find_prompts(content, "/@", "@/")
|
||||
|
||||
assert.equals(1, #prompts)
|
||||
assert.is_true(prompts[1].content:match("function"))
|
||||
assert.is_true(prompts[1].content:match("brackets"))
|
||||
end)
|
||||
|
||||
it("should handle empty prompt content", function()
|
||||
local content = "/@ @/"
|
||||
local prompts = parser.find_prompts(content, "/@", "@/")
|
||||
|
||||
assert.equals(1, #prompts)
|
||||
assert.equals(" ", prompts[1].content)
|
||||
end)
|
||||
|
||||
it("should handle custom tags", function()
|
||||
local content = "<!-- prompt: create button -->"
|
||||
local prompts = parser.find_prompts(content, "<!-- prompt:", "-->")
|
||||
|
||||
assert.equals(1, #prompts)
|
||||
assert.is_true(prompts[1].content:match("create button"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("detect_prompt_type", function()
|
||||
it("should detect refactor type", function()
|
||||
assert.equals("refactor", parser.detect_prompt_type("refactor this code"))
|
||||
assert.equals("refactor", parser.detect_prompt_type("REFACTOR the function"))
|
||||
end)
|
||||
|
||||
it("should detect add type", function()
|
||||
assert.equals("add", parser.detect_prompt_type("add a new function"))
|
||||
assert.equals("add", parser.detect_prompt_type("create a component"))
|
||||
assert.equals("add", parser.detect_prompt_type("implement sorting algorithm"))
|
||||
end)
|
||||
|
||||
it("should detect document type", function()
|
||||
assert.equals("document", parser.detect_prompt_type("document this function"))
|
||||
assert.equals("document", parser.detect_prompt_type("add jsdoc comments"))
|
||||
assert.equals("document", parser.detect_prompt_type("comment the code"))
|
||||
end)
|
||||
|
||||
it("should detect explain type", function()
|
||||
assert.equals("explain", parser.detect_prompt_type("explain this code"))
|
||||
assert.equals("explain", parser.detect_prompt_type("what does this do"))
|
||||
assert.equals("explain", parser.detect_prompt_type("how does this work"))
|
||||
end)
|
||||
|
||||
it("should return generic for unknown types", function()
|
||||
assert.equals("generic", parser.detect_prompt_type("do something"))
|
||||
assert.equals("generic", parser.detect_prompt_type("make it better"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("clean_prompt", function()
|
||||
it("should trim whitespace", function()
|
||||
assert.equals("hello", parser.clean_prompt(" hello "))
|
||||
assert.equals("hello", parser.clean_prompt("\n\nhello\n\n"))
|
||||
end)
|
||||
|
||||
it("should normalize multiple newlines", function()
|
||||
local input = "line1\n\n\n\nline2"
|
||||
local expected = "line1\n\nline2"
|
||||
assert.equals(expected, parser.clean_prompt(input))
|
||||
end)
|
||||
|
||||
it("should preserve single newlines", function()
|
||||
local input = "line1\nline2\nline3"
|
||||
assert.equals(input, parser.clean_prompt(input))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("has_closing_tag", function()
|
||||
it("should return true when closing tag exists", function()
|
||||
assert.is_true(parser.has_closing_tag("some text @/", "@/"))
|
||||
assert.is_true(parser.has_closing_tag("@/", "@/"))
|
||||
end)
|
||||
|
||||
it("should return false when closing tag missing", function()
|
||||
assert.is_false(parser.has_closing_tag("some text", "@/"))
|
||||
assert.is_false(parser.has_closing_tag("", "@/"))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
305
tests/spec/patch_spec.lua
Normal file
305
tests/spec/patch_spec.lua
Normal file
@@ -0,0 +1,305 @@
|
||||
---@diagnostic disable: undefined-global
|
||||
-- Tests for lua/codetyper/agent/patch.lua
|
||||
|
||||
describe("patch", function()
|
||||
local patch
|
||||
|
||||
before_each(function()
|
||||
-- Reset module state before each test
|
||||
package.loaded["codetyper.agent.patch"] = nil
|
||||
patch = require("codetyper.agent.patch")
|
||||
end)
|
||||
|
||||
describe("generate_id", function()
|
||||
it("should generate unique IDs", function()
|
||||
local id1 = patch.generate_id()
|
||||
local id2 = patch.generate_id()
|
||||
|
||||
assert.is_not.equals(id1, id2)
|
||||
assert.is_true(id1:match("^patch_"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("snapshot_buffer", function()
|
||||
local test_buf
|
||||
|
||||
before_each(function()
|
||||
test_buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_lines(test_buf, 0, -1, false, {
|
||||
"line 1",
|
||||
"line 2",
|
||||
"line 3",
|
||||
"line 4",
|
||||
"line 5",
|
||||
})
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
if vim.api.nvim_buf_is_valid(test_buf) then
|
||||
vim.api.nvim_buf_delete(test_buf, { force = true })
|
||||
end
|
||||
end)
|
||||
|
||||
it("should capture changedtick", function()
|
||||
local snapshot = patch.snapshot_buffer(test_buf)
|
||||
|
||||
assert.is_number(snapshot.changedtick)
|
||||
end)
|
||||
|
||||
it("should capture content hash", function()
|
||||
local snapshot = patch.snapshot_buffer(test_buf)
|
||||
|
||||
assert.is_string(snapshot.content_hash)
|
||||
assert.is_true(#snapshot.content_hash > 0)
|
||||
end)
|
||||
|
||||
it("should snapshot specific range", function()
|
||||
local snapshot = patch.snapshot_buffer(test_buf, { start_line = 2, end_line = 4 })
|
||||
|
||||
assert.equals(test_buf, snapshot.bufnr)
|
||||
assert.is_truthy(snapshot.range)
|
||||
assert.equals(2, snapshot.range.start_line)
|
||||
assert.equals(4, snapshot.range.end_line)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("is_snapshot_stale", function()
|
||||
local test_buf
|
||||
|
||||
before_each(function()
|
||||
test_buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_lines(test_buf, 0, -1, false, {
|
||||
"original content",
|
||||
"line 2",
|
||||
})
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
if vim.api.nvim_buf_is_valid(test_buf) then
|
||||
vim.api.nvim_buf_delete(test_buf, { force = true })
|
||||
end
|
||||
end)
|
||||
|
||||
it("should return false for unchanged buffer", function()
|
||||
local snapshot = patch.snapshot_buffer(test_buf)
|
||||
|
||||
local is_stale, reason = patch.is_snapshot_stale(snapshot)
|
||||
|
||||
assert.is_false(is_stale)
|
||||
assert.is_nil(reason)
|
||||
end)
|
||||
|
||||
it("should return true when content changes", function()
|
||||
local snapshot = patch.snapshot_buffer(test_buf)
|
||||
|
||||
-- Modify buffer
|
||||
vim.api.nvim_buf_set_lines(test_buf, 0, 1, false, { "modified content" })
|
||||
|
||||
local is_stale, reason = patch.is_snapshot_stale(snapshot)
|
||||
|
||||
assert.is_true(is_stale)
|
||||
assert.equals("content_changed", reason)
|
||||
end)
|
||||
|
||||
it("should return true for invalid buffer", function()
|
||||
local snapshot = patch.snapshot_buffer(test_buf)
|
||||
|
||||
-- Delete buffer
|
||||
vim.api.nvim_buf_delete(test_buf, { force = true })
|
||||
|
||||
local is_stale, reason = patch.is_snapshot_stale(snapshot)
|
||||
|
||||
assert.is_true(is_stale)
|
||||
assert.equals("buffer_invalid", reason)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("queue_patch", function()
|
||||
it("should add patch to queue", function()
|
||||
local p = {
|
||||
event_id = "test_event",
|
||||
target_bufnr = 1,
|
||||
target_path = "/test/file.lua",
|
||||
original_snapshot = {
|
||||
bufnr = 1,
|
||||
changedtick = 0,
|
||||
content_hash = "abc123",
|
||||
},
|
||||
generated_code = "function test() end",
|
||||
confidence = 0.9,
|
||||
}
|
||||
|
||||
local queued = patch.queue_patch(p)
|
||||
|
||||
assert.is_truthy(queued.id)
|
||||
assert.equals("pending", queued.status)
|
||||
|
||||
local pending = patch.get_pending()
|
||||
assert.equals(1, #pending)
|
||||
end)
|
||||
|
||||
it("should set default status", function()
|
||||
local p = {
|
||||
event_id = "test",
|
||||
generated_code = "code",
|
||||
confidence = 0.8,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "x" },
|
||||
}
|
||||
|
||||
local queued = patch.queue_patch(p)
|
||||
|
||||
assert.equals("pending", queued.status)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("get", function()
|
||||
it("should return patch by ID", function()
|
||||
local p = patch.queue_patch({
|
||||
event_id = "test",
|
||||
generated_code = "code",
|
||||
confidence = 0.8,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "x" },
|
||||
})
|
||||
|
||||
local found = patch.get(p.id)
|
||||
|
||||
assert.is_not.nil(found)
|
||||
assert.equals(p.id, found.id)
|
||||
end)
|
||||
|
||||
it("should return nil for unknown ID", function()
|
||||
local found = patch.get("unknown_id")
|
||||
assert.is_nil(found)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("mark_applied", function()
|
||||
it("should mark patch as applied", function()
|
||||
local p = patch.queue_patch({
|
||||
event_id = "test",
|
||||
generated_code = "code",
|
||||
confidence = 0.8,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "x" },
|
||||
})
|
||||
|
||||
local success = patch.mark_applied(p.id)
|
||||
|
||||
assert.is_true(success)
|
||||
assert.equals("applied", patch.get(p.id).status)
|
||||
assert.is_truthy(patch.get(p.id).applied_at)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("mark_stale", function()
|
||||
it("should mark patch as stale with reason", function()
|
||||
local p = patch.queue_patch({
|
||||
event_id = "test",
|
||||
generated_code = "code",
|
||||
confidence = 0.8,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "x" },
|
||||
})
|
||||
|
||||
local success = patch.mark_stale(p.id, "content_changed")
|
||||
|
||||
assert.is_true(success)
|
||||
assert.equals("stale", patch.get(p.id).status)
|
||||
assert.equals("content_changed", patch.get(p.id).stale_reason)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("stats", function()
|
||||
it("should return correct statistics", function()
|
||||
local p1 = patch.queue_patch({
|
||||
event_id = "test1",
|
||||
generated_code = "code1",
|
||||
confidence = 0.8,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "x" },
|
||||
})
|
||||
|
||||
patch.queue_patch({
|
||||
event_id = "test2",
|
||||
generated_code = "code2",
|
||||
confidence = 0.9,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "y" },
|
||||
})
|
||||
|
||||
patch.mark_applied(p1.id)
|
||||
|
||||
local stats = patch.stats()
|
||||
|
||||
assert.equals(2, stats.total)
|
||||
assert.equals(1, stats.pending)
|
||||
assert.equals(1, stats.applied)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("get_for_event", function()
|
||||
it("should return patches for specific event", function()
|
||||
patch.queue_patch({
|
||||
event_id = "event_a",
|
||||
generated_code = "code1",
|
||||
confidence = 0.8,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "x" },
|
||||
})
|
||||
|
||||
patch.queue_patch({
|
||||
event_id = "event_b",
|
||||
generated_code = "code2",
|
||||
confidence = 0.9,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "y" },
|
||||
})
|
||||
|
||||
patch.queue_patch({
|
||||
event_id = "event_a",
|
||||
generated_code = "code3",
|
||||
confidence = 0.7,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "z" },
|
||||
})
|
||||
|
||||
local event_a_patches = patch.get_for_event("event_a")
|
||||
|
||||
assert.equals(2, #event_a_patches)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("clear", function()
|
||||
it("should clear all patches", function()
|
||||
patch.queue_patch({
|
||||
event_id = "test",
|
||||
generated_code = "code",
|
||||
confidence = 0.8,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "x" },
|
||||
})
|
||||
|
||||
patch.clear()
|
||||
|
||||
assert.equals(0, #patch.get_pending())
|
||||
assert.equals(0, patch.stats().total)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("cancel_for_buffer", function()
|
||||
it("should cancel patches for specific buffer", function()
|
||||
patch.queue_patch({
|
||||
event_id = "test1",
|
||||
target_bufnr = 1,
|
||||
generated_code = "code1",
|
||||
confidence = 0.8,
|
||||
original_snapshot = { bufnr = 1, changedtick = 0, content_hash = "x" },
|
||||
})
|
||||
|
||||
patch.queue_patch({
|
||||
event_id = "test2",
|
||||
target_bufnr = 2,
|
||||
generated_code = "code2",
|
||||
confidence = 0.9,
|
||||
original_snapshot = { bufnr = 2, changedtick = 0, content_hash = "y" },
|
||||
})
|
||||
|
||||
local cancelled = patch.cancel_for_buffer(1)
|
||||
|
||||
assert.equals(1, cancelled)
|
||||
assert.equals(1, #patch.get_pending())
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
332
tests/spec/queue_spec.lua
Normal file
332
tests/spec/queue_spec.lua
Normal file
@@ -0,0 +1,332 @@
|
||||
---@diagnostic disable: undefined-global
|
||||
-- Tests for lua/codetyper/agent/queue.lua
|
||||
|
||||
describe("queue", function()
|
||||
local queue
|
||||
|
||||
before_each(function()
|
||||
-- Reset module state before each test
|
||||
package.loaded["codetyper.agent.queue"] = nil
|
||||
queue = require("codetyper.agent.queue")
|
||||
end)
|
||||
|
||||
describe("generate_id", function()
|
||||
it("should generate unique IDs", function()
|
||||
local id1 = queue.generate_id()
|
||||
local id2 = queue.generate_id()
|
||||
|
||||
assert.is_not.equals(id1, id2)
|
||||
assert.is_true(id1:match("^evt_"))
|
||||
assert.is_true(id2:match("^evt_"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("hash_content", function()
|
||||
it("should generate consistent hashes", function()
|
||||
local content = "test content"
|
||||
local hash1 = queue.hash_content(content)
|
||||
local hash2 = queue.hash_content(content)
|
||||
|
||||
assert.equals(hash1, hash2)
|
||||
end)
|
||||
|
||||
it("should generate different hashes for different content", function()
|
||||
local hash1 = queue.hash_content("content A")
|
||||
local hash2 = queue.hash_content("content B")
|
||||
|
||||
assert.is_not.equals(hash1, hash2)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("enqueue", function()
|
||||
it("should add event to queue", function()
|
||||
local event = {
|
||||
bufnr = 1,
|
||||
prompt_content = "test prompt",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
}
|
||||
|
||||
local enqueued = queue.enqueue(event)
|
||||
|
||||
assert.is_not.nil(enqueued.id)
|
||||
assert.equals("pending", enqueued.status)
|
||||
assert.equals(1, queue.size())
|
||||
end)
|
||||
|
||||
it("should set default priority to 2", function()
|
||||
local event = {
|
||||
bufnr = 1,
|
||||
prompt_content = "test prompt",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
}
|
||||
|
||||
local enqueued = queue.enqueue(event)
|
||||
|
||||
assert.equals(2, enqueued.priority)
|
||||
end)
|
||||
|
||||
it("should maintain priority order", function()
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "low priority",
|
||||
target_path = "/test/file.lua",
|
||||
priority = 3,
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "high priority",
|
||||
target_path = "/test/file.lua",
|
||||
priority = 1,
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
local first = queue.dequeue()
|
||||
assert.equals("high priority", first.prompt_content)
|
||||
end)
|
||||
|
||||
it("should generate content hash automatically", function()
|
||||
local event = {
|
||||
bufnr = 1,
|
||||
prompt_content = "test prompt",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
}
|
||||
|
||||
local enqueued = queue.enqueue(event)
|
||||
|
||||
assert.is_not.nil(enqueued.content_hash)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("dequeue", function()
|
||||
it("should return nil when queue is empty", function()
|
||||
local event = queue.dequeue()
|
||||
assert.is_nil(event)
|
||||
end)
|
||||
|
||||
it("should return and mark event as processing", function()
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "test",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
local event = queue.dequeue()
|
||||
|
||||
assert.is_not.nil(event)
|
||||
assert.equals("processing", event.status)
|
||||
end)
|
||||
|
||||
it("should skip non-pending events", function()
|
||||
local evt1 = queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "first",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "second",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
-- Mark first as completed
|
||||
queue.complete(evt1.id)
|
||||
|
||||
local event = queue.dequeue()
|
||||
assert.equals("second", event.prompt_content)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("peek", function()
|
||||
it("should return next pending without removing", function()
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "test",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
local event1 = queue.peek()
|
||||
local event2 = queue.peek()
|
||||
|
||||
assert.is_not.nil(event1)
|
||||
assert.equals(event1.id, event2.id)
|
||||
assert.equals("pending", event1.status)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("get", function()
|
||||
it("should return event by ID", function()
|
||||
local enqueued = queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "test",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
local event = queue.get(enqueued.id)
|
||||
|
||||
assert.is_not.nil(event)
|
||||
assert.equals(enqueued.id, event.id)
|
||||
end)
|
||||
|
||||
it("should return nil for unknown ID", function()
|
||||
local event = queue.get("unknown_id")
|
||||
assert.is_nil(event)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("update_status", function()
|
||||
it("should update event status", function()
|
||||
local enqueued = queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "test",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
local success = queue.update_status(enqueued.id, "completed")
|
||||
|
||||
assert.is_true(success)
|
||||
assert.equals("completed", queue.get(enqueued.id).status)
|
||||
end)
|
||||
|
||||
it("should return false for unknown ID", function()
|
||||
local success = queue.update_status("unknown_id", "completed")
|
||||
assert.is_false(success)
|
||||
end)
|
||||
|
||||
it("should merge extra fields", function()
|
||||
local enqueued = queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "test",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
queue.update_status(enqueued.id, "completed", { error = "test error" })
|
||||
|
||||
local event = queue.get(enqueued.id)
|
||||
assert.equals("test error", event.error)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("cancel_for_buffer", function()
|
||||
it("should cancel all pending events for buffer", function()
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "buffer 1 - first",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "buffer 1 - second",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
queue.enqueue({
|
||||
bufnr = 2,
|
||||
prompt_content = "buffer 2",
|
||||
target_path = "/test/file2.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
local cancelled = queue.cancel_for_buffer(1)
|
||||
|
||||
assert.equals(2, cancelled)
|
||||
assert.equals(1, queue.pending_count())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("stats", function()
|
||||
it("should return correct statistics", function()
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "pending",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
local evt = queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "to complete",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
queue.complete(evt.id)
|
||||
|
||||
local stats = queue.stats()
|
||||
|
||||
assert.equals(2, stats.total)
|
||||
assert.equals(1, stats.pending)
|
||||
assert.equals(1, stats.completed)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("clear", function()
|
||||
it("should clear all events", function()
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "test",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
queue.clear()
|
||||
|
||||
assert.equals(0, queue.size())
|
||||
end)
|
||||
|
||||
it("should clear only specified status", function()
|
||||
local evt = queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "to complete",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
queue.complete(evt.id)
|
||||
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "pending",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
queue.clear("completed")
|
||||
|
||||
assert.equals(1, queue.size())
|
||||
assert.equals(1, queue.pending_count())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("listeners", function()
|
||||
it("should notify listeners on enqueue", function()
|
||||
local notifications = {}
|
||||
queue.add_listener(function(event_type, event, size)
|
||||
table.insert(notifications, { type = event_type, event = event, size = size })
|
||||
end)
|
||||
|
||||
queue.enqueue({
|
||||
bufnr = 1,
|
||||
prompt_content = "test",
|
||||
target_path = "/test/file.lua",
|
||||
range = { start_line = 1, end_line = 1 },
|
||||
})
|
||||
|
||||
assert.equals(1, #notifications)
|
||||
assert.equals("enqueue", notifications[1].type)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
139
tests/spec/utils_spec.lua
Normal file
139
tests/spec/utils_spec.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
---@diagnostic disable: undefined-global
|
||||
-- Tests for lua/codetyper/utils.lua
|
||||
|
||||
describe("utils", function()
|
||||
local utils = require("codetyper.utils")
|
||||
|
||||
describe("is_coder_file", function()
|
||||
it("should return true for coder files", function()
|
||||
assert.is_true(utils.is_coder_file("index.coder.ts"))
|
||||
assert.is_true(utils.is_coder_file("main.coder.lua"))
|
||||
assert.is_true(utils.is_coder_file("/path/to/file.coder.py"))
|
||||
end)
|
||||
|
||||
it("should return false for regular files", function()
|
||||
assert.is_false(utils.is_coder_file("index.ts"))
|
||||
assert.is_false(utils.is_coder_file("main.lua"))
|
||||
assert.is_false(utils.is_coder_file("coder.ts"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("get_target_path", function()
|
||||
it("should convert coder path to target path", function()
|
||||
assert.equals("index.ts", utils.get_target_path("index.coder.ts"))
|
||||
assert.equals("main.lua", utils.get_target_path("main.coder.lua"))
|
||||
assert.equals("/path/to/file.py", utils.get_target_path("/path/to/file.coder.py"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("get_coder_path", function()
|
||||
it("should convert target path to coder path", function()
|
||||
assert.equals("index.coder.ts", utils.get_coder_path("index.ts"))
|
||||
assert.equals("main.coder.lua", utils.get_coder_path("main.lua"))
|
||||
end)
|
||||
|
||||
it("should preserve directory path", function()
|
||||
local result = utils.get_coder_path("/path/to/file.py")
|
||||
assert.is_truthy(result:match("/path/to/"))
|
||||
assert.is_truthy(result:match("file%.coder%.py"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("escape_pattern", function()
|
||||
it("should escape special pattern characters", function()
|
||||
-- Note: @ is NOT a special Lua pattern character
|
||||
-- Special chars are: ( ) . % + - * ? [ ] ^ $
|
||||
assert.equals("/@", utils.escape_pattern("/@"))
|
||||
assert.equals("@/", utils.escape_pattern("@/"))
|
||||
assert.equals("hello%.world", utils.escape_pattern("hello.world"))
|
||||
assert.equals("test%+pattern", utils.escape_pattern("test+pattern"))
|
||||
end)
|
||||
|
||||
it("should handle multiple special characters", function()
|
||||
local input = "(test)[pattern]"
|
||||
local escaped = utils.escape_pattern(input)
|
||||
-- Use string.find with plain=true to avoid pattern interpretation
|
||||
assert.is_truthy(string.find(escaped, "%(", 1, true))
|
||||
assert.is_truthy(string.find(escaped, "%)", 1, true))
|
||||
assert.is_truthy(string.find(escaped, "%[", 1, true))
|
||||
assert.is_truthy(string.find(escaped, "%]", 1, true))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("file operations", function()
|
||||
local test_dir
|
||||
local test_file
|
||||
|
||||
before_each(function()
|
||||
test_dir = vim.fn.tempname()
|
||||
utils.ensure_dir(test_dir)
|
||||
test_file = test_dir .. "/test.txt"
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
vim.fn.delete(test_dir, "rf")
|
||||
end)
|
||||
|
||||
describe("ensure_dir", function()
|
||||
it("should create directory", function()
|
||||
local new_dir = test_dir .. "/subdir"
|
||||
local result = utils.ensure_dir(new_dir)
|
||||
|
||||
assert.is_true(result)
|
||||
assert.equals(1, vim.fn.isdirectory(new_dir))
|
||||
end)
|
||||
|
||||
it("should return true for existing directory", function()
|
||||
local result = utils.ensure_dir(test_dir)
|
||||
assert.is_true(result)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("write_file", function()
|
||||
it("should write content to file", function()
|
||||
local result = utils.write_file(test_file, "test content")
|
||||
|
||||
assert.is_true(result)
|
||||
assert.is_true(utils.file_exists(test_file))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("read_file", function()
|
||||
it("should read file content", function()
|
||||
utils.write_file(test_file, "test content")
|
||||
|
||||
local content = utils.read_file(test_file)
|
||||
|
||||
assert.equals("test content", content)
|
||||
end)
|
||||
|
||||
it("should return nil for non-existent file", function()
|
||||
local content = utils.read_file("/non/existent/file.txt")
|
||||
assert.is_nil(content)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("file_exists", function()
|
||||
it("should return true for existing file", function()
|
||||
utils.write_file(test_file, "content")
|
||||
assert.is_true(utils.file_exists(test_file))
|
||||
end)
|
||||
|
||||
it("should return false for non-existent file", function()
|
||||
assert.is_false(utils.file_exists("/non/existent/file.txt"))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("get_filetype", function()
|
||||
it("should return filetype for buffer", function()
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.bo[buf].filetype = "lua"
|
||||
|
||||
local ft = utils.get_filetype(buf)
|
||||
|
||||
assert.equals("lua", ft)
|
||||
vim.api.nvim_buf_delete(buf, { force = true })
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
Reference in New Issue
Block a user