@omnitronix/lucky-sharky-game-engine 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/__tests__/base-game.test.d.ts +2 -0
- package/dist/__tests__/base-game.test.d.ts.map +1 -0
- package/dist/__tests__/base-game.test.js +262 -0
- package/dist/__tests__/base-game.test.js.map +1 -0
- package/dist/__tests__/bonus-handlers.test.d.ts +2 -0
- package/dist/__tests__/bonus-handlers.test.d.ts.map +1 -0
- package/dist/__tests__/bonus-handlers.test.js +243 -0
- package/dist/__tests__/bonus-handlers.test.js.map +1 -0
- package/dist/__tests__/error-paths.test.d.ts +2 -0
- package/dist/__tests__/error-paths.test.d.ts.map +1 -0
- package/dist/__tests__/error-paths.test.js +328 -0
- package/dist/__tests__/error-paths.test.js.map +1 -0
- package/dist/__tests__/free-spins-handler.test.d.ts +2 -0
- package/dist/__tests__/free-spins-handler.test.d.ts.map +1 -0
- package/dist/__tests__/free-spins-handler.test.js +410 -0
- package/dist/__tests__/free-spins-handler.test.js.map +1 -0
- package/dist/__tests__/helpers/test-engine-factory.d.ts +98 -0
- package/dist/__tests__/helpers/test-engine-factory.d.ts.map +1 -0
- package/dist/__tests__/helpers/test-engine-factory.js +236 -0
- package/dist/__tests__/helpers/test-engine-factory.js.map +1 -0
- package/dist/__tests__/hold-win-handler.test.d.ts +2 -0
- package/dist/__tests__/hold-win-handler.test.d.ts.map +1 -0
- package/dist/__tests__/hold-win-handler.test.js +481 -0
- package/dist/__tests__/hold-win-handler.test.js.map +1 -0
- package/dist/__tests__/rng-gli19-compliance.test.d.ts +0 -4
- package/dist/__tests__/rng-gli19-compliance.test.d.ts.map +1 -1
- package/dist/__tests__/rng-gli19-compliance.test.js +232 -198
- package/dist/__tests__/rng-gli19-compliance.test.js.map +1 -1
- package/dist/__tests__/rng-security.test.d.ts +2 -0
- package/dist/__tests__/rng-security.test.d.ts.map +1 -0
- package/dist/__tests__/rng-security.test.js +379 -0
- package/dist/__tests__/rng-security.test.js.map +1 -0
- package/dist/__tests__/rtp-diagnostic.test.d.ts +6 -0
- package/dist/__tests__/rtp-diagnostic.test.d.ts.map +1 -0
- package/dist/__tests__/rtp-diagnostic.test.js +216 -0
- package/dist/__tests__/rtp-diagnostic.test.js.map +1 -0
- package/dist/__tests__/rtp-simulation.test.d.ts +0 -4
- package/dist/__tests__/rtp-simulation.test.d.ts.map +1 -1
- package/dist/__tests__/rtp-simulation.test.js +228 -153
- package/dist/__tests__/rtp-simulation.test.js.map +1 -1
- package/dist/__tests__/spin-generator.test.d.ts +2 -0
- package/dist/__tests__/spin-generator.test.d.ts.map +1 -0
- package/dist/__tests__/spin-generator.test.js +375 -0
- package/dist/__tests__/spin-generator.test.js.map +1 -0
- package/dist/__tests__/state-transitions.test.d.ts +2 -0
- package/dist/__tests__/state-transitions.test.d.ts.map +1 -0
- package/dist/__tests__/state-transitions.test.js +249 -0
- package/dist/__tests__/state-transitions.test.js.map +1 -0
- package/dist/__tests__/symbol-distribution.test.d.ts +2 -0
- package/dist/__tests__/symbol-distribution.test.d.ts.map +1 -0
- package/dist/__tests__/symbol-distribution.test.js +210 -0
- package/dist/__tests__/symbol-distribution.test.js.map +1 -0
- package/dist/__tests__/win-calculator.test.d.ts +2 -0
- package/dist/__tests__/win-calculator.test.d.ts.map +1 -0
- package/dist/__tests__/win-calculator.test.js +516 -0
- package/dist/__tests__/win-calculator.test.js.map +1 -0
- package/dist/config/game-logic-config/game-logic-config.d.ts.map +1 -1
- package/dist/config/game-logic-config/game-logic-config.js +27 -17
- package/dist/config/game-logic-config/game-logic-config.js.map +1 -1
- package/dist/logic/handlers/free-spins.handler.d.ts +2 -0
- package/dist/logic/handlers/free-spins.handler.d.ts.map +1 -1
- package/dist/logic/handlers/free-spins.handler.js +74 -6
- package/dist/logic/handlers/free-spins.handler.js.map +1 -1
- package/package.json +4 -2
- package/dist/__tests__/rng-seed-security.test.d.ts +0 -6
- package/dist/__tests__/rng-seed-security.test.d.ts.map +0 -1
- package/dist/__tests__/rng-seed-security.test.js +0 -263
- package/dist/__tests__/rng-seed-security.test.js.map +0 -1
- package/dist/__tests__/rng-seed-type.test.d.ts +0 -16
- package/dist/__tests__/rng-seed-type.test.d.ts.map +0 -1
- package/dist/__tests__/rng-seed-type.test.js +0 -288
- package/dist/__tests__/rng-seed-type.test.js.map +0 -1
- package/dist/__tests__/rng-stress-boundary.test.d.ts +0 -5
- package/dist/__tests__/rng-stress-boundary.test.d.ts.map +0 -1
- package/dist/__tests__/rng-stress-boundary.test.js +0 -395
- package/dist/__tests__/rng-stress-boundary.test.js.map +0 -1
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Free Spins Handler Tests - Lucky Sharky
|
|
5
|
+
*
|
|
6
|
+
* Tests the Free Spins bonus mechanics:
|
|
7
|
+
* - Free spins initialization by booster level (different spin counts)
|
|
8
|
+
* - Retrigger mechanics (additional scatters during free spins)
|
|
9
|
+
* - Guaranteed spin handling
|
|
10
|
+
* - Free spins completion and return to base game
|
|
11
|
+
* - Win accumulation during free spins
|
|
12
|
+
* - Multiplier application in free spins
|
|
13
|
+
*/
|
|
14
|
+
const test_engine_factory_1 = require("./helpers/test-engine-factory");
|
|
15
|
+
describe('Free Spins Handler - Lucky Sharky', () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
(0, test_engine_factory_1.resetCommandIdCounter)();
|
|
18
|
+
});
|
|
19
|
+
describe('Free Spins Initialization by Booster Level', () => {
|
|
20
|
+
it('should award 10 free spins at booster level 0', async () => {
|
|
21
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
22
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
23
|
+
// Ensure booster level is 0
|
|
24
|
+
expect(session.publicState.boosterLevel).toBe(0);
|
|
25
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
26
|
+
expect(debugResult.success).toBe(true);
|
|
27
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
28
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
29
|
+
expect(bonusResult.success).toBe(true);
|
|
30
|
+
// Verify the series completed (freeSpinsRemaining should be 0)
|
|
31
|
+
expect(bonusResult.publicState.freeSpinsRemaining).toBe(0);
|
|
32
|
+
});
|
|
33
|
+
it('should award 15 free spins at booster level 3', async () => {
|
|
34
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
35
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
36
|
+
// Set booster level to 3 manually
|
|
37
|
+
const modifiedPrivate = {
|
|
38
|
+
...session.privateState,
|
|
39
|
+
boosterLevel: 3,
|
|
40
|
+
boosterMemory: 3,
|
|
41
|
+
};
|
|
42
|
+
const modifiedPublic = {
|
|
43
|
+
...session.publicState,
|
|
44
|
+
boosterLevel: 3,
|
|
45
|
+
};
|
|
46
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, modifiedPublic, modifiedPrivate, 'FREE_SPINS');
|
|
47
|
+
expect(debugResult.success).toBe(true);
|
|
48
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
49
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
50
|
+
expect(bonusResult.success).toBe(true);
|
|
51
|
+
expect(bonusResult.publicState.freeSpinsRemaining).toBe(0);
|
|
52
|
+
});
|
|
53
|
+
it('should award 20 free spins at booster level 6', async () => {
|
|
54
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
55
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
56
|
+
const modifiedPrivate = {
|
|
57
|
+
...session.privateState,
|
|
58
|
+
boosterLevel: 6,
|
|
59
|
+
boosterMemory: 6,
|
|
60
|
+
};
|
|
61
|
+
const modifiedPublic = {
|
|
62
|
+
...session.publicState,
|
|
63
|
+
boosterLevel: 6,
|
|
64
|
+
};
|
|
65
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, modifiedPublic, modifiedPrivate, 'FREE_SPINS');
|
|
66
|
+
expect(debugResult.success).toBe(true);
|
|
67
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
68
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
69
|
+
expect(bonusResult.success).toBe(true);
|
|
70
|
+
expect(bonusResult.publicState.freeSpinsRemaining).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
it('should award 25 free spins at booster level 9', async () => {
|
|
73
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
74
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
75
|
+
const modifiedPrivate = {
|
|
76
|
+
...session.privateState,
|
|
77
|
+
boosterLevel: 9,
|
|
78
|
+
boosterMemory: 9,
|
|
79
|
+
};
|
|
80
|
+
const modifiedPublic = {
|
|
81
|
+
...session.publicState,
|
|
82
|
+
boosterLevel: 9,
|
|
83
|
+
};
|
|
84
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, modifiedPublic, modifiedPrivate, 'FREE_SPINS');
|
|
85
|
+
expect(debugResult.success).toBe(true);
|
|
86
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
87
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
88
|
+
expect(bonusResult.success).toBe(true);
|
|
89
|
+
expect(bonusResult.publicState.freeSpinsRemaining).toBe(0);
|
|
90
|
+
});
|
|
91
|
+
it('should use boosterMemory if boosterLevel is 0', async () => {
|
|
92
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
93
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
94
|
+
// Simulate scenario where boosterMemory was saved before entering free spins
|
|
95
|
+
const modifiedPrivate = {
|
|
96
|
+
...session.privateState,
|
|
97
|
+
boosterLevel: 0,
|
|
98
|
+
boosterMemory: 6, // Should use this value
|
|
99
|
+
};
|
|
100
|
+
const modifiedPublic = {
|
|
101
|
+
...session.publicState,
|
|
102
|
+
boosterLevel: 0,
|
|
103
|
+
};
|
|
104
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, modifiedPublic, modifiedPrivate, 'FREE_SPINS');
|
|
105
|
+
expect(debugResult.success).toBe(true);
|
|
106
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
107
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
108
|
+
expect(bonusResult.success).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe('Retrigger Mechanics', () => {
|
|
112
|
+
it('should support retrigger at booster level 3 (+5 spins)', async () => {
|
|
113
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
114
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
115
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
116
|
+
expect(debugResult.success).toBe(true);
|
|
117
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
118
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
119
|
+
// Free spins should complete successfully regardless of retrigger
|
|
120
|
+
expect(bonusResult.success).toBe(true);
|
|
121
|
+
expect(bonusResult.privateState.nextSpinType).toBe('BASE_GAME_SPIN');
|
|
122
|
+
});
|
|
123
|
+
it('should allow multiple retriggers during free spins', async () => {
|
|
124
|
+
const { engine, lcgRng } = (0, test_engine_factory_1.createEngineWithLcgRng)(42424);
|
|
125
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
126
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
127
|
+
expect(debugResult.success).toBe(true);
|
|
128
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
129
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
130
|
+
expect(bonusResult.success).toBe(true);
|
|
131
|
+
// Verify RNG outcome is present
|
|
132
|
+
expect(bonusResult.rngOutcome).toBeDefined();
|
|
133
|
+
});
|
|
134
|
+
it('should not retrigger if booster level does not increase', async () => {
|
|
135
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
136
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
137
|
+
// Start at max booster level
|
|
138
|
+
const modifiedPrivate = {
|
|
139
|
+
...session.privateState,
|
|
140
|
+
boosterLevel: 9,
|
|
141
|
+
boosterMemory: 9,
|
|
142
|
+
};
|
|
143
|
+
const modifiedPublic = {
|
|
144
|
+
...session.publicState,
|
|
145
|
+
boosterLevel: 9,
|
|
146
|
+
};
|
|
147
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, modifiedPublic, modifiedPrivate, 'FREE_SPINS');
|
|
148
|
+
expect(debugResult.success).toBe(true);
|
|
149
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
150
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
151
|
+
expect(bonusResult.success).toBe(true);
|
|
152
|
+
expect(bonusResult.publicState.freeSpinsRemaining).toBe(0);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
describe('Guaranteed Spin Handling', () => {
|
|
156
|
+
it('should decrement guaranteeSpinCount during free spins', async () => {
|
|
157
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
158
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
159
|
+
const modifiedPrivate = {
|
|
160
|
+
...session.privateState,
|
|
161
|
+
guaranteeSpinCount: 5,
|
|
162
|
+
};
|
|
163
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, modifiedPrivate, 'FREE_SPINS');
|
|
164
|
+
expect(debugResult.success).toBe(true);
|
|
165
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
166
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
167
|
+
expect(bonusResult.success).toBe(true);
|
|
168
|
+
// Guarantee count should be decremented or zero
|
|
169
|
+
expect(bonusResult.privateState.guaranteeSpinCount).toBeGreaterThanOrEqual(0);
|
|
170
|
+
});
|
|
171
|
+
it('should handle zero guaranteeSpinCount', async () => {
|
|
172
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
173
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
174
|
+
const modifiedPrivate = {
|
|
175
|
+
...session.privateState,
|
|
176
|
+
guaranteeSpinCount: 0,
|
|
177
|
+
};
|
|
178
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, modifiedPrivate, 'FREE_SPINS');
|
|
179
|
+
expect(debugResult.success).toBe(true);
|
|
180
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
181
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
182
|
+
expect(bonusResult.success).toBe(true);
|
|
183
|
+
expect(bonusResult.privateState.guaranteeSpinCount).toBe(0);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('Free Spins Completion and Return to Base Game', () => {
|
|
187
|
+
it('should return to BASE_GAME_SPIN after all free spins complete', async () => {
|
|
188
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
189
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
190
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
191
|
+
expect(debugResult.success).toBe(true);
|
|
192
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
193
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
194
|
+
expect(bonusResult.success).toBe(true);
|
|
195
|
+
expect(bonusResult.privateState.nextSpinType).toBe('BASE_GAME_SPIN');
|
|
196
|
+
expect(bonusResult.publicState.freeSpinsRemaining).toBe(0);
|
|
197
|
+
});
|
|
198
|
+
it('should restore booster level after free spins', async () => {
|
|
199
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
200
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
201
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
202
|
+
expect(debugResult.success).toBe(true);
|
|
203
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
204
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
205
|
+
expect(bonusResult.success).toBe(true);
|
|
206
|
+
// Booster should be restored to pre-bonus level (stored in boosterMemory)
|
|
207
|
+
expect(bonusResult.publicState.boosterLevel).toBeGreaterThanOrEqual(0);
|
|
208
|
+
expect(bonusResult.publicState.boosterLevel).toBeLessThanOrEqual(9);
|
|
209
|
+
expect(bonusResult.privateState.boosterMemory).toBe(0);
|
|
210
|
+
});
|
|
211
|
+
it('should clear pending bonuses after completion', async () => {
|
|
212
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
213
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
214
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
215
|
+
expect(debugResult.success).toBe(true);
|
|
216
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
217
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
218
|
+
expect(bonusResult.success).toBe(true);
|
|
219
|
+
expect(bonusResult.privateState.pendingBonuses).toEqual([]);
|
|
220
|
+
});
|
|
221
|
+
it('should allow normal spins after free spins complete', async () => {
|
|
222
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
223
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
224
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
225
|
+
expect(debugResult.success).toBe(true);
|
|
226
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
227
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
228
|
+
expect(bonusResult.success).toBe(true);
|
|
229
|
+
// Should be able to execute a normal spin
|
|
230
|
+
const spinResult = await (0, test_engine_factory_1.executeSpin)(engine, bonusResult.publicState, bonusResult.privateState);
|
|
231
|
+
expect(spinResult.success).toBe(true);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
describe('Win Accumulation During Free Spins', () => {
|
|
235
|
+
it('should produce non-negative total winnings', async () => {
|
|
236
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
237
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
238
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
239
|
+
expect(debugResult.success).toBe(true);
|
|
240
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
241
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
242
|
+
expect(bonusResult.success).toBe(true);
|
|
243
|
+
if (bonusResult.outcome) {
|
|
244
|
+
const outcomes = Array.isArray(bonusResult.outcome)
|
|
245
|
+
? bonusResult.outcome
|
|
246
|
+
: [bonusResult.outcome];
|
|
247
|
+
for (const o of outcomes) {
|
|
248
|
+
if (o.playerWinning !== undefined) {
|
|
249
|
+
expect(o.playerWinning).toBeGreaterThanOrEqual(0);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
it('should produce finite win amounts', async () => {
|
|
255
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
256
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
257
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
258
|
+
expect(debugResult.success).toBe(true);
|
|
259
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
260
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
261
|
+
expect(bonusResult.success).toBe(true);
|
|
262
|
+
if (bonusResult.outcome) {
|
|
263
|
+
const outcomes = Array.isArray(bonusResult.outcome)
|
|
264
|
+
? bonusResult.outcome
|
|
265
|
+
: [bonusResult.outcome];
|
|
266
|
+
for (const o of outcomes) {
|
|
267
|
+
if (o.playerWinning !== undefined) {
|
|
268
|
+
expect(Number.isFinite(o.playerWinning)).toBe(true);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
it('should scale wins with bet amount during free spins', async () => {
|
|
274
|
+
// Test at bet 1.0
|
|
275
|
+
const engine1 = (0, test_engine_factory_1.createEngine)();
|
|
276
|
+
const session1 = await (0, test_engine_factory_1.initSession)(engine1, 1.0);
|
|
277
|
+
const debug1 = await (0, test_engine_factory_1.debugTriggerBonus)(engine1, session1.publicState, session1.privateState, 'FREE_SPINS', 1.0);
|
|
278
|
+
expect(debug1.success).toBe(true);
|
|
279
|
+
const bonus1 = debug1.privateState.pendingBonuses[0];
|
|
280
|
+
const result1 = await (0, test_engine_factory_1.startBonusRound)(engine1, debug1.publicState, debug1.privateState, bonus1.bonusType, 1.0);
|
|
281
|
+
expect(result1.success).toBe(true);
|
|
282
|
+
// Test at bet 5.0
|
|
283
|
+
const engine2 = (0, test_engine_factory_1.createEngine)();
|
|
284
|
+
const session2 = await (0, test_engine_factory_1.initSession)(engine2, 5.0);
|
|
285
|
+
const debug2 = await (0, test_engine_factory_1.debugTriggerBonus)(engine2, session2.publicState, session2.privateState, 'FREE_SPINS', 5.0);
|
|
286
|
+
expect(debug2.success).toBe(true);
|
|
287
|
+
const bonus2 = debug2.privateState.pendingBonuses[0];
|
|
288
|
+
const result2 = await (0, test_engine_factory_1.startBonusRound)(engine2, debug2.publicState, debug2.privateState, bonus2.bonusType, 5.0);
|
|
289
|
+
expect(result2.success).toBe(true);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
describe('Multiplier Application in Free Spins', () => {
|
|
293
|
+
it('should apply booster multiplier to wins', async () => {
|
|
294
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
295
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
296
|
+
// Set higher booster level for visible multiplier effect
|
|
297
|
+
const modifiedPrivate = {
|
|
298
|
+
...session.privateState,
|
|
299
|
+
boosterLevel: 5,
|
|
300
|
+
boosterMemory: 5,
|
|
301
|
+
};
|
|
302
|
+
const modifiedPublic = {
|
|
303
|
+
...session.publicState,
|
|
304
|
+
boosterLevel: 5,
|
|
305
|
+
};
|
|
306
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, modifiedPublic, modifiedPrivate, 'FREE_SPINS');
|
|
307
|
+
expect(debugResult.success).toBe(true);
|
|
308
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
309
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
310
|
+
expect(bonusResult.success).toBe(true);
|
|
311
|
+
});
|
|
312
|
+
it('should handle booster level progression during free spins', async () => {
|
|
313
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
314
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
315
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
316
|
+
expect(debugResult.success).toBe(true);
|
|
317
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
318
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
319
|
+
expect(bonusResult.success).toBe(true);
|
|
320
|
+
if (bonusResult.outcome) {
|
|
321
|
+
const outcomes = Array.isArray(bonusResult.outcome)
|
|
322
|
+
? bonusResult.outcome
|
|
323
|
+
: [bonusResult.outcome];
|
|
324
|
+
for (const o of outcomes) {
|
|
325
|
+
if (o.boosterLevel !== undefined) {
|
|
326
|
+
expect(o.boosterLevel).toBeGreaterThanOrEqual(0);
|
|
327
|
+
expect(o.boosterLevel).toBeLessThanOrEqual(9);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
it('should cap booster level at 9 during free spins', async () => {
|
|
333
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
334
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
335
|
+
// Start at max level
|
|
336
|
+
const modifiedPrivate = {
|
|
337
|
+
...session.privateState,
|
|
338
|
+
boosterLevel: 9,
|
|
339
|
+
boosterMemory: 9,
|
|
340
|
+
};
|
|
341
|
+
const modifiedPublic = {
|
|
342
|
+
...session.publicState,
|
|
343
|
+
boosterLevel: 9,
|
|
344
|
+
};
|
|
345
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, modifiedPublic, modifiedPrivate, 'FREE_SPINS');
|
|
346
|
+
expect(debugResult.success).toBe(true);
|
|
347
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
348
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
349
|
+
expect(bonusResult.success).toBe(true);
|
|
350
|
+
if (bonusResult.outcome) {
|
|
351
|
+
const outcomes = Array.isArray(bonusResult.outcome)
|
|
352
|
+
? bonusResult.outcome
|
|
353
|
+
: [bonusResult.outcome];
|
|
354
|
+
for (const o of outcomes) {
|
|
355
|
+
if (o.boosterLevel !== undefined) {
|
|
356
|
+
expect(o.boosterLevel).toBeLessThanOrEqual(9);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
describe('RNG Outcome Tracking', () => {
|
|
363
|
+
it('should include RNG outcome for free spins', async () => {
|
|
364
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
365
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
366
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
367
|
+
expect(debugResult.success).toBe(true);
|
|
368
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
369
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
370
|
+
expect(bonusResult.success).toBe(true);
|
|
371
|
+
expect(bonusResult.rngOutcome).toBeDefined();
|
|
372
|
+
});
|
|
373
|
+
it('should produce valid RNG outcome structure', async () => {
|
|
374
|
+
const engine = (0, test_engine_factory_1.createEngine)();
|
|
375
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
376
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
377
|
+
expect(debugResult.success).toBe(true);
|
|
378
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
379
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
380
|
+
expect(bonusResult.success).toBe(true);
|
|
381
|
+
if (bonusResult.rngOutcome) {
|
|
382
|
+
const validation = (0, test_engine_factory_1.validateRngOutcome)(bonusResult.rngOutcome);
|
|
383
|
+
expect(validation.valid).toBe(true);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
describe('Deterministic Testing with Mock RNG', () => {
|
|
388
|
+
it('should produce deterministic results with same mock values', async () => {
|
|
389
|
+
const mockValues = Array(100).fill(50);
|
|
390
|
+
const { engine: engine1 } = (0, test_engine_factory_1.createEngineWithMockRng)(mockValues);
|
|
391
|
+
const { engine: engine2 } = (0, test_engine_factory_1.createEngineWithMockRng)(mockValues);
|
|
392
|
+
const session1 = await (0, test_engine_factory_1.initSession)(engine1);
|
|
393
|
+
const session2 = await (0, test_engine_factory_1.initSession)(engine2);
|
|
394
|
+
const debug1 = await (0, test_engine_factory_1.debugTriggerBonus)(engine1, session1.publicState, session1.privateState, 'FREE_SPINS');
|
|
395
|
+
const debug2 = await (0, test_engine_factory_1.debugTriggerBonus)(engine2, session2.publicState, session2.privateState, 'FREE_SPINS');
|
|
396
|
+
expect(debug1.success).toBe(true);
|
|
397
|
+
expect(debug2.success).toBe(true);
|
|
398
|
+
});
|
|
399
|
+
it('should handle LCG RNG for reproducible simulations', async () => {
|
|
400
|
+
const { engine, lcgRng } = (0, test_engine_factory_1.createEngineWithLcgRng)(12345);
|
|
401
|
+
const session = await (0, test_engine_factory_1.initSession)(engine);
|
|
402
|
+
const debugResult = await (0, test_engine_factory_1.debugTriggerBonus)(engine, session.publicState, session.privateState, 'FREE_SPINS');
|
|
403
|
+
expect(debugResult.success).toBe(true);
|
|
404
|
+
const bonus = debugResult.privateState.pendingBonuses[0];
|
|
405
|
+
const bonusResult = await (0, test_engine_factory_1.startBonusRound)(engine, debugResult.publicState, debugResult.privateState, bonus.bonusType);
|
|
406
|
+
expect(bonusResult.success).toBe(true);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
//# sourceMappingURL=free-spins-handler.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"free-spins-handler.test.js","sourceRoot":"","sources":["../../src/__tests__/free-spins-handler.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lucky Sharky Test Engine Factory.
|
|
3
|
+
* Creates engine instances with controllable RNG for deterministic testing.
|
|
4
|
+
*
|
|
5
|
+
* Mock injection strategy:
|
|
6
|
+
* The engine, gameLogic, and all handlers share a SINGLE RngService instance.
|
|
7
|
+
* We replace the `rngClient` on that shared instance so all RNG calls go
|
|
8
|
+
* through our mock — no need to rebuild gameLogic or handlers.
|
|
9
|
+
*/
|
|
10
|
+
import { LuckySharkyV1GameEngine } from '../../lucky-sharky-v1.game-engine';
|
|
11
|
+
import { MockRngClient, LcgRngClient } from '@omnitronix/game-test-utils';
|
|
12
|
+
export { MockRngClient, LcgRngClient, nextCommandId, resetCommandIdCounter, getCommandIdCounter, canonicalizeRngOutcome, hashRngOutcome, hashString, isValidSha256, validateRngOutcome, validateRngOutcomeRecord, serializeState, deserializeState, cloneState, statesEqual, roundTripState, diffStates, } from '@omnitronix/game-test-utils';
|
|
13
|
+
/**
|
|
14
|
+
* Create a standard engine with default (DummyRngClient) RNG.
|
|
15
|
+
* Suitable for integration tests that don't need deterministic outcomes.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createEngine(): LuckySharkyV1GameEngine;
|
|
18
|
+
/**
|
|
19
|
+
* Create an engine with a MockRngClient injected.
|
|
20
|
+
* Returns both the engine and the mock for test control.
|
|
21
|
+
*
|
|
22
|
+
* @param values - Fixed value or sequence of values the mock will return.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createEngineWithMockRng(values?: number[] | number): {
|
|
25
|
+
engine: LuckySharkyV1GameEngine;
|
|
26
|
+
mockRng: MockRngClient;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Create an engine with an LCG RNG for reproducible simulations.
|
|
30
|
+
* Same seed always produces the same sequence.
|
|
31
|
+
*
|
|
32
|
+
* @param seed - LCG seed (default: 12345)
|
|
33
|
+
*/
|
|
34
|
+
export declare function createEngineWithLcgRng(seed?: number): {
|
|
35
|
+
engine: LuckySharkyV1GameEngine;
|
|
36
|
+
lcgRng: LcgRngClient;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Inject a mock RNG client into an existing engine.
|
|
40
|
+
* Useful when you need to change mock behavior mid-test.
|
|
41
|
+
*/
|
|
42
|
+
export declare function injectMockRng(engine: LuckySharkyV1GameEngine, mockRng: MockRngClient): void;
|
|
43
|
+
/**
|
|
44
|
+
* Wait for the engine's async preload() to finish.
|
|
45
|
+
* The engine constructor fires preload() without await (fire-and-forget).
|
|
46
|
+
* We poll for `engine.reels` to be defined before proceeding.
|
|
47
|
+
*/
|
|
48
|
+
export declare function waitForEngineReady(engine: any, timeout?: number): Promise<void>;
|
|
49
|
+
export interface SessionState {
|
|
50
|
+
publicState: any;
|
|
51
|
+
privateState: any;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Initialize a session on the engine.
|
|
55
|
+
* Returns the public and private state for subsequent commands.
|
|
56
|
+
* Waits for engine preload to complete before issuing any commands.
|
|
57
|
+
*/
|
|
58
|
+
export declare function initSession(engine: LuckySharkyV1GameEngine, betAmount?: number, betAmountThresholds?: number[]): Promise<SessionState>;
|
|
59
|
+
/**
|
|
60
|
+
* Execute a SPIN command and return the full result.
|
|
61
|
+
*/
|
|
62
|
+
export declare function executeSpin(engine: LuckySharkyV1GameEngine, publicState: any, privateState: any, betAmount?: number): Promise<any>;
|
|
63
|
+
/**
|
|
64
|
+
* Execute a SPIN command with error handling for known engine bugs.
|
|
65
|
+
* Returns null if a known bug is encountered, allowing tests to skip bad spins.
|
|
66
|
+
*/
|
|
67
|
+
export declare function safeExecuteSpin(engine: LuckySharkyV1GameEngine, publicState: any, privateState: any, betAmount?: number): Promise<any | null>;
|
|
68
|
+
/**
|
|
69
|
+
* Execute multiple spins, threading state through each one.
|
|
70
|
+
* Returns an array of all results.
|
|
71
|
+
*/
|
|
72
|
+
export declare function executeSpins(engine: LuckySharkyV1GameEngine, publicState: any, privateState: any, count: number, betAmount?: number): Promise<any[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Start a bonus round.
|
|
75
|
+
*/
|
|
76
|
+
export declare function startBonusRound(engine: LuckySharkyV1GameEngine, publicState: any, privateState: any, bonusType?: string, betAmount?: number): Promise<any>;
|
|
77
|
+
/**
|
|
78
|
+
* Trigger a debug bonus.
|
|
79
|
+
*/
|
|
80
|
+
export declare function debugTriggerBonus(engine: LuckySharkyV1GameEngine, publicState: any, privateState: any, bonusType?: string, betAmount?: number): Promise<any>;
|
|
81
|
+
/**
|
|
82
|
+
* Get the rngOutcome from the engine's RNG service for a specific command.
|
|
83
|
+
*/
|
|
84
|
+
export declare function getRngOutcome(engine: LuckySharkyV1GameEngine, commandId: string): any;
|
|
85
|
+
/**
|
|
86
|
+
* Get all RNG outcomes currently tracked by the engine.
|
|
87
|
+
*/
|
|
88
|
+
export declare function getAllRngOutcomes(engine: LuckySharkyV1GameEngine): any;
|
|
89
|
+
/**
|
|
90
|
+
* Run a full init + N spins cycle. Convenience for RTP simulations.
|
|
91
|
+
* Returns { results, finalPublicState, finalPrivateState }.
|
|
92
|
+
*/
|
|
93
|
+
export declare function runSpinCycle(engine: LuckySharkyV1GameEngine, spinCount: number, betAmount?: number): Promise<{
|
|
94
|
+
results: any[];
|
|
95
|
+
finalPublicState: any;
|
|
96
|
+
finalPrivateState: any;
|
|
97
|
+
}>;
|
|
98
|
+
//# sourceMappingURL=test-engine-factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-engine-factory.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/test-engine-factory.ts"],"names":[],"mappings":"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"}
|