aboutsummaryrefslogtreecommitdiff
path: root/internal/game/hardening_casino_entropy_wiring_test.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-18 05:31:09 -0400
committerhistoria <[not public]>2026-09-18 05:31:09 -0400
commit153acb7f644d755c45533b7c5311e3ffa47e8973 (patch)
tree7645e6f7b65bc3830b078e3b9336d2e6b199d584 /internal/game/hardening_casino_entropy_wiring_test.go
parent87d08f0151e643951769e323995f1c5ed89ee200 (diff)
downloadthehouseoficarus-153acb7f644d755c45533b7c5311e3ffa47e8973.tar.gz
fix: harden engine across casino, economy, concurrency, security, and content
Diffstat (limited to 'internal/game/hardening_casino_entropy_wiring_test.go')
-rw-r--r--internal/game/hardening_casino_entropy_wiring_test.go72
1 files changed, 72 insertions, 0 deletions
diff --git a/internal/game/hardening_casino_entropy_wiring_test.go b/internal/game/hardening_casino_entropy_wiring_test.go
new file mode 100644
index 0000000..4f7c75e
--- /dev/null
+++ b/internal/game/hardening_casino_entropy_wiring_test.go
@@ -0,0 +1,72 @@
+package game
+
+// Closure regressions for the casino RNG hardening: the entropy-seeded
+// constructor must be the one PRODUCTION wires, not merely available.
+
+import (
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+// TestProductionCasinoSeedIsEntropy pins the production wiring: game.New must
+// build its casino manager with casino.NewManagerEntropy, and no production
+// file may call casino.NewManager(seed). That constructor exists only as the
+// deterministic test seam, because a wall-clock seed makes every shoe, reel,
+// and spin stream reconstructible from the startup nanosecond.
+//
+// The manager's seed is unexported and deliberately not observable through the
+// casino API (NewManagerEntropy and NewManager are otherwise
+// indistinguishable), so this is a source-level guard: it is the only place
+// the choice of constructor can be pinned. It fails on a revert to
+// NewManager(time.Now().UnixNano()).
+func TestProductionCasinoSeedIsEntropy(t *testing.T) {
+ root := filepath.Join("..", "..")
+ sawEntropyCall := false
+ err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if d.IsDir() {
+ name := d.Name()
+ // Skip VCS internals, generated/test data, and the persisted
+ // game data (which is not Go source at all).
+ if path != root && (strings.HasPrefix(name, ".") || name == "testdata" || name == "vendor") {
+ return fs.SkipDir
+ }
+ return nil
+ }
+ if !strings.HasSuffix(d.Name(), ".go") || strings.HasSuffix(d.Name(), "_test.go") {
+ return nil
+ }
+ data, readErr := os.ReadFile(path)
+ if readErr != nil {
+ return readErr
+ }
+ rel, relErr := filepath.Rel(root, path)
+ if relErr != nil {
+ rel = path
+ }
+ for i, line := range strings.Split(string(data), "\n") {
+ trimmed := strings.TrimSpace(line)
+ if strings.HasPrefix(trimmed, "//") || strings.HasPrefix(trimmed, "*") {
+ continue // prose about the seam, not a call
+ }
+ if strings.Contains(trimmed, "casino.NewManager(") {
+ t.Errorf("%s:%d calls the deterministic casino.NewManager(seed) outside tests: %s", rel, i+1, trimmed)
+ }
+ if strings.Contains(trimmed, "casino.NewManagerEntropy()") {
+ sawEntropyCall = true
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ t.Fatalf("walk module: %v", err)
+ }
+ if !sawEntropyCall {
+ t.Fatal("no production call to casino.NewManagerEntropy() found: the casino RNG entropy fix is not wired")
+ }
+}