-- SPDX-FileCopyrightText: © 2022—2026 Vladimir Zorin -- SPDX-License-Identifier: LicenseRef-OWL-1.0-or-later -- Licensed under OWL v1.0+. See LICENSE. local testimony = require("testimony") local fixture = require("tests.fixture") local repos = require("lilgit.repos") local testify = testimony.new("== lilgit.repos ==") if not fixture.has_git() then print("git binary not available, skipping repos_spec") testify:conclude() return end local fx = fixture.make() assert(fx, "failed to build git fixture") -- Both object layouts: loose (worktree) and packed after gc (bare). local dirs = { worktree = fx.git_dir, bare = fx.bare_dir } for label, git_dir in pairs(dirs) do testify:that("resolve_ref: branch name (" .. label .. ")", function() local sha, is_sha = repos.resolve_ref(git_dir, fx.branch) testimony.assert_equal(fx.head, sha) testimony.assert_false(is_sha) end) testify:that("resolve_ref: slashed branch (" .. label .. ")", function() testimony.assert_equal(fx.feature, (repos.resolve_ref(git_dir, "feature/foo"))) end) testify:that("resolve_ref: annotated tag peels to commit (" .. label .. ")", function() testimony.assert_equal(fx.tag_commit, (repos.resolve_ref(git_dir, "v1.0"))) end) testify:that("resolve_ref: full refs path (" .. label .. ")", function() testimony.assert_equal(fx.head, (repos.resolve_ref(git_dir, "refs/heads/" .. fx.branch))) end) testify:that("resolve_ref: HEAD (" .. label .. ")", function() testimony.assert_equal(fx.head, (repos.resolve_ref(git_dir, "HEAD"))) end) testify:that("resolve_ref: 40-hex commit sha (" .. label .. ")", function() local sha, is_sha = repos.resolve_ref(git_dir, fx.head) testimony.assert_equal(fx.head, sha) testimony.assert_true(is_sha) end) testify:that("resolve_ref: 40-hex tag sha peels to commit (" .. label .. ")", function() local sha, is_sha = repos.resolve_ref(git_dir, fx.tag) testimony.assert_equal(fx.tag_commit, sha) testimony.assert_true(is_sha) end) end testify:that("resolve_ref: rejects garbage", function() testimony.assert_nil(repos.resolve_ref(fx.git_dir, "no-such-ref")) testimony.assert_nil(repos.resolve_ref(fx.git_dir, fx.head:sub(1, 39))) testimony.assert_nil(repos.resolve_ref(fx.git_dir, "")) testimony.assert_nil(repos.resolve_ref(fx.git_dir, "a\0b")) end) testify:that("resolve_ref: sha naming a blob is not browsable", function() testimony.assert_nil(repos.resolve_ref(fx.git_dir, fx.readme_blob)) end) testify:that("walk: empty path yields the root tree", function() local node = repos.walk(fx.git_dir, fx.head, {}) testimony.assert_equal("tree", node.kind) end) testify:that("walk: nested blob", function() local node = repos.walk(fx.git_dir, fx.head, { "src", "main.lua" }) testimony.assert_equal("blob", node.kind) end) testify:that("walk: subdirectory tree", function() local node = repos.walk(fx.git_dir, fx.head, { "assets" }) testimony.assert_equal("tree", node.kind) end) testify:that("walk: symlink", function() local node = repos.walk(fx.git_dir, fx.head, { "link.md" }) testimony.assert_equal("symlink", node.kind) testimony.assert_equal("README.md", repos.read_blob(fx.git_dir, node.sha)) end) testify:that("walk: rejects traversal and bad segments", function() testimony.assert_nil((repos.walk(fx.git_dir, fx.head, { ".." }))) testimony.assert_nil((repos.walk(fx.git_dir, fx.head, { "." }))) testimony.assert_nil((repos.walk(fx.git_dir, fx.head, { "" }))) testimony.assert_nil((repos.walk(fx.git_dir, fx.head, { "a\0b" }))) testimony.assert_nil((repos.walk(fx.git_dir, fx.head, { "no-such-file" }))) testimony.assert_nil((repos.walk(fx.git_dir, fx.head, { "README.md", "deeper" }))) end) testify:that("list_tree: trees first, alphabetical within groups", function() local root = repos.walk(fx.git_dir, fx.head, {}) local listing = repos.list_tree(fx.git_dir, root.sha) local names = {} for _, entry in ipairs(listing) do names[#names + 1] = entry.name end testimony.assert_equal({ "assets", "docs", "src", "README.md", "link.md", "plain.xyz" }, names) testimony.assert_equal("symlink", listing[5].kind) end) testify:that("find_readme: prefers README.md, honors priority order", function() local root = repos.walk(fx.git_dir, fx.head, {}) local listing = repos.list_tree(fx.git_dir, root.sha) testimony.assert_equal("README.md", repos.find_readme(listing).name) local fake = { { name = "readme", kind = "blob" }, { name = "README.dj", kind = "blob" }, { name = "README.md", kind = "tree" }, } testimony.assert_equal("README.dj", repos.find_readme(fake).name) testimony.assert_nil(repos.find_readme({ { name = "other.txt", kind = "blob" } })) end) testify:that("discover: finds both repos, resolves metadata", function() local registry = repos.discover({ repos_dir = fx.root, repos = {} }, nil) testimony.assert_not_nil(registry.by_name.fix) testimony.assert_not_nil(registry.by_name.bare) testimony.assert_equal({ "bare", "fix" }, { registry.ordered[1].name, registry.ordered[2].name }) testimony.assert_equal(fx.branch, registry.by_name.fix.default_branch) testimony.assert_equal(fx.git_dir, registry.by_name.fix.git_dir) testimony.assert_equal(fx.bare_dir, registry.by_name.bare.git_dir) testimony.assert_equal(fx.dir, registry.prefix_map["/fix"]) -- Stock "Unnamed repository" boilerplate is not a description. testimony.assert_nil(registry.by_name.bare.description) end) testify:that("discover: explicit config entry wins on name collision", function() local registry = repos.discover({ repos_dir = fx.root, repos = { { name = "fix", path = fx.bare_dir, description = "explicit", default_branch = "custom" } }, }, nil) testimony.assert_equal("explicit", registry.by_name.fix.description) testimony.assert_equal("custom", registry.by_name.fix.default_branch) testimony.assert_equal(fx.bare_dir, registry.by_name.fix.git_dir) end) fixture.destroy(fx) testify:conclude()