lib-core.git-test v0.1.0: smoke test for the libgit2 wrapper

Exercises the sync API (clone, is_repo, describe, tags) against a
public Gitea repo over HTTPS, plus the async ls_remote_tags handshake
through the engine async-pool. Designed for SPOREL_CI runs: kicks off
the async work in init(), polls in update(), calls engine.exit(0)
once the result has been taken.
This commit is contained in:
Axel Meyer
2026-05-31 01:15:40 +02:00
commit 177c2bda45
3 changed files with 129 additions and 0 deletions

112
init.lua Normal file
View File

@@ -0,0 +1,112 @@
-- lib-core.git-test/init.lua
--
-- Smoke test for lib-core.git v0.1.0. Exercises the synchronous API
-- (is_repo, clone, describe, tags) against a real Gitea repo over
-- HTTPS, plus the asynchronous start_ls_remote_tags handshake through
-- the engine async-pool.
--
-- Designed to run under SPOREL_CI=1 (calls engine.exit on completion).
-- Interactive run just sits on the polling loop and renders nothing.
local git = require("lib-core.git")
-- libgit2 sees this as C:\tmp\... on Windows (Win32 path-translation,
-- NOT MSYS2's /tmp -> /c/msys64/tmp). For cross-platform smoke runs we
-- choose a path that exists in the engine's CWD if possible; falling
-- back to the platform tmp is fine because the test runs in CI mode and
-- cleans up after itself.
local CLONE_DIR = (os.getenv("TEMP") or "/tmp") .. "/sporel-git-smoke-clone"
-- Slash normalization for libgit2: it accepts forward slashes on both
-- platforms.
CLONE_DIR = CLONE_DIR:gsub("\\", "/")
-- Public Gitea repo used as the smoke-test target. sporel-meta and
-- sporel-engine are private (require token auth which lib-core.git
-- v0.1.0 does not yet plumb through), but sporel-lib-core.maps is
-- public and conveniently small.
local TARGET_URL = "https://git.davoryn.de/sporel/sporel-lib-core.maps.git"
-- Cross-platform recursive rmdir via the platform shell. os.execute on
-- Windows invokes cmd.exe (NOT MSYS2-bash) so we branch on path-sep.
local function rmrf(path)
local is_win = package.config:sub(1, 1) == "\\"
if is_win then
local winpath = path:gsub("/", "\\")
os.execute('if exist "' .. winpath .. '" rmdir /S /Q "' .. winpath .. '"')
else
os.execute("rm -rf '" .. path .. "'")
end
end
-- Module-state for the async leg, populated in init() and polled in update().
local async_handle = nil
local async_done = false
local frames = 0
function init(ctx)
-- Clean state from any previous run. (Done in init() rather than at
-- chunk-load so a hot-reload during dev cycles gets a fresh dir.)
rmrf(CLONE_DIR)
-- --- Synchronous probes -------------------------------------------------
-- is_repo of a non-existent path: must return false. (Avoid using /tmp
-- because Windows may have a .git somewhere upstream of it.)
assert(git.is_repo(CLONE_DIR .. "-no-such-subdir") == false,
"is_repo should reject a non-existent dir")
engine.print("git-test: cloning " .. TARGET_URL)
local ok, err = git.clone(TARGET_URL, CLONE_DIR, "master")
assert(ok, "clone failed: " .. tostring(err))
assert(git.is_repo(CLONE_DIR), "cloned dir should be a repo")
engine.print("git-test: clone OK")
local desc, derr = git.describe(CLONE_DIR)
-- describe can legitimately fail on a repo with no annotated tags;
-- treat nil as soft-fail with a marker, not a hard assertion.
if desc then
engine.print("git-test: describe: " .. desc)
else
engine.print("git-test: describe (no tag yet): " .. tostring(derr))
end
local tags, terr = git.tags(CLONE_DIR)
assert(tags ~= nil, "tags failed: " .. tostring(terr))
engine.print("git-test: local tag count = " .. #tags)
-- --- Kick off async ls-remote ------------------------------------------
async_handle = git.start_ls_remote_tags(TARGET_URL)
engine.print("git-test: async ls_remote_tags dispatched")
end
function update(ctx, dt)
frames = frames + 1
if async_handle and not async_done then
if git.is_done(async_handle) then
local result = git.take_result(async_handle)
async_handle = nil
async_done = true
if result.tags then
engine.print("git-test: async tag count = " .. #result.tags)
else
engine.print("git-test: async ERROR = "
.. tostring(result.error))
end
end
end
-- CI completion: drop out once async finished (or after a generous
-- bound — network hiccup safety).
if os.getenv("SPOREL_CI") == "1" then
if async_done then
engine.print("git-test: PASS (CI)")
engine.exit(0)
elseif frames > 1800 then -- ~30s at 60fps
engine.print("git-test: TIMEOUT after " .. frames .. " frames")
engine.exit(1)
end
end
end
function render()
-- No visuals; this is a CI smoke. Interactive runs see a blank window.
end

6
manifest.core Normal file
View File

@@ -0,0 +1,6 @@
{
"id": "lib-core.git-test.core",
"kind": "lib",
"version": "0.1.0",
"license": "Proprietary"
}

11
manifest.module Normal file
View File

@@ -0,0 +1,11 @@
{
"id": "lib-core.git-test",
"version": "0.1.0",
"kind": "module",
"api": "^0.1",
"ci_frames": 1800,
"entry_point": "init.lua",
"deps": [
{"id": "lib-core.git", "version": "0.1.0"}
]
}