---@diagnostic disable: access-invisible local h = require("test.git.helpers") local t = require("test") ---@param r ow.Git.Repo ---@param key string ---@param timeout integer? local function wait_cleared(r, key, timeout) t.wait_for(function() return r._cache[key] == nil end, key .. " cache to clear", timeout or 2000) end t.test("list_refs returns heads, tags, remotes (no HEAD)", function() local dir = h.make_repo({ a = "x" }) h.git(dir, "branch", "feature") h.git(dir, "tag", "v1") local r = assert(require("git.core.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 = h.make_repo({ a = "x" }) local r = assert(require("git.core.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 = h.make_repo({ a = "x" }) local r = assert(require("git.core.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 = h.make_repo({ a = "x" }) local r = assert(require("git.core.repo").resolve(dir)) t.eq(r:list_stash_refs(), {}) end) t.test("list_stash_refs lists stash + entries when stash exists", function() local dir = h.make_repo({ a = "x" }) t.write(dir, "a", "modified") h.git(dir, "stash") local r = assert(require("git.core.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 = h.make_repo({ a = "x" }) local r = assert(require("git.core.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 = h.make_repo({ a = "x" }) local r = assert(require("git.core.repo").resolve(dir)) local _ = r:list_refs() t.truthy(r._cache.refs) t.write(dir, "b", "y") h.git(dir, "add", "b") h.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 = h.make_repo({ a = "x" }) local r = assert(require("git.core.repo").resolve(dir)) local _ = r:list_refs() t.truthy(r._cache.refs) h.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 = h.make_repo({ a = "x" }) local r = assert(require("git.core.repo").resolve(dir)) local _ = r:list_refs() h.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("resolve_sha returns ok + full sha for a known blob", function() local dir = h.make_repo({ a = "hello\n" }) local r = assert(require("git.core.repo").resolve(dir)) local blob = h.git(dir, "rev-parse", "HEAD:a").stdout local short = blob:sub(1, 7) local full, status = r:resolve_sha(short) t.eq(status, "ok") t.eq(full, blob) end) t.test("resolve_sha returns missing for an unknown prefix", function() local dir = h.make_repo({ a = "x" }) local r = assert(require("git.core.repo").resolve(dir)) local full, status = r:resolve_sha("0000deadbeef") t.eq(full, nil) t.eq(status, "missing") end) t.test("resolve_sha caches by prefix", function() local dir = h.make_repo({ a = "x" }) local r = assert(require("git.core.repo").resolve(dir)) local blob = h.git(dir, "rev-parse", "HEAD:a").stdout local short = blob:sub(1, 7) local _, _ = r:resolve_sha(short) t.truthy(r._cache["resolve:" .. short], "result should be cached") end) t.test("watcher cleans up after a slash-branch dir is removed", function() local dir = h.make_repo({ a = "x" }) local r = assert(require("git.core.repo").resolve(dir)) h.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" t.wait_for(function() return r._watchers[feat_path] ~= nil end, "watcher to be installed on feat/ subdir", 2000) 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. h.git(dir, "branch", "-D", "feat/foo") t.wait_for(function() return r._watchers[feat_path] == nil end, "watcher on feat/ subdir to close", 2000) t.falsy(r._watchers[feat_path], "watcher should self-close") end)