-- lib-core.composition-test — Phase A.1 -- 14 assertions: 4 define_template + 4 create + 3 list_by_* + 2 destroy + 1 list_templates. -- Pattern follows P.2.4 Test-Module-Pattern; TAP output via engine.test.* local composition = require("lib-core.composition") local T = engine.test local M = {} function M.run_tests(ctx) -- ---------------------------------------------------------------- -- define_template (4) -- ---------------------------------------------------------------- -- Happy path: define + retrieve via list_templates composition.define_template{ id = "sign", properties = { sprite_path = "sprites/sign.png", text = "default text", position = {x = 0, y = 0}, }, tags = {"renderable"}, } local templates_after_define = composition.list_templates() local found_sign = false for _, id in ipairs(templates_after_define) do if id == "sign" then found_sign = true; break end end T.assert(found_sign, "define_template 'sign' registers in list_templates") -- Loud-error on missing id local ok_missing = pcall(composition.define_template, { properties = {} }) T.assert(not ok_missing, "define_template loud-errors on missing id") -- Loud-error on duplicate id (sign already defined above) local ok_dup = pcall(composition.define_template, { id = "sign", properties = {} }) T.assert(not ok_dup, "define_template loud-errors on duplicate id") -- Loud-error on deferred slots-block (Phase D) local ok_slots = pcall(composition.define_template, { id = "sword", slots = { blade = {} }, }) T.assert(not ok_slots, "define_template loud-errors on 'slots' block (Phase D trigger)") -- ---------------------------------------------------------------- -- create (4) -- ---------------------------------------------------------------- -- Happy path: create returns a non-nil entity that carries template -- properties + per-instance overrides local sign1 = composition.create{ template = "sign", properties = { position = {x = 144, y = 200}, text = "Hello", }, } T.assert(sign1 ~= nil, "create returns a non-nil entity handle") T.equals(sign1:get_property("text"), "Hello", "create override sets text-property") T.equals(sign1:get_property("position.x"), 144, "create flattens nested position into position.x") -- Template-default-property survives when override doesn't touch it T.equals(sign1:get_property("sprite_path"), "sprites/sign.png", "create preserves template default for sprite_path") -- ---------------------------------------------------------------- -- list_by_template + list_by_tag (3) -- ---------------------------------------------------------------- -- Single instance local by_tpl = composition.list_by_template("sign") T.assert(#by_tpl == 1, "list_by_template returns 1 entity after 1 create") -- Tag-Index local by_tag = composition.list_by_tag("renderable") T.assert(#by_tag == 1, "list_by_tag returns 1 entity for 'renderable'") -- Unknown template / tag → empty local empty_tpl = composition.list_by_template("nonexistent") T.assert(#empty_tpl == 0, "list_by_template empty for unknown template") -- ---------------------------------------------------------------- -- destroy (2) -- ---------------------------------------------------------------- -- Create a second instance, then destroy it, indexes shrink local sign2 = composition.create{ template = "sign", properties = { position = {x = 50, y = 50}, text = "T2" }, } T.assert(#composition.list_by_template("sign") == 2, "list_by_template grows to 2 after second create") composition.destroy(sign2) T.assert(#composition.list_by_template("sign") == 1, "destroy shrinks template-index back to 1") -- ---------------------------------------------------------------- -- list_templates (1) -- ---------------------------------------------------------------- -- list_templates is non-empty after define T.assert(#composition.list_templates() >= 1, "list_templates returns at least 1 entry after define_template") end return M