diff options
| author | historia <[not public]> | 2026-09-18 05:31:09 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-18 05:31:09 -0400 |
| commit | 153acb7f644d755c45533b7c5311e3ffa47e8973 (patch) | |
| tree | 7645e6f7b65bc3830b078e3b9336d2e6b199d584 /internal/validate/polish_destructive_fail_test.go | |
| parent | 87d08f0151e643951769e323995f1c5ed89ee200 (diff) | |
| download | thehouseoficarus-153acb7f644d755c45533b7c5311e3ffa47e8973.tar.gz | |
fix: harden engine across casino, economy, concurrency, security, and content
Diffstat (limited to 'internal/validate/polish_destructive_fail_test.go')
| -rw-r--r-- | internal/validate/polish_destructive_fail_test.go | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/internal/validate/polish_destructive_fail_test.go b/internal/validate/polish_destructive_fail_test.go new file mode 100644 index 0000000..8c9a038 --- /dev/null +++ b/internal/validate/polish_destructive_fail_test.go @@ -0,0 +1,64 @@ +package validate + +// Regression: the "a failable recipe must not yield nothing" invariant was +// enforced only for smelting (and only by a shipped-data test), so a future +// cooking/fletching/combine recipe with a success formula (or a declared +// fail_message) and no fail product would silently consume materials and +// return nothing. validateCraftBlocks now warns on it. + +import ( + "path/filepath" + "testing" +) + +func writePolishCraftRecipe(t *testing.T, dir, craftBody string) { + t.Helper() + writeFile(t, filepath.Join(dir, "items", "polish_in.yaml"), "id: polish_in\nname: polish in\n") + writeFile(t, filepath.Join(dir, "items", "polish_out.yaml"), + "id: polish_out\nname: polish out\ncraft:\n - type: cooking\n"+craftBody+ + " ingredients:\n - items: [polish_in]\n quantity: 1\n") +} + +func TestCraftFailableWithoutFailProductIsWarned(t *testing.T) { + cases := []struct { + name string + craftBody string + }{ + {"success formula", " success:\n base: 0.5\n cap: 0.95\n per_level: 0.02\n"}, + {"declared fail_message", " fail_message: You burn it.\n"}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + dir := t.TempDir() + writePolishCraftRecipe(t, dir, c.craftBody) + issues := validateCraftBlocks(newSource(dir)) + if !containsMsg(issues, "failure destroys materials with no fail product") { + t.Fatalf("expected a destructive-failure warning, got: %+v", issues) + } + for _, issue := range issues { + if issue.Level != "WARN" { + t.Fatalf("destructive failure must be a WARN, got: %+v", issue) + } + } + }) + } +} + +func TestCraftWithFailProductOrGuaranteedSuccessDoesNotWarn(t *testing.T) { + cases := []struct { + name string + craftBody string + }{ + {"declares a fail product", " fail: polish_in\n success:\n base: 0.5\n cap: 0.95\n per_level: 0.02\n"}, + {"success cannot fall below 1", " success:\n base: 1.0\n cap: 1.0\n per_level: 0\n"}, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + dir := t.TempDir() + writePolishCraftRecipe(t, dir, c.craftBody) + if issues := validateCraftBlocks(newSource(dir)); containsMsg(issues, "failure destroys materials with no fail product") { + t.Fatalf("did not expect a destructive-failure warning, got: %+v", issues) + } + }) + } +} |
