diff options
Diffstat (limited to 'internal/game/correct_audit_fixes_test.go')
| -rw-r--r-- | internal/game/correct_audit_fixes_test.go | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/internal/game/correct_audit_fixes_test.go b/internal/game/correct_audit_fixes_test.go new file mode 100644 index 0000000..90fdc53 --- /dev/null +++ b/internal/game/correct_audit_fixes_test.go @@ -0,0 +1,104 @@ +package game + +import ( + "testing" + + "thehouseoficarus/internal/behavior" + "thehouseoficarus/internal/casino" + "thehouseoficarus/internal/item" + "thehouseoficarus/internal/prediction" +) + +// Regression: gather verbs must normalize to the gather action type, otherwise +// startAction's bhType != verb gate rejects mine/chop/fish/cut outright. +func TestGatherVerbsNormalizeToGatherAction(t *testing.T) { + for _, verb := range []string{"mine", "chop", "fish", "cut", "shear"} { + if got := normalizeVerb(verb); got != string(behavior.TypeGather) { + t.Errorf("normalizeVerb(%q) = %q, want %q", verb, got, behavior.TypeGather) + } + } +} + +// Regression: an unbounded step count must be rejected before it allocates. +func TestParseWalkDirectionsRejectsHugeCount(t *testing.T) { + if _, err := parseWalkDirections("999999999n"); err == nil { + t.Fatal("expected an error for an unbounded step count") + } + dirs, err := parseWalkDirections("3n2e") + if err != nil { + t.Fatalf("parseWalkDirections: %v", err) + } + if len(dirs) != 5 { + t.Fatalf("len(dirs) = %d, want 5", len(dirs)) + } +} + +// Regression: collectPredictions must not mark resolved contracts collected +// when the credit cap blocks the payout, or the winnings are lost forever. +func TestCollectPredictionsKeepsContractsWhenCreditCapBlocks(t *testing.T) { + h := newHarness(t) + h.seedAccount("alice") + h.seedCharacter("alice", "alice") + sess, _ := h.connect("alice") + + sess.Player.PredictionContracts = []prediction.Contract{ + {MarketID: "m1", Status: "resolved", Proceeds: 1000}, + } + sess.Player.Credits = casino.MaxCredits + + h.g.RunOnOwner(func() { h.g.collectPredictions(sess) }) + if got := sess.Player.PredictionContracts[0].Status; got != "resolved" { + t.Fatalf("credit-cap collect changed status to %q; the payout would be lost", got) + } + + // With room under the cap, the same contract must still be collectable. + sess.Player.Credits = 0 + h.g.RunOnOwner(func() { h.g.collectPredictions(sess) }) + if got := sess.Player.PredictionContracts[0].Status; got != "collected" { + t.Fatalf("status = %q, want collected", got) + } + if sess.Player.Credits != 1000 { + t.Fatalf("credits = %d, want 1000", sess.Player.Credits) + } +} + +// Regression: a craft with no ingredient entries must be unsatisfiable, not +// "always satisfied" (which would loop producing output for free). +func TestPlanIngredientsRejectsEmptyCraft(t *testing.T) { + c := &item.CraftDef{Type: "fletch"} + if plan := planIngredients(c, func(string) int { return 999 }); plan != nil { + t.Fatalf("empty craft produced a plan: %+v", plan) + } +} + +// Regression: a command reached through a prefix/abbreviation must not have its +// arguments lowercased (global/symbol/prompt/addlocalobj read rawInput). +func TestGlobalCommandPreservesArgumentCase(t *testing.T) { + h := newHarness(t) + h.seedAccount("alice") + h.seedCharacter("alice", "alice") + h.seedAccount("bob") + h.seedCharacter("bob", "bob") + alice, _ := h.connect("alice") + _, bobConn := h.connect("bob") + h.g.RunOnOwner(func() { h.g.HandleSessionOnOwner(alice, "glo Hello World") }) + if !bobConn.Contains("Hello World") { + t.Fatalf("global broadcast lost argument case: %s", bobConn.Bytes()) + } +} + +// Regression: the player-file classifier must canonicalize repeated "/" and +// "data/" prefixes so a raw-editor path cannot dodge the online check. +func TestPlayerDataFileNameCanonicalizesPrefixes(t *testing.T) { + for _, in := range []string{ + "players/characters/alice.yaml", + "//players/characters/alice.yaml", + "data/data/players/characters/alice.yaml", + "/data/players/characters/alice.yaml", + } { + name, kind, ok := playerDataFileName(in) + if !ok || name != "alice" || kind != "characters" { + t.Errorf("playerDataFileName(%q) = (%q, %q, %v), want (alice, characters, true)", in, name, kind, ok) + } + } +} |
