From 17857ccbc21a01cabd4c5348f762d2bf62e325c5 Mon Sep 17 00:00:00 2001 From: Calic Date: Sun, 14 Jun 2026 19:29:51 +0200 Subject: [PATCH] test: de-duplication + malformed-locale assertions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §19 verifies sources={c, c} is de-duplicated (have=1, not 2). §20 verifies {sink=c} without sources raises a loud-error attributing crafting + sources. Co-Authored-By: Claude Opus 4.7 (1M context) --- init.lua | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/init.lua b/init.lua index 488275c..fa34697 100644 --- a/init.lua +++ b/init.lua @@ -1,5 +1,7 @@ --- lib-core.crafting-test — Phase C.1 --- 12 assertions: 5 registry (define_recipe + list + get) + 7 discovery/match/craft. +-- lib-core.crafting-test — Phase C.1 + D.0 +-- 20 assertions: 12 base (registry + discovery/match/craft), 6 locale-API +-- (Form-2 happy + greedy-drain + aggregate + Loud-Errors), 2 silent-fail +-- regressions (duplicate-sources de-dup + malformed-locale loud-error). -- Pattern follows P.2.4 Test-Module-Pattern; TAP output via engine.test.* local composition = require("lib-core.composition") @@ -531,6 +533,73 @@ function M.run_tests(ctx) and string.find(err18, "sink") ~= nil and string.find(err18, "nil") ~= nil, "18. Loud-Error: locale.sink=nil → error mentions 'crafting', 'sink', 'nil'") + + -- ================================================================ + -- §19: Duplicate sources are de-duplicated. + -- Single container with 1 rock; pass {c, c} as sources. + -- count_by_template must NOT double-count → can_craft for a + -- recipe needing 2 rocks reports have=1 (not 2). + -- ================================================================ + local c_19 = setup_container{ + { template = "craft_test.rock", count = 1 }, + } + composition.define_template{ + id = "craft_test.rock_pile_19", + properties = { + stack_mode = "individual", + sprite_path = "sprites/pile.png", + position = {x = 0, y = 0}, + }, + tags = {"renderable", "item"}, + } + crafting.define_recipe{ + id = "r2rocks_19", + inputs = { { template = "craft_test.rock", count = 2 } }, + output = { template = "craft_test.rock_pile_19", count = 1 }, + } + local cc19 = crafting.can_craft("r2rocks_19", + { sources = {c_19, c_19}, sink = c_19 }, {}) + local missing_match_19 = false + if cc19.missing then + for _, m in ipairs(cc19.missing) do + if m.template == "craft_test.rock" + and m.needed == 2 and m.have == 1 then + missing_match_19 = true; break + end + end + end + T.assert(cc19.ok == false and cc19.error == "missing_inputs" + and missing_match_19, + "19. Duplicate sources de-duplicated: sources={c,c} with 1 rock reports have=1 (not 2)") + + -- ================================================================ + -- §20: Loud-Error — Form-2 table without sources field. + -- Caller typo passes {sink=c} without sources. resolve_locale + -- must loud-error attributing crafting + sources, not silently + -- fall through to the Form-1 bw-compat shim (which would then + -- crash deep inside inventory-list). + -- ================================================================ + local c_20 = setup_container{} + composition.define_template{ + id = "craft_test.x_20", + properties = { + stack_mode = "individual", + sprite_path = "sprites/x.png", + position = {x = 0, y = 0}, + }, + tags = {"renderable", "item"}, + } + crafting.define_recipe{ + id = "rp_20", + inputs = { { template = "craft_test.rock", count = 1 } }, + output = { template = "craft_test.x_20", count = 1 }, + } + local malformed_20 = { sink = c_20 } + local ok20, err20 = pcall(crafting.craft, "rp_20", malformed_20, {}) + T.assert(not ok20 and type(err20) == "string" + and string.find(err20, "crafting") ~= nil + and string.find(err20, "sources") ~= nil, + "20. Loud-Error: locale table without 'sources' field → error mentions 'crafting' and 'sources'") end return M