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") } }