diff --git a/init.lua b/init.lua index df3e62d..488275c 100644 --- a/init.lua +++ b/init.lua @@ -72,6 +72,20 @@ local function setup_container(items) return container end +-- Sibling helper for multi-source tests: creates an additional container +-- without resetting the recipe registry. Use AFTER setup_container() to +-- avoid wiping recipes that were just defined for the test. +local function extra_container(items) + local container = composition.create{ template = "craft_test.container" } + for _, spec in ipairs(items) do + for _ = 1, spec.count do + local e = composition.create{ template = spec.template } + inv.add(container, e) + end + end + return container +end + -- --------------------------------------------------------------------- -- Tests -- --------------------------------------------------------------------- @@ -334,6 +348,189 @@ function M.run_tests(ctx) end T.assert(cc12.ok == false and cc12.error == "missing_inputs" and missing_match_12, "12. count-form: 2-of-3 rocks reports missing={template, needed=3, have=2}") + + -- ================================================================ + -- §13: Bw-Compat — craft(recipe, entity_handle, ctx) still works + -- with bare-entity-handle locale (Form 1). + -- ================================================================ + local container13 = setup_container{ + { template = "craft_test.rock", count = 1 }, + { template = "craft_test.stick", count = 1 }, + } + crafting.define_recipe{ + id = "rock_pick_r", + inputs = { + { template = "craft_test.rock", count = 1 }, + { template = "craft_test.stick", count = 1 }, + }, + output = { template = "craft_test.rock_pick", count = 1 }, + } + local res13 = crafting.craft("rock_pick_r", container13, {}) + local pick_count_13 = 0 + local rock_count_13 = 0 + local stick_count_13 = 0 + for _, e in ipairs(inv.contents(container13)) do + local tpl = composition.template_of(e) + if tpl == "craft_test.rock_pick" then pick_count_13 = pick_count_13 + 1 end + if tpl == "craft_test.rock" then rock_count_13 = rock_count_13 + 1 end + if tpl == "craft_test.stick" then stick_count_13 = stick_count_13 + 1 end + end + T.assert(res13.ok == true + and res13.crafted_items ~= nil and #res13.crafted_items == 1 + and res13.consumed ~= nil and #res13.consumed == 2 + and pick_count_13 == 1 and rock_count_13 == 0 and stick_count_13 == 0, + "13. Bw-Compat: craft(recipe, entity_handle, ctx) accepts bare handle, post-state matches v0.1 behavior") + + -- ================================================================ + -- §14: Form-2 happy-path — 2 sources, sink = source[1]. + -- c1=1 rock, c2=1 stick, recipe needs both, + -- locale={sources={c1,c2}, sink=c1}. + -- Post: c1 has 1 rock_pick + 0 rock, c2 has 0 stick. + -- ================================================================ + local c1_14 = setup_container{ + { template = "craft_test.rock", count = 1 }, + } + local c2_14 = extra_container{ + { template = "craft_test.stick", count = 1 }, + } + crafting.define_recipe{ + id = "rock_pick_r", + inputs = { + { template = "craft_test.rock", count = 1 }, + { template = "craft_test.stick", count = 1 }, + }, + output = { template = "craft_test.rock_pick", count = 1 }, + } + local res14 = crafting.craft("rock_pick_r", + { sources = {c1_14, c2_14}, sink = c1_14 }, {}) + + local c1_pick_14, c1_rock_14, c1_stick_14 = 0, 0, 0 + for _, e in ipairs(inv.contents(c1_14)) do + local tpl = composition.template_of(e) + if tpl == "craft_test.rock_pick" then c1_pick_14 = c1_pick_14 + 1 end + if tpl == "craft_test.rock" then c1_rock_14 = c1_rock_14 + 1 end + if tpl == "craft_test.stick" then c1_stick_14 = c1_stick_14 + 1 end + end + local c2_pick_14, c2_rock_14, c2_stick_14 = 0, 0, 0 + for _, e in ipairs(inv.contents(c2_14)) do + local tpl = composition.template_of(e) + if tpl == "craft_test.rock_pick" then c2_pick_14 = c2_pick_14 + 1 end + if tpl == "craft_test.rock" then c2_rock_14 = c2_rock_14 + 1 end + if tpl == "craft_test.stick" then c2_stick_14 = c2_stick_14 + 1 end + end + T.assert(res14.ok == true + and c1_pick_14 == 1 and c1_rock_14 == 0 and c1_stick_14 == 0 + and c2_pick_14 == 0 and c2_rock_14 == 0 and c2_stick_14 == 0, + "14. Form-2 happy-path: sources={c1,c2}, sink=c1; c1 ends with rock_pick + no rock, c2 ends empty") + + -- ================================================================ + -- §15: Greedy-drain-order — source1=2 rocks, source2=5 rocks, + -- recipe needs 3 rocks → source1 fully drained, source2 has 4 left. + -- ================================================================ + local c1_15 = setup_container{ + { template = "craft_test.rock", count = 2 }, + } + local c2_15 = extra_container{ + { template = "craft_test.rock", count = 5 }, + } + crafting.define_recipe{ + id = "rock_pile_r", + inputs = { + { template = "craft_test.rock", count = 3 }, + }, + output = { template = "craft_test.rock_pick", count = 1 }, + } + local res15 = crafting.craft("rock_pile_r", + { sources = {c1_15, c2_15}, sink = c1_15 }, {}) + + local c1_rock_15 = 0 + for _, e in ipairs(inv.contents(c1_15)) do + if composition.template_of(e) == "craft_test.rock" then + c1_rock_15 = c1_rock_15 + 1 + end + end + local c2_rock_15 = 0 + for _, e in ipairs(inv.contents(c2_15)) do + if composition.template_of(e) == "craft_test.rock" then + c2_rock_15 = c2_rock_15 + 1 + end + end + T.assert(res15.ok == true + and c1_rock_15 == 0 and c2_rock_15 == 4, + "15. Greedy-drain-order: source1 (2 rocks) fully drained, source2 (5 rocks) has 4 remaining after needing 3") + + -- ================================================================ + -- §16: Aggregate missing-count — source1=1 rock, source2=1 rock, + -- recipe needs 3 → missing.have=2. + -- ================================================================ + local c1_16 = setup_container{ + { template = "craft_test.rock", count = 1 }, + } + local c2_16 = extra_container{ + { template = "craft_test.rock", count = 1 }, + } + crafting.define_recipe{ + id = "rock_pile_r", + inputs = { + { template = "craft_test.rock", count = 3 }, + }, + output = { template = "craft_test.rock_pick", count = 1 }, + } + local cc16 = crafting.can_craft("rock_pile_r", + { sources = {c1_16, c2_16}, sink = c1_16 }, {}) + local missing_match_16 = false + if cc16.missing then + for _, m in ipairs(cc16.missing) do + if m.template == "craft_test.rock" + and m.needed == 3 and m.have == 2 then + missing_match_16 = true; break + end + end + end + T.assert(cc16.ok == false and cc16.error == "missing_inputs" + and missing_match_16, + "16. Aggregate missing-count across sources: 1+1 rocks vs need 3 reports have=2") + + -- ================================================================ + -- §17: Loud-Error empty sources — locale={sources={}, sink=c1} + -- → error matches "crafting" + "sources" + "non-empty". + -- ================================================================ + local c1_17 = setup_container{ + { template = "craft_test.rock", count = 1 }, + } + crafting.define_recipe{ + id = "rock_pile_r", + inputs = { { template = "craft_test.rock", count = 1 } }, + output = { template = "craft_test.rock_pick", count = 1 }, + } + local ok17, err17 = pcall(crafting.can_craft, "rock_pile_r", + { sources = {}, sink = c1_17 }, {}) + T.assert(not ok17 and type(err17) == "string" + and string.find(err17, "crafting") ~= nil + and string.find(err17, "sources") ~= nil + and (string.find(err17, "non%-empty") ~= nil + or string.find(err17, "non empty") ~= nil), + "17. Loud-Error: locale.sources={} → error mentions 'crafting', 'sources', 'non-empty'") + + -- ================================================================ + -- §18: Loud-Error nil sink — locale={sources={c1}, sink=nil} + -- → error matches "crafting" + "sink" + "nil". + -- ================================================================ + local c1_18 = setup_container{ + { template = "craft_test.rock", count = 1 }, + } + crafting.define_recipe{ + id = "rock_pile_r", + inputs = { { template = "craft_test.rock", count = 1 } }, + output = { template = "craft_test.rock_pick", count = 1 }, + } + local ok18, err18 = pcall(crafting.can_craft, "rock_pile_r", + { sources = {c1_18}, sink = nil }, {}) + T.assert(not ok18 and type(err18) == "string" + and string.find(err18, "crafting") ~= nil + and string.find(err18, "sink") ~= nil + and string.find(err18, "nil") ~= nil, + "18. Loud-Error: locale.sink=nil → error mentions 'crafting', 'sink', 'nil'") end return M diff --git a/manifest.lib b/manifest.lib index 4dbe2ed..5f8ef72 100644 --- a/manifest.lib +++ b/manifest.lib @@ -1,10 +1,10 @@ { "id": "lib-core.crafting-test", "role": "test", - "version": "0.1.0", + "version": "0.2.0", "api_min": "0.1", "deps": [ - {"id": "lib-core.crafting", "version": "0.1.0"}, + {"id": "lib-core.crafting", "version": "0.2.0"}, {"id": "lib-core.inventory-list", "version": "0.1.0"}, {"id": "lib-core.composition", "version": "0.3.0"} ]