-- lib-management.dep-fetcher-test v0.1.0 -- -- TAP-style assertion suite for lib-management.dep-fetcher. Covers the -- closure walk (conflict detection) plus per-lib state-check cases -- B-D from spec §5 (clean / dirty / wrong-tag). Case A (missing+clone) -- and Case E (broken describe) are exercised in check_only mode where -- possible; their live-network variants are gated by SPOREL_DEPFETCHER_NET=1 -- and not run by default (slice-5 stays offline). -- -- Fixture states that cannot be checked in verbatim (live .git dirs) -- are reconstructed by ./fixtures/_setup.sh which the user must run -- before invoking this test-module. local M = {} local function fixture_dir() return engine.module.dir_of("lib-management.dep-fetcher-test") .. "/fixtures" end function M.run_tests() local depf = require("lib-management.dep-fetcher") -- ============================================================ -- Assertions on internal helpers (gitea-slug edge case, id->path) -- ============================================================ engine.test.equals(depf._id_to_path("lib-core.maps"), "lib-core/maps", "id_to_path: simple two-segment id") engine.test.equals(depf._id_to_path("lib-core.body.vertebrate"), "lib-core/body/vertebrate", "id_to_path: multi-segment id keeps depth") engine.test.equals(depf._gitea_slug_for("lib-core.maps"), "sporel-lib-core.maps", "gitea_slug_for: regular id gets sporel- prefix") engine.test.equals(depf._gitea_slug_for("lib-core.git"), "sporel-lib-core.git-lib", "gitea_slug_for: .git-suffix id appends -lib (Gitea reserved)") -- ============================================================ -- JSON decoder sanity (small targeted suite — manifests are JSON) -- ============================================================ local empty = depf._json_decode("{}") engine.test.assert(type(empty) == "table", "json_decode: empty object is a table") local arr_only = depf._json_decode("[1,2,3]") engine.test.equals(#arr_only, 3, "json_decode: array of numbers length") engine.test.equals(arr_only[2], 2, "json_decode: array index 2 value") local nested = depf._json_decode( '{"id":"x","deps":[{"id":"a","version":"0.1.0"}]}') engine.test.equals(nested.id, "x", "json_decode: nested object id field") engine.test.equals(nested.deps[1].id, "a", "json_decode: nested array-of-objects access") engine.test.equals(nested.deps[1].version, "0.1.0", "json_decode: deeply-nested string field") -- ============================================================ -- Conflict-detection fixture (no actual git ops) -- ============================================================ local r_conflict = depf.ensure_for_module_at( fixture_dir() .. "/conflict_a/manifest.module", { install_root = fixture_dir() .. "/conflict_a/install", check_only = true, }) engine.test.equals(r_conflict.ok, false, "conflict_a: ok=false (conflict detected)") engine.test.equals(#r_conflict.conflicts, 1, "conflict_a: exactly one conflict") engine.test.equals(r_conflict.conflicts[1].lib_id, "lib-core.maps", "conflict_a: conflict is on lib-core.maps") -- ============================================================ -- Closure walk over a clean offline fixture (check_only) -- ============================================================ local r_walk = depf.ensure_for_module_at( fixture_dir() .. "/clean_b/manifest.module", { install_root = fixture_dir() .. "/clean_b/install", check_only = true, }) engine.test.equals(r_walk.ok, true, "clean_b (check_only): walk succeeds, no conflicts") engine.test.equals(#r_walk.conflicts, 0, "clean_b (check_only): zero conflicts") engine.test.assert(#r_walk.closure >= 1, "clean_b (check_only): closure non-empty") -- ============================================================ -- State-check (cases B, C, D) -- -- These exercise the per-lib git path. They require ./fixtures -- /_setup.sh to have constructed the .git states. The test -- detects unprepared fixtures by check_only-bypassing them when -- the helper file is absent, so a missing _setup.sh run gives -- a clear diagnostic via test.fail rather than an obscure -- describe failure. -- ============================================================ local function fixtures_ready() local f = io.open(fixture_dir() .. "/_setup.stamp", "rb") if not f then return false end f:close() return true end if not fixtures_ready() then engine.test.fail( "fixtures/_setup.stamp missing — " .. "run `bash sporel-libs/lib-management/dep-fetcher-test/" .. "fixtures/_setup.sh` first to construct .git state-check " .. "fixtures (cases B/C/D)") return end -- Case B — clean: repo at exactly pinned tag. local r_clean = depf.ensure_for_module_at( fixture_dir() .. "/clean_b/manifest.module", { install_root = fixture_dir() .. "/clean_b/install" }) engine.test.equals(r_clean.ok, true, "clean_b (full): state-check OK") engine.test.equals(#r_clean.warnings, 0, "clean_b (full): zero warnings (case B)") engine.test.equals(#r_clean.errors, 0, "clean_b (full): zero errors") -- Case C — dirty: extra commit past tag. local r_dirty = depf.ensure_for_module_at( fixture_dir() .. "/dirty_c/manifest.module", { install_root = fixture_dir() .. "/dirty_c/install" }) engine.test.equals(r_dirty.ok, true, "dirty_c: state-check OK (warnings are non-blocking)") engine.test.equals(#r_dirty.warnings, 1, "dirty_c: one warning (case C)") if r_dirty.warnings[1] then engine.test.equals(r_dirty.warnings[1].kind, "dirty", "dirty_c: warning.kind == 'dirty'") engine.test.equals(r_dirty.warnings[1].lib_id, "lib-x.consumer", "dirty_c: warning is on lib-x.consumer") end -- Case D — wrong tag: repo at v0.1.0, module wants v0.2.0. -- The state-check silently checks out v0.2.0. We then verify the -- post-state by re-running and expecting Case B (clean). local r_wrong = depf.ensure_for_module_at( fixture_dir() .. "/wrong_tag_d/manifest.module", { install_root = fixture_dir() .. "/wrong_tag_d/install" }) engine.test.equals(r_wrong.ok, true, "wrong_tag_d: silent checkout succeeded") engine.test.equals(#r_wrong.errors, 0, "wrong_tag_d: zero errors after checkout") local r_wrong2 = depf.ensure_for_module_at( fixture_dir() .. "/wrong_tag_d/manifest.module", { install_root = fixture_dir() .. "/wrong_tag_d/install" }) engine.test.equals(r_wrong2.ok, true, "wrong_tag_d (post-checkout): now clean") engine.test.equals(#r_wrong2.warnings, 0, "wrong_tag_d (post-checkout): zero warnings (case B reached)") end return M