test(git): cover repo caching and :G completion dispatch

This commit is contained in:
2026-05-07 16:47:22 +02:00
parent c07e9c8de3
commit 50b05b0c81
4 changed files with 308 additions and 50 deletions
+131
View File
@@ -0,0 +1,131 @@
local t = require("test")
local helpers = require("test.git.helpers")
require("git").init()
local git = helpers.git
local make_repo = helpers.make_repo
---@param r ow.Git.Repo
---@param key string
---@param timeout integer?
local function wait_cleared(r, key, timeout)
vim.wait(timeout or 2000, function()
return r._cache[key] == nil
end)
end
t.test("list_refs returns heads, tags, remotes (no HEAD)", function()
local dir = make_repo({ a = "x" })
git(dir, "branch", "feature")
git(dir, "tag", "v1")
local r = assert(require("git.repo").resolve(dir))
local refs = r:list_refs()
table.sort(refs)
t.eq(refs, { "feature", "main", "v1" })
end)
t.test("list_pseudo_refs always includes HEAD", function()
local dir = make_repo({ a = "x" })
local r = assert(require("git.repo").resolve(dir))
t.eq(r:list_pseudo_refs(), { "HEAD" })
end)
t.test("list_pseudo_refs picks up MERGE_HEAD when present", function()
local dir = make_repo({ a = "x" })
local r = assert(require("git.repo").resolve(dir))
t.write(dir .. "/.git", "MERGE_HEAD", "deadbeef\n")
-- Bypass cache (file appeared after first scan).
r._cache = {}
local refs = r:list_pseudo_refs()
table.sort(refs)
t.eq(refs, { "HEAD", "MERGE_HEAD" })
end)
t.test("list_stash_refs is empty when no stash", function()
local dir = make_repo({ a = "x" })
local r = assert(require("git.repo").resolve(dir))
t.eq(r:list_stash_refs(), {})
end)
t.test("list_stash_refs lists stash + entries when stash exists", function()
local dir = make_repo({ a = "x" })
t.write(dir, "a", "modified")
git(dir, "stash")
local r = assert(require("git.repo").resolve(dir))
local refs = r:list_stash_refs()
t.eq(#refs, 2)
t.eq(refs[1], "stash")
t.eq(refs[2], "stash@{0}")
end)
t.test("get_cached memoizes by key", function()
local dir = make_repo({ a = "x" })
local r = assert(require("git.repo").resolve(dir))
local calls = 0
local v1 = r:get_cached("k", function()
calls = calls + 1
return { "first" }
end)
local v2 = r:get_cached("k", function()
calls = calls + 1
return { "second" }
end)
t.eq(calls, 1)
t.truthy(v1 == v2, "second call should return cached table")
end)
t.test("cache clears after top-level .git change (commit)", function()
local dir = make_repo({ a = "x" })
local r = assert(require("git.repo").resolve(dir))
local _ = r:list_refs()
t.truthy(r._cache.refs)
t.write(dir, "b", "y")
git(dir, "add", "b")
git(dir, "commit", "-q", "-m", "two")
wait_cleared(r, "refs")
t.falsy(r._cache.refs, "cache should be cleared after commit")
end)
t.test("cache clears after slash-branch creation (polyfill)", function()
local dir = make_repo({ a = "x" })
local r = assert(require("git.repo").resolve(dir))
local _ = r:list_refs()
t.truthy(r._cache.refs)
git(dir, "branch", "feat/foo")
wait_cleared(r, "refs")
t.falsy(r._cache.refs, "cache should clear via polyfilled subdir watcher")
local refs = r:list_refs()
table.sort(refs)
t.eq(refs, { "feat/foo", "main" })
end)
t.test("cache clears after deeply nested slash branch", function()
local dir = make_repo({ a = "x" })
local r = assert(require("git.repo").resolve(dir))
local _ = r:list_refs()
git(dir, "branch", "deep/a/b/c")
wait_cleared(r, "refs")
local refs = r:list_refs()
table.sort(refs)
t.eq(refs, { "deep/a/b/c", "main" })
end)
t.test("watcher cleans up after a slash-branch dir is removed", function()
local dir = make_repo({ a = "x" })
local r = assert(require("git.repo").resolve(dir))
git(dir, "branch", "feat/foo")
-- Wait for the dynamic watcher on .git/refs/heads/feat to be added.
local feat_path = dir .. "/.git/refs/heads/feat"
vim.wait(2000, function()
return r._watchers[feat_path] ~= nil
end)
t.truthy(r._watchers[feat_path], "feat/ subdir should be watched")
-- Remove the branch; the feat/ directory becomes empty and is
-- pruned by git, triggering the deleted-self event.
git(dir, "branch", "-D", "feat/foo")
vim.wait(2000, function()
return r._watchers[feat_path] == nil
end)
t.falsy(r._watchers[feat_path], "watcher should self-close")
end)