feat(launcher-test): cover frontmatter + markdown parsers
Extends run_tests from 12 -> ~46 asserts: 10 frontmatter cases (simple field, no-frontmatter passthrough, numeric field, multi-line body, non-string input, empty meta) and ~25 markdown cases (headers, bullets, ordered, images, bold/italic/code spans, paragraph joining, mixed mid-block). frontmatter.lua and markdown.lua are byte-identical copies of the launcher's helpers; smoke.sh enforces no drift via git hash-object.
This commit is contained in:
117
init.lua
117
init.lua
@@ -1,34 +1,119 @@
|
||||
-- lib-management.launcher-test — P.3.4
|
||||
-- 11 assertions: 5 FSM transitions + 1 FSM stability + 5 Engine-Surface existence.
|
||||
-- (measure_text runtime-behavior check dropped — needs Raylib font, not loaded in headless test-mode.)
|
||||
-- Pattern follows P.3.1 Test-Module-Pattern; TAP output via engine.test.*
|
||||
|
||||
-- FSM-Source: byte-identical Kopie aus modules/lib-management.launcher/fsm.lua.
|
||||
-- Sporel-Resolver lehnt module-als-dep ab (nur lib-deps werden resolved),
|
||||
-- daher hat dieses Test-Modul seine eigene fsm.lua. Drift wird via Smoke-
|
||||
-- diff-check verhindert (siehe smoke.sh P0-S15-pre).
|
||||
local M = {}
|
||||
|
||||
local fsm = dofile(engine.module.dir_of("lib-management.launcher-test") .. "/fsm.lua")
|
||||
local DIR = engine.module.dir_of("lib-management.launcher-test")
|
||||
local fsm = dofile(DIR .. "/fsm.lua")
|
||||
|
||||
-- Inject a tiny JSON decoder for the headless test context — depf is not
|
||||
-- a lib-dep of this test-lib. Same shape contract as
|
||||
-- lib-management.dep-fetcher._json_decode: returns (tbl, err).
|
||||
_G.LAUNCHER_FRONTMATTER_DECODER = function(json_str)
|
||||
-- Minimal flat-object decoder: supports {"key":"value", "n":42}.
|
||||
-- Tests only feed it shallow JSON.
|
||||
local t = {}
|
||||
for k, v in json_str:gmatch('"([%w_]+)"%s*:%s*"([^"]*)"') do
|
||||
t[k] = v
|
||||
end
|
||||
for k, n in json_str:gmatch('"([%w_]+)"%s*:%s*(%-?%d+%.?%d*)') do
|
||||
t[k] = tonumber(n)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local fm = dofile(DIR .. "/frontmatter.lua")
|
||||
local md = dofile(DIR .. "/markdown.lua")
|
||||
|
||||
function M.run_tests(ctx)
|
||||
-- FSM transitions (5)
|
||||
-- ----- FSM transitions (unchanged) -----
|
||||
engine.test.equals(fsm.next(fsm.STATE_LIST, "esc"), fsm.STATE_QUIT_CONFIRM, "list+esc -> quit_confirm")
|
||||
engine.test.equals(fsm.next(fsm.STATE_QUIT_CONFIRM, "esc"), fsm.STATE_LIST, "quit_confirm+esc -> list")
|
||||
engine.test.equals(fsm.next(fsm.STATE_QUIT_CONFIRM, "enter"), fsm.EXIT, "quit_confirm+enter -> exit")
|
||||
engine.test.equals(fsm.next(fsm.STATE_QUIT_CONFIRM, "click_yes"), fsm.EXIT, "quit_confirm+click_yes -> exit")
|
||||
engine.test.equals(fsm.next(fsm.STATE_QUIT_CONFIRM, "click_no"), fsm.STATE_LIST, "quit_confirm+click_no -> list")
|
||||
engine.test.equals(fsm.next(fsm.STATE_LIST, "click_module"), fsm.STATE_LIST, "list+click_module -> list (switch is side-effect)")
|
||||
engine.test.equals(fsm.next("garbage_state", "esc"), "garbage_state", "unknown state passes through unchanged")
|
||||
|
||||
-- FSM stability (2)
|
||||
engine.test.equals(fsm.next(fsm.STATE_LIST, "click_module"), fsm.STATE_LIST, "list+click_module -> list (switch is side-effect)")
|
||||
engine.test.equals(fsm.next("garbage_state", "esc"), "garbage_state", "unknown state passes through unchanged")
|
||||
|
||||
-- Engine-surface existence (6)
|
||||
-- ----- Engine-surface existence (unchanged) -----
|
||||
engine.test.assert(type(engine.module.list) == "function", "engine.module.list is function")
|
||||
engine.test.assert(type(engine.module.dir_of) == "function", "engine.module.dir_of is function")
|
||||
engine.test.assert(type(engine.render.draw_text) == "function", "engine.render.draw_text is function")
|
||||
engine.test.assert(type(engine.render.measure_text) == "function", "engine.render.measure_text is function")
|
||||
engine.test.assert(type(engine.module.list()) == "table", "engine.module.list() returns table")
|
||||
|
||||
-- ----- Frontmatter (10) -----
|
||||
local meta, body = fm.parse('---\n"name":"Test"\n---\nbody text\n')
|
||||
engine.test.equals(meta.name, "Test", "fm: simple name field")
|
||||
engine.test.equals(body, "body text", "fm: body after separator")
|
||||
meta, body = fm.parse("no frontmatter here")
|
||||
engine.test.equals(next(meta), nil, "fm: empty meta when no frontmatter")
|
||||
engine.test.equals(body, "no frontmatter here", "fm: full content as body")
|
||||
meta, body = fm.parse('---\n"n":42\n---\n\nLine 1\nLine 2\n')
|
||||
engine.test.equals(meta.n, 42, "fm: numeric field")
|
||||
engine.test.equals(body:find("Line 1") ~= nil, true, "fm: body multi-line")
|
||||
local m2, b2, err = fm.parse(42)
|
||||
engine.test.equals(m2, nil, "fm: non-string returns nil")
|
||||
engine.test.assert(err and err:find("string"), "fm: non-string error mentions 'string'")
|
||||
meta, body = fm.parse('---\n---\nempty meta\n')
|
||||
engine.test.equals(next(meta), nil, "fm: explicit empty meta")
|
||||
engine.test.equals(body, "empty meta", "fm: body after empty meta")
|
||||
|
||||
-- ----- Markdown parse (25) -----
|
||||
local blocks = md.parse("# Header\n\nparagraph text\n")
|
||||
engine.test.equals(#blocks, 2, "md: header + paragraph -> 2 blocks")
|
||||
engine.test.equals(blocks[1].type, "h1", "md: '#' -> h1")
|
||||
engine.test.equals(blocks[1].text, "Header", "md: h1 text stripped")
|
||||
engine.test.equals(blocks[2].type, "p", "md: paragraph block-type")
|
||||
|
||||
blocks = md.parse("## H2\n### H3\n")
|
||||
engine.test.equals(blocks[1].type, "h2", "md: '##' -> h2")
|
||||
engine.test.equals(blocks[2].type, "h3", "md: '###' -> h3")
|
||||
|
||||
blocks = md.parse("- item one\n- item two\n")
|
||||
engine.test.equals(#blocks, 2, "md: two bullet items")
|
||||
engine.test.equals(blocks[1].type, "bullet", "md: '-' -> bullet")
|
||||
|
||||
blocks = md.parse("1. first\n2. second\n")
|
||||
engine.test.equals(blocks[1].type, "ordered", "md: '1.' -> ordered")
|
||||
engine.test.equals(blocks[1].index, 1, "md: ordered index parsed")
|
||||
engine.test.equals(blocks[2].index, 2, "md: ordered index increments")
|
||||
|
||||
blocks = md.parse("\n")
|
||||
engine.test.equals(blocks[1].type, "img", "md: '!\\[\\]\\(\\)' -> img")
|
||||
engine.test.equals(blocks[1].alt, "alt text", "md: img alt extracted")
|
||||
engine.test.equals(blocks[1].src, "teaser.png", "md: img src extracted")
|
||||
|
||||
blocks = md.parse("plain **bold** word\n")
|
||||
engine.test.equals(blocks[1].type, "p", "md: bold-in-paragraph stays p")
|
||||
local has_bold = false
|
||||
for _, s in ipairs(blocks[1].spans) do if s.kind == "bold" then has_bold = true end end
|
||||
engine.test.assert(has_bold, "md: bold span recognised")
|
||||
|
||||
blocks = md.parse("plain *italic* word\n")
|
||||
local has_italic = false
|
||||
for _, s in ipairs(blocks[1].spans) do if s.kind == "italic" then has_italic = true end end
|
||||
engine.test.assert(has_italic, "md: italic span recognised")
|
||||
|
||||
blocks = md.parse("inline `code` here\n")
|
||||
local has_code = false
|
||||
for _, s in ipairs(blocks[1].spans) do if s.kind == "code" then has_code = true end end
|
||||
engine.test.assert(has_code, "md: code span recognised")
|
||||
|
||||
blocks = md.parse("line one\nline two\n\nsecond para\n")
|
||||
engine.test.equals(#blocks, 2, "md: blank line breaks paragraph")
|
||||
engine.test.equals(blocks[1].spans[1].text:find("line one") ~= nil, true,
|
||||
"md: paragraph 1 joins lines")
|
||||
engine.test.equals(blocks[2].spans[1].text:find("second para") ~= nil, true,
|
||||
"md: paragraph 2 separated")
|
||||
|
||||
blocks = md.parse("")
|
||||
engine.test.equals(#blocks, 0, "md: empty body -> no blocks")
|
||||
blocks = md.parse(nil)
|
||||
engine.test.equals(#blocks, 0, "md: nil body -> no blocks")
|
||||
|
||||
blocks = md.parse("# A\n\n- one\n- two\n\nparagraph\n")
|
||||
engine.test.equals(blocks[1].type, "h1", "md: mixed h1+bullets+p: h1 first")
|
||||
engine.test.equals(blocks[2].type, "bullet", "md: mixed: bullet second")
|
||||
engine.test.equals(blocks[3].type, "bullet", "md: mixed: bullet third")
|
||||
engine.test.equals(blocks[4].type, "p", "md: mixed: p fourth")
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
Reference in New Issue
Block a user