feat: add luatest (#1064)
This commit is contained in:
parent
6c10081899
commit
e14eb002d5
38
.github/workflows/lua.yaml
vendored
38
.github/workflows/lua.yaml
vendored
@ -13,6 +13,41 @@ on:
|
|||||||
- "**/*.lua"
|
- "**/*.lua"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
# reference from: https://github.com/nvim-lua/plenary.nvim/blob/2d9b06177a975543726ce5c73fca176cedbffe9d/.github/workflows/default.yml#L6C3-L43C20
|
||||||
|
run_tests:
|
||||||
|
name: unit tests
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
rev: v0.10.0/nvim-linux64.tar.gz
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- run: date +%F > todays-date
|
||||||
|
- name: Restore cache for today's nightly.
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: _neovim
|
||||||
|
key: ${{ runner.os }}-${{ matrix.rev }}-${{ hashFiles('todays-date') }}
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
test -d _neovim || {
|
||||||
|
mkdir -p _neovim
|
||||||
|
curl -sL "https://github.com/neovim/neovim/releases/download/${{ matrix.rev }}" | tar xzf - --strip-components=1 -C "${PWD}/_neovim"
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
export PATH="${PWD}/_neovim/bin:${PATH}"
|
||||||
|
export VIM="${PWD}/_neovim/share/nvim/runtime"
|
||||||
|
# install nvim-lua/plenary.nvim
|
||||||
|
git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim
|
||||||
|
nvim --version
|
||||||
|
make luatest
|
||||||
|
|
||||||
stylua:
|
stylua:
|
||||||
name: Check Lua style
|
name: Check Lua style
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -24,7 +59,8 @@ jobs:
|
|||||||
crate: stylua
|
crate: stylua
|
||||||
features: lua54
|
features: lua54
|
||||||
- run: stylua --version
|
- run: stylua --version
|
||||||
- run: stylua --check ./lua/ ./plugin/
|
- run: stylua --color always --check ./lua/ ./plugin/ ./tests/
|
||||||
|
|
||||||
luacheck:
|
luacheck:
|
||||||
name: Lint Lua
|
name: Lint Lua
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
15
Makefile
15
Makefile
@ -66,8 +66,8 @@ clean:
|
|||||||
luacheck:
|
luacheck:
|
||||||
@luacheck `find -name "*.lua"` --codes
|
@luacheck `find -name "*.lua"` --codes
|
||||||
|
|
||||||
stylecheck:
|
luastylecheck:
|
||||||
@stylua --check lua/ plugin/
|
@stylua --check lua/ plugin/ tests/
|
||||||
|
|
||||||
stylefix:
|
stylefix:
|
||||||
@stylua lua/ plugin/
|
@stylua lua/ plugin/
|
||||||
@ -81,3 +81,14 @@ ruststylecheck:
|
|||||||
rustlint:
|
rustlint:
|
||||||
@rustup component add clippy 2> /dev/null
|
@rustup component add clippy 2> /dev/null
|
||||||
@cargo clippy -F luajit --all -- -F clippy::dbg-macro -D warnings
|
@cargo clippy -F luajit --all -- -F clippy::dbg-macro -D warnings
|
||||||
|
|
||||||
|
.PHONY: rusttest
|
||||||
|
rusttest:
|
||||||
|
@cargo test --features luajit
|
||||||
|
|
||||||
|
.PHONY: luatest
|
||||||
|
luatest:
|
||||||
|
nvim --headless -c "PlenaryBustedDirectory tests/"
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: luacheck luastylecheck ruststylecheck rustlint
|
||||||
|
@ -153,12 +153,12 @@ end
|
|||||||
---@param str string
|
---@param str string
|
||||||
---@param opts? {suffix?: string, prefix?: string}
|
---@param opts? {suffix?: string, prefix?: string}
|
||||||
function M.trim(str, opts)
|
function M.trim(str, opts)
|
||||||
if not opts then return str end
|
|
||||||
local res = str
|
local res = str
|
||||||
|
if not opts then return res end
|
||||||
if opts.suffix then
|
if opts.suffix then
|
||||||
res = str:sub(#str - #opts.suffix + 1) == opts.suffix and str:sub(1, #str - #opts.suffix) or str
|
res = res:sub(#res - #opts.suffix + 1) == opts.suffix and res:sub(1, #res - #opts.suffix) or res
|
||||||
end
|
end
|
||||||
if opts.prefix then res = str:sub(1, #opts.prefix) == opts.prefix and str:sub(#opts.prefix + 1) or str end
|
if opts.prefix then res = res:sub(1, #opts.prefix) == opts.prefix and res:sub(#opts.prefix + 1) or res end
|
||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -463,6 +463,8 @@ function M.url_join(...)
|
|||||||
::continue::
|
::continue::
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if result:sub(-1) == "/" then result = result:sub(1, -2) end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
141
tests/utils/file_spec.lua
Normal file
141
tests/utils/file_spec.lua
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
local File = require("avante.utils.file")
|
||||||
|
local mock = require("luassert.mock")
|
||||||
|
local stub = require("luassert.stub")
|
||||||
|
|
||||||
|
describe("File", function()
|
||||||
|
local test_file = "test.txt"
|
||||||
|
local test_content = "test content\nline 2"
|
||||||
|
|
||||||
|
-- Mock vim API
|
||||||
|
local api_mock
|
||||||
|
local loop_mock
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
-- Setup mocks
|
||||||
|
api_mock = mock(vim.api, true)
|
||||||
|
loop_mock = mock(vim.loop, true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
-- Clean up mocks
|
||||||
|
mock.revert(api_mock)
|
||||||
|
mock.revert(loop_mock)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("read_content", function()
|
||||||
|
it("should read file content", function()
|
||||||
|
vim.fn.readfile = stub().returns({ "test content", "line 2" })
|
||||||
|
|
||||||
|
local content = File.read_content(test_file)
|
||||||
|
assert.equals(test_content, content)
|
||||||
|
assert.stub(vim.fn.readfile).was_called_with(test_file)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return nil for non-existent file", function()
|
||||||
|
vim.fn.readfile = stub().returns(nil)
|
||||||
|
|
||||||
|
local content = File.read_content("nonexistent.txt")
|
||||||
|
assert.is_nil(content)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should use cache for subsequent reads", function()
|
||||||
|
vim.fn.readfile = stub().returns({ "test content", "line 2" })
|
||||||
|
local new_test_file = "test1.txt"
|
||||||
|
|
||||||
|
-- First read
|
||||||
|
local content1 = File.read_content(new_test_file)
|
||||||
|
assert.equals(test_content, content1)
|
||||||
|
|
||||||
|
-- Second read (should use cache)
|
||||||
|
local content2 = File.read_content(new_test_file)
|
||||||
|
assert.equals(test_content, content2)
|
||||||
|
|
||||||
|
-- readfile should only be called once
|
||||||
|
assert.stub(vim.fn.readfile).was_called(1)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("exists", function()
|
||||||
|
it("should return true for existing file", function()
|
||||||
|
loop_mock.fs_stat.returns({ type = "file" })
|
||||||
|
|
||||||
|
assert.is_true(File.exists(test_file))
|
||||||
|
assert.stub(loop_mock.fs_stat).was_called_with(test_file)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return false for non-existent file", function()
|
||||||
|
loop_mock.fs_stat.returns(nil)
|
||||||
|
|
||||||
|
assert.is_false(File.exists("nonexistent.txt"))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("get_file_icon", function()
|
||||||
|
local Filetype
|
||||||
|
local devicons_mock
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
-- Mock plenary.filetype
|
||||||
|
Filetype = mock(require("plenary.filetype"), true)
|
||||||
|
-- Prepare devicons mock
|
||||||
|
devicons_mock = {
|
||||||
|
get_icon = stub().returns(""),
|
||||||
|
}
|
||||||
|
-- Reset _G.MiniIcons
|
||||||
|
_G.MiniIcons = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function() mock.revert(Filetype) end)
|
||||||
|
|
||||||
|
it("should get icon using nvim-web-devicons", function()
|
||||||
|
Filetype.detect.returns("lua")
|
||||||
|
devicons_mock.get_icon.returns("")
|
||||||
|
|
||||||
|
-- Mock require for nvim-web-devicons
|
||||||
|
local old_require = _G.require
|
||||||
|
_G.require = function(module)
|
||||||
|
if module == "nvim-web-devicons" then return devicons_mock end
|
||||||
|
return old_require(module)
|
||||||
|
end
|
||||||
|
|
||||||
|
local icon = File.get_file_icon("test.lua")
|
||||||
|
assert.equals("", icon)
|
||||||
|
assert.stub(Filetype.detect).was_called_with("test.lua", {})
|
||||||
|
assert.stub(devicons_mock.get_icon).was_called()
|
||||||
|
|
||||||
|
_G.require = old_require
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should get icon using MiniIcons if available", function()
|
||||||
|
_G.MiniIcons = {
|
||||||
|
get = stub().returns("", "color", "name"),
|
||||||
|
}
|
||||||
|
|
||||||
|
Filetype.detect.returns("lua")
|
||||||
|
|
||||||
|
local icon = File.get_file_icon("test.lua")
|
||||||
|
assert.equals("", icon)
|
||||||
|
assert.stub(Filetype.detect).was_called_with("test.lua", {})
|
||||||
|
assert.stub(_G.MiniIcons.get).was_called_with("filetype", "lua")
|
||||||
|
|
||||||
|
_G.MiniIcons = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should handle unknown filetypes", function()
|
||||||
|
Filetype.detect.returns(nil)
|
||||||
|
devicons_mock.get_icon.returns("")
|
||||||
|
|
||||||
|
-- Mock require for nvim-web-devicons
|
||||||
|
local old_require = _G.require
|
||||||
|
_G.require = function(module)
|
||||||
|
if module == "nvim-web-devicons" then return devicons_mock end
|
||||||
|
return old_require(module)
|
||||||
|
end
|
||||||
|
|
||||||
|
local icon = File.get_file_icon("unknown.xyz")
|
||||||
|
assert.equals("", icon)
|
||||||
|
|
||||||
|
_G.require = old_require
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
123
tests/utils/init_spec.lua
Normal file
123
tests/utils/init_spec.lua
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
local Utils = require("avante.utils")
|
||||||
|
|
||||||
|
describe("Utils", function()
|
||||||
|
describe("trim", function()
|
||||||
|
it("should trim prefix", function() assert.equals("test", Utils.trim("prefix_test", { prefix = "prefix_" })) end)
|
||||||
|
|
||||||
|
it("should trim suffix", function() assert.equals("test", Utils.trim("test_suffix", { suffix = "_suffix" })) end)
|
||||||
|
|
||||||
|
it(
|
||||||
|
"should trim both prefix and suffix",
|
||||||
|
function() assert.equals("test", Utils.trim("prefix_test_suffix", { prefix = "prefix_", suffix = "_suffix" })) end
|
||||||
|
)
|
||||||
|
|
||||||
|
it(
|
||||||
|
"should return original string if no match",
|
||||||
|
function() assert.equals("test", Utils.trim("test", { prefix = "xxx", suffix = "yyy" })) end
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("url_join", function()
|
||||||
|
it("should join url parts correctly", function()
|
||||||
|
assert.equals("http://example.com/path", Utils.url_join("http://example.com", "path"))
|
||||||
|
assert.equals("http://example.com/path", Utils.url_join("http://example.com/", "/path"))
|
||||||
|
assert.equals("http://example.com/path/to", Utils.url_join("http://example.com", "path", "to"))
|
||||||
|
assert.equals("http://example.com/path", Utils.url_join("http://example.com/", "/path/"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should handle empty parts", function()
|
||||||
|
assert.equals("http://example.com", Utils.url_join("http://example.com", ""))
|
||||||
|
assert.equals("http://example.com", Utils.url_join("http://example.com", nil))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("is_type", function()
|
||||||
|
it("should check basic types correctly", function()
|
||||||
|
assert.is_true(Utils.is_type("string", "test"))
|
||||||
|
assert.is_true(Utils.is_type("number", 123))
|
||||||
|
assert.is_true(Utils.is_type("boolean", true))
|
||||||
|
assert.is_true(Utils.is_type("table", {}))
|
||||||
|
assert.is_true(Utils.is_type("function", function() end))
|
||||||
|
assert.is_true(Utils.is_type("nil", nil))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should check list type correctly", function()
|
||||||
|
assert.is_true(Utils.is_type("list", { 1, 2, 3 }))
|
||||||
|
assert.is_false(Utils.is_type("list", { a = 1, b = 2 }))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should check map type correctly", function()
|
||||||
|
assert.is_true(Utils.is_type("map", { a = 1, b = 2 }))
|
||||||
|
assert.is_false(Utils.is_type("map", { 1, 2, 3 }))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("get_indentation", function()
|
||||||
|
it("should get correct indentation", function()
|
||||||
|
assert.equals(" ", Utils.get_indentation(" test"))
|
||||||
|
assert.equals("\t", Utils.get_indentation("\ttest"))
|
||||||
|
assert.equals("", Utils.get_indentation("test"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should handle empty or nil input", function()
|
||||||
|
assert.equals("", Utils.get_indentation(""))
|
||||||
|
assert.equals("", Utils.get_indentation(nil))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("remove_indentation", function()
|
||||||
|
it("should remove indentation correctly", function()
|
||||||
|
assert.equals("test", Utils.remove_indentation(" test"))
|
||||||
|
assert.equals("test", Utils.remove_indentation("\ttest"))
|
||||||
|
assert.equals("test", Utils.remove_indentation("test"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should handle empty or nil input", function()
|
||||||
|
assert.equals("", Utils.remove_indentation(""))
|
||||||
|
assert.equals(nil, Utils.remove_indentation(nil))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("is_first_letter_uppercase", function()
|
||||||
|
it("should detect uppercase first letter", function()
|
||||||
|
assert.is_true(Utils.is_first_letter_uppercase("Test"))
|
||||||
|
assert.is_true(Utils.is_first_letter_uppercase("ABC"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should detect lowercase first letter", function()
|
||||||
|
assert.is_false(Utils.is_first_letter_uppercase("test"))
|
||||||
|
assert.is_false(Utils.is_first_letter_uppercase("abc"))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("extract_mentions", function()
|
||||||
|
it("should extract @codebase mention", function()
|
||||||
|
local result = Utils.extract_mentions("test @codebase")
|
||||||
|
assert.equals("test ", result.new_content)
|
||||||
|
assert.is_true(result.enable_project_context)
|
||||||
|
assert.is_false(result.enable_diagnostics)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should extract @diagnostics mention", function()
|
||||||
|
local result = Utils.extract_mentions("test @diagnostics")
|
||||||
|
assert.equals("test @diagnostics", result.new_content)
|
||||||
|
assert.is_false(result.enable_project_context)
|
||||||
|
assert.is_true(result.enable_diagnostics)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should handle multiple mentions", function()
|
||||||
|
local result = Utils.extract_mentions("test @codebase @diagnostics")
|
||||||
|
assert.equals("test @diagnostics", result.new_content)
|
||||||
|
assert.is_true(result.enable_project_context)
|
||||||
|
assert.is_true(result.enable_diagnostics)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("get_mentions", function()
|
||||||
|
it("should return valid mentions", function()
|
||||||
|
local mentions = Utils.get_mentions()
|
||||||
|
assert.equals("codebase", mentions[1].command)
|
||||||
|
assert.equals("diagnostics", mentions[2].command)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
Loading…
x
Reference in New Issue
Block a user