@openora/core 0.4.1-canary.152 → 0.4.1-canary.153

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.
Files changed (122) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/admin-console/__tests__/backoffice.service.int.test.js +20 -3
  3. package/dist/admin-console/__tests__/backoffice.service.int.test.js.map +1 -1
  4. package/dist/audit/__tests__/map-event.test.js +46 -0
  5. package/dist/audit/__tests__/map-event.test.js.map +1 -1
  6. package/dist/audit/plugin.d.ts.map +1 -1
  7. package/dist/audit/plugin.js +74 -0
  8. package/dist/audit/plugin.js.map +1 -1
  9. package/dist/audit/service/audit.service.d.ts.map +1 -1
  10. package/dist/audit/service/audit.service.js +1 -6
  11. package/dist/audit/service/audit.service.js.map +1 -1
  12. package/dist/casino/gaming/__tests__/admin-reporting.int.test.js +20 -3
  13. package/dist/casino/gaming/__tests__/admin-reporting.int.test.js.map +1 -1
  14. package/dist/casino/gaming/__tests__/game-category.service.int.test.d.ts +2 -0
  15. package/dist/casino/gaming/__tests__/game-category.service.int.test.d.ts.map +1 -0
  16. package/dist/casino/gaming/__tests__/game-category.service.int.test.js +134 -0
  17. package/dist/casino/gaming/__tests__/game-category.service.int.test.js.map +1 -0
  18. package/dist/casino/gaming/__tests__/game-provider.service.int.test.d.ts +2 -0
  19. package/dist/casino/gaming/__tests__/game-provider.service.int.test.d.ts.map +1 -0
  20. package/dist/casino/gaming/__tests__/game-provider.service.int.test.js +274 -0
  21. package/dist/casino/gaming/__tests__/game-provider.service.int.test.js.map +1 -0
  22. package/dist/casino/gaming/__tests__/gaming-catalog-admin.router.int.test.d.ts +2 -0
  23. package/dist/casino/gaming/__tests__/gaming-catalog-admin.router.int.test.d.ts.map +1 -0
  24. package/dist/casino/gaming/__tests__/gaming-catalog-admin.router.int.test.js +174 -0
  25. package/dist/casino/gaming/__tests__/gaming-catalog-admin.router.int.test.js.map +1 -0
  26. package/dist/casino/gaming/__tests__/gaming-migration.int.test.d.ts +2 -0
  27. package/dist/casino/gaming/__tests__/gaming-migration.int.test.d.ts.map +1 -0
  28. package/dist/casino/gaming/__tests__/gaming-migration.int.test.js +171 -0
  29. package/dist/casino/gaming/__tests__/gaming-migration.int.test.js.map +1 -0
  30. package/dist/casino/gaming/__tests__/gaming.service.int.test.js +274 -13
  31. package/dist/casino/gaming/__tests__/gaming.service.int.test.js.map +1 -1
  32. package/dist/casino/gaming/contract/index.d.ts +520 -18
  33. package/dist/casino/gaming/contract/index.d.ts.map +1 -1
  34. package/dist/casino/gaming/contract/index.js +167 -4
  35. package/dist/casino/gaming/contract/index.js.map +1 -1
  36. package/dist/casino/gaming/drizzle/migrations/0005_natural_thunderbolt_ross.sql +223 -0
  37. package/dist/casino/gaming/drizzle/migrations/meta/0005_snapshot.json +680 -0
  38. package/dist/casino/gaming/drizzle/migrations/meta/_journal.json +7 -0
  39. package/dist/casino/gaming/plugin.d.ts.map +1 -1
  40. package/dist/casino/gaming/plugin.js +9 -2
  41. package/dist/casino/gaming/plugin.js.map +1 -1
  42. package/dist/casino/gaming/router/index.d.ts +372 -18
  43. package/dist/casino/gaming/router/index.d.ts.map +1 -1
  44. package/dist/casino/gaming/router/index.js +62 -6
  45. package/dist/casino/gaming/router/index.js.map +1 -1
  46. package/dist/casino/gaming/schema/index.d.ts +499 -4
  47. package/dist/casino/gaming/schema/index.d.ts.map +1 -1
  48. package/dist/casino/gaming/schema/index.js +72 -6
  49. package/dist/casino/gaming/schema/index.js.map +1 -1
  50. package/dist/casino/gaming/service/game-category.service.d.ts +109 -0
  51. package/dist/casino/gaming/service/game-category.service.d.ts.map +1 -0
  52. package/dist/casino/gaming/service/game-category.service.js +176 -0
  53. package/dist/casino/gaming/service/game-category.service.js.map +1 -0
  54. package/dist/casino/gaming/service/game-provider.service.d.ts +118 -0
  55. package/dist/casino/gaming/service/game-provider.service.d.ts.map +1 -0
  56. package/dist/casino/gaming/service/game-provider.service.js +251 -0
  57. package/dist/casino/gaming/service/game-provider.service.js.map +1 -0
  58. package/dist/casino/gaming/service/gaming.service.d.ts +122 -14
  59. package/dist/casino/gaming/service/gaming.service.d.ts.map +1 -1
  60. package/dist/casino/gaming/service/gaming.service.js +189 -23
  61. package/dist/casino/gaming/service/gaming.service.js.map +1 -1
  62. package/dist/casino/lobby/__tests__/lobby.cache.int.test.js +137 -4
  63. package/dist/casino/lobby/__tests__/lobby.cache.int.test.js.map +1 -1
  64. package/dist/casino/lobby/contract/index.d.ts +68 -8
  65. package/dist/casino/lobby/contract/index.d.ts.map +1 -1
  66. package/dist/casino/lobby/contract/index.js +4 -3
  67. package/dist/casino/lobby/contract/index.js.map +1 -1
  68. package/dist/casino/lobby/router/index.d.ts +34 -4
  69. package/dist/casino/lobby/router/index.d.ts.map +1 -1
  70. package/dist/casino/lobby/service/lobby.service.d.ts +34 -4
  71. package/dist/casino/lobby/service/lobby.service.d.ts.map +1 -1
  72. package/dist/casino/lobby/service/lobby.service.js +60 -26
  73. package/dist/casino/lobby/service/lobby.service.js.map +1 -1
  74. package/dist/casino/shared/game-catalog.d.ts +32 -0
  75. package/dist/casino/shared/game-catalog.d.ts.map +1 -0
  76. package/dist/casino/shared/game-catalog.js +67 -0
  77. package/dist/casino/shared/game-catalog.js.map +1 -0
  78. package/dist/compliance/__tests__/rg-monitoring.service.int.test.js +17 -3
  79. package/dist/compliance/__tests__/rg-monitoring.service.int.test.js.map +1 -1
  80. package/dist/compliance/__tests__/rg-self-service.int.test.js +2 -2
  81. package/dist/compliance/__tests__/rg-self-service.int.test.js.map +1 -1
  82. package/dist/contracts/__tests__/kit.test.js +21 -1
  83. package/dist/contracts/__tests__/kit.test.js.map +1 -1
  84. package/dist/contracts/kit.d.ts +3 -0
  85. package/dist/contracts/kit.d.ts.map +1 -1
  86. package/dist/contracts/kit.js +8 -0
  87. package/dist/contracts/kit.js.map +1 -1
  88. package/dist/contracts/schemas/__tests__/events.test.js +7 -0
  89. package/dist/contracts/schemas/__tests__/events.test.js.map +1 -1
  90. package/dist/contracts/schemas/__tests__/game-schemas.test.d.ts +2 -0
  91. package/dist/contracts/schemas/__tests__/game-schemas.test.d.ts.map +1 -0
  92. package/dist/contracts/schemas/__tests__/game-schemas.test.js +43 -0
  93. package/dist/contracts/schemas/__tests__/game-schemas.test.js.map +1 -0
  94. package/dist/contracts/schemas/events.d.ts +109 -0
  95. package/dist/contracts/schemas/events.d.ts.map +1 -1
  96. package/dist/contracts/schemas/events.js +86 -0
  97. package/dist/contracts/schemas/events.js.map +1 -1
  98. package/dist/contracts/schemas/game.d.ts +39 -0
  99. package/dist/contracts/schemas/game.d.ts.map +1 -1
  100. package/dist/contracts/schemas/game.js +32 -0
  101. package/dist/contracts/schemas/game.js.map +1 -1
  102. package/dist/contracts/schemas/iam.d.ts +1 -1
  103. package/dist/contracts/schemas/iam.js +1 -1
  104. package/dist/contracts/schemas/iam.js.map +1 -1
  105. package/dist/react/client.d.ts +2 -1
  106. package/dist/react/client.d.ts.map +1 -1
  107. package/dist/react/client.js +2 -1
  108. package/dist/react/client.js.map +1 -1
  109. package/dist/server/auth/permissions.d.ts +12 -12
  110. package/dist/server/auth/permissions.js +1 -1
  111. package/dist/server/auth/permissions.js.map +1 -1
  112. package/dist/server/db/__tests__/query-helpers.test.js +47 -1
  113. package/dist/server/db/__tests__/query-helpers.test.js.map +1 -1
  114. package/dist/server/db/index.d.ts +1 -1
  115. package/dist/server/db/index.d.ts.map +1 -1
  116. package/dist/server/db/index.js +1 -1
  117. package/dist/server/db/index.js.map +1 -1
  118. package/dist/server/db/query-helpers.d.ts +5 -0
  119. package/dist/server/db/query-helpers.d.ts.map +1 -1
  120. package/dist/server/db/query-helpers.js +37 -0
  121. package/dist/server/db/query-helpers.js.map +1 -1
  122. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gaming-catalog-admin.router.int.test.js","sourceRoot":"","sources":["../../../../src/casino/gaming/__tests__/gaming-catalog-admin.router.int.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gaming-migration.int.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gaming-migration.int.test.d.ts","sourceRoot":"","sources":["../../../../src/casino/gaming/__tests__/gaming-migration.int.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,171 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { readdirSync, readFileSync } from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { sql, eq } from 'drizzle-orm';
5
+ import { createTestDb } from '@openora/core/testing';
6
+ import { game, gameCategory, gameCategoryGame, gameProvider } from '../schema/index.js';
7
+ let db;
8
+ // Matched by migration index (0003/0004/...) so later renames of the random
9
+ // drizzle tag suffix never break this file.
10
+ function migrationStatements(index) {
11
+ const dir = fileURLToPath(new URL('../drizzle/migrations', import.meta.url));
12
+ const [file] = readdirSync(dir)
13
+ .filter((f) => new RegExp(`^${index}_.*\\.sql$`).exec(f))
14
+ .sort();
15
+ if (!file) {
16
+ throw new Error(`migration ${index}_* not found`);
17
+ }
18
+ return readFileSync(`${dir}/${file}`, 'utf8')
19
+ .split('--> statement-breakpoint')
20
+ .map((s) => s.trim())
21
+ .filter((s) => s.length > 0);
22
+ }
23
+ async function apply(indexes) {
24
+ for (const index of indexes) {
25
+ for (const stmt of migrationStatements(index)) {
26
+ await db.drizzle.db.execute(sql.raw(stmt));
27
+ }
28
+ }
29
+ }
30
+ async function nullability() {
31
+ const cols = (await db.drizzle.db.execute(sql `
32
+ SELECT column_name AS "name", is_nullable AS "nullable"
33
+ FROM information_schema.columns
34
+ WHERE table_name = 'game'
35
+ AND column_name IN ('slug', 'provider_id', 'aggregator', 'provider', 'category')`));
36
+ return new Map(cols.rows.map((r) => [r.name, r.nullable]));
37
+ }
38
+ beforeAll(async () => {
39
+ db = await createTestDb([]);
40
+ });
41
+ afterAll(async () => {
42
+ await db.drop();
43
+ });
44
+ describe('gaming catalog migration 0005 (real PG)', () => {
45
+ it('expands, backfills and enforces NOT NULL in one migration, keeping legacy columns', async () => {
46
+ await apply(['0000', '0001', '0002']);
47
+ await db.drizzle.db.execute(sql `
48
+ INSERT INTO "game" ("name", "provider", "category", "is_active") VALUES
49
+ ('Roulette', 'Pragmatic Play', 'Table Games', true),
50
+ ('Roulette', 'NetEnt', 'Slots', true),
51
+ ('Aces', 'ACME', 'Slots', false),
52
+ ('Kings', 'Acme', 'Slots', true)`);
53
+ await apply(['0003', '0004', '0005']);
54
+ const providers = await db.drizzle.db
55
+ .select({ slug: gameProvider.slug, name: gameProvider.name, isActive: gameProvider.isActive })
56
+ .from(gameProvider);
57
+ expect(providers.map((p) => p.slug).sort()).toEqual([
58
+ 'acme',
59
+ 'acme-2',
60
+ 'netent',
61
+ 'pragmatic-play',
62
+ ]);
63
+ expect(providers.map((p) => p.name).sort()).toEqual([
64
+ 'ACME',
65
+ 'Acme',
66
+ 'NetEnt',
67
+ 'Pragmatic Play',
68
+ ]);
69
+ expect(new Set(providers.map((p) => p.isActive))).toEqual(new Set([true]));
70
+ const games = await db.drizzle.db
71
+ .select({
72
+ name: game.name,
73
+ slug: game.slug,
74
+ providerId: game.providerId,
75
+ aggregator: game.aggregator,
76
+ isActive: game.isActive,
77
+ })
78
+ .from(game);
79
+ expect(games.map((g) => g.slug).sort()).toEqual(['aces', 'kings', 'roulette', 'roulette-2']);
80
+ expect(new Set(games.map((g) => g.aggregator))).toEqual(new Set(['direct']));
81
+ const byName = new Map(games.map((g) => [g.name, g]));
82
+ expect(byName.get('Aces')).toMatchObject({ isActive: false });
83
+ expect(byName.get('Kings')).toMatchObject({ isActive: true });
84
+ const providerNames = await db.drizzle.db
85
+ .select({ slug: game.slug, name: gameProvider.name })
86
+ .from(game)
87
+ .innerJoin(gameProvider, eq(game.providerId, gameProvider.id));
88
+ expect(new Map(providerNames.map((r) => [r.slug, r.name])).get('aces')).toBe('ACME');
89
+ const categories = await db.drizzle.db.select({ slug: gameCategory.slug }).from(gameCategory);
90
+ expect(categories.map((c) => c.slug).sort()).toEqual(['slots', 'table-games']);
91
+ const links = await db.drizzle.db.select().from(gameCategoryGame);
92
+ expect(links).toHaveLength(4);
93
+ const enforced = await nullability();
94
+ expect(enforced.get('slug')).toBe('NO');
95
+ expect(enforced.get('provider_id')).toBe('NO');
96
+ expect(enforced.get('aggregator')).toBe('NO');
97
+ // Legacy columns stay until the follow-up drop migration.
98
+ expect(enforced.get('provider')).toBe('YES');
99
+ expect(enforced.get('category')).toBe('YES');
100
+ });
101
+ it('accepts a previous-release legacy insert during a rolling deploy', async () => {
102
+ // Runs on the schema the previous test migrated: the old writer only knows
103
+ // name/provider/category, one onto a backfilled provider and one onto new names.
104
+ await db.drizzle.db.execute(sql `
105
+ INSERT INTO "game" ("name", "provider", "category", "is_active") VALUES
106
+ ('Roulette', 'NetEnt', 'Slots', true),
107
+ ('Crash X', 'Fresh Studio', 'Crash', true)`);
108
+ const rows = await db.drizzle.db
109
+ .select({
110
+ name: game.name,
111
+ slug: game.slug,
112
+ aggregator: game.aggregator,
113
+ providerName: gameProvider.name,
114
+ providerSlug: gameProvider.slug,
115
+ providerActive: gameProvider.isActive,
116
+ categorySlug: gameCategory.slug,
117
+ })
118
+ .from(game)
119
+ .innerJoin(gameProvider, eq(game.providerId, gameProvider.id))
120
+ .innerJoin(gameCategoryGame, eq(gameCategoryGame.gameId, game.id))
121
+ .innerJoin(gameCategory, eq(gameCategoryGame.categoryId, gameCategory.id))
122
+ .where(sql `${game.slug} IN ('roulette-3', 'crash-x')`);
123
+ expect(new Map(rows.map((r) => [r.slug, r]))).toEqual(new Map([
124
+ [
125
+ 'roulette-3',
126
+ expect.objectContaining({
127
+ aggregator: 'direct',
128
+ providerSlug: 'netent',
129
+ categorySlug: 'slots',
130
+ }),
131
+ ],
132
+ [
133
+ 'crash-x',
134
+ expect.objectContaining({
135
+ aggregator: 'direct',
136
+ providerName: 'Fresh Studio',
137
+ providerSlug: 'fresh-studio',
138
+ providerActive: true,
139
+ categorySlug: 'crash',
140
+ }),
141
+ ],
142
+ ]));
143
+ const mapped = (await db.drizzle.db.execute(sql `
144
+ SELECT 1 FROM "game_provider_aggregator_mapping" "m"
145
+ JOIN "game_provider" "p" ON "p"."id" = "m"."provider_id"
146
+ WHERE "p"."slug" = 'fresh-studio' AND "m"."aggregator" = 'direct'`));
147
+ expect(mapped.rows).toHaveLength(1);
148
+ });
149
+ it('keeps the legacy triggers off the new-shape insert path', async () => {
150
+ const [studio] = await db.drizzle.db
151
+ .select({ id: gameProvider.id })
152
+ .from(gameProvider)
153
+ .where(eq(gameProvider.slug, 'netent'));
154
+ const [inserted] = await db.drizzle.db
155
+ .insert(game)
156
+ .values({
157
+ name: 'Fresh Game',
158
+ slug: 'custom-slug',
159
+ providerId: studio.id,
160
+ aggregator: 'hub',
161
+ })
162
+ .returning({ id: game.id, slug: game.slug, aggregator: game.aggregator });
163
+ expect(inserted).toMatchObject({ slug: 'custom-slug', aggregator: 'hub' });
164
+ const links = await db.drizzle.db
165
+ .select()
166
+ .from(gameCategoryGame)
167
+ .where(eq(gameCategoryGame.gameId, inserted.id));
168
+ expect(links).toEqual([]);
169
+ });
170
+ });
171
+ //# sourceMappingURL=gaming-migration.int.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gaming-migration.int.test.js","sourceRoot":"","sources":["../../../../src/casino/gaming/__tests__/gaming-migration.int.test.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,17 @@
1
1
  import { describe, it, expect, vi, beforeAll, afterAll, beforeEach } from 'vitest';
2
+ import { randomUUID } from 'node:crypto';
2
3
  import { eq, sql } from 'drizzle-orm';
3
4
  import { createTestDb } from '@openora/core/testing';
4
5
  import { migrate as migrateProfile } from '@openora/core/pam/migrate/profile';
5
- import { mock, makeEventBus, makeIdentityReader } from '../../../testing/mock.js';
6
+ import { mock, makeEventBus, makeIdentityReader, NO_CLIENT_META } from '../../../testing/mock.js';
6
7
  import { migrate } from '../migrate.js';
7
- import { game, gameRound } from '../schema/index.js';
8
- import { GamingService, GameNotFoundError, RgRestrictedError, InsufficientBalanceError, WinCreditFailedError, ExternalRoundOwnerMismatchError, } from '../service/gaming.service.js';
8
+ import { game, gameCategory, gameCategoryGame, gameProvider, gameProviderAggregatorMapping, gameRound, } from '../schema/index.js';
9
+ import { GamingService, GameAggregatorNotMappedError, GameNotFoundError, GameSlugTakenError, RgRestrictedError, InsufficientBalanceError, WinCreditFailedError, ExternalRoundOwnerMismatchError, } from '../service/gaming.service.js';
10
+ import { GameProviderNotFoundError } from '../service/game-provider.service.js';
11
+ import { GameCategoryNotFoundError } from '../service/game-category.service.js';
9
12
  let db;
10
13
  const noopEvents = makeEventBus();
14
+ const ACTOR = { actorId: '00000000-0000-4000-8000-000000000001', ...NO_CLIENT_META };
11
15
  const eligibility = (isRestricted) => mock({ isRestricted: vi.fn().mockResolvedValue(isRestricted) });
12
16
  const unrestricted = eligibility(false);
13
17
  function makeWalletCommands(debitResult, creditResult = { ok: true, newBalance: '0' }) {
@@ -19,14 +23,43 @@ function makeWalletCommands(debitResult, creditResult = { ok: true, newBalance:
19
23
  function makeService({ provider = mock({
20
24
  launchGame: vi.fn().mockResolvedValue({ launchUrl: 'https://mock/play', token: 'tok' }),
21
25
  endRound: vi.fn(),
22
- }), playEligibility = unrestricted, walletCommands = makeWalletCommands({ ok: true, newBalance: '0', currency: 'USD' }), rgLimits, } = {}) {
23
- return new GamingService(db.drizzle, noopEvents, provider, playEligibility, walletCommands, makeIdentityReader(), rgLimits);
26
+ }), playEligibility = unrestricted, walletCommands = makeWalletCommands({ ok: true, newBalance: '0', currency: 'USD' }), rgLimits, events = noopEvents, } = {}) {
27
+ return new GamingService(db.drizzle, events, provider, playEligibility, walletCommands, makeIdentityReader(), rgLimits);
24
28
  }
25
- async function seedGame(overrides = {}) {
29
+ async function seedProvider(overrides = {}) {
30
+ const [row] = await db.drizzle.db
31
+ .insert(gameProvider)
32
+ .values({ slug: `studio-${randomUUID()}`, name: 'Studio', isActive: true, ...overrides })
33
+ .returning();
34
+ await db.drizzle.db
35
+ .insert(gameProviderAggregatorMapping)
36
+ .values({ providerId: row.id, aggregator: 'direct', vendorId: row.slug });
37
+ return row;
38
+ }
39
+ async function seedCategory(overrides = {}) {
40
+ const [row] = await db.drizzle.db
41
+ .insert(gameCategory)
42
+ .values({ slug: `category-${randomUUID()}`, name: 'Slots', ...overrides })
43
+ .returning();
44
+ return row;
45
+ }
46
+ async function seedGame(overrides = {}, categoryIds) {
47
+ const provider = await seedProvider({ name: 'Mock Studio' });
48
+ const ids = categoryIds ?? [(await seedCategory()).id];
26
49
  const [row] = await db.drizzle.db
27
50
  .insert(game)
28
- .values({ name: 'Game', provider: 'mock', category: 'slots', ...overrides })
51
+ .values({
52
+ name: 'Game',
53
+ slug: `game-${randomUUID()}`,
54
+ providerId: provider.id,
55
+ aggregator: 'direct',
56
+ isActive: true,
57
+ ...overrides,
58
+ })
29
59
  .returning();
60
+ await db.drizzle.db
61
+ .insert(gameCategoryGame)
62
+ .values(ids.map((categoryId) => ({ gameId: row.id, categoryId })));
30
63
  return row;
31
64
  }
32
65
  beforeAll(async () => {
@@ -36,22 +69,87 @@ afterAll(async () => {
36
69
  await db.drop();
37
70
  });
38
71
  beforeEach(async () => {
39
- await db.drizzle.db.execute(sql `TRUNCATE ${gameRound}, ${game} RESTART IDENTITY CASCADE`);
72
+ await db.drizzle.db.execute(sql `TRUNCATE ${gameRound}, ${gameCategoryGame}, ${game}, ${gameProvider}, ${gameCategory} RESTART IDENTITY CASCADE`);
40
73
  });
41
74
  describe('GamingService lobby (real PG)', () => {
42
- it('listGames returns only active games, ordered by name', async () => {
75
+ it('listGamesPublic paginates playable games ordered by name', async () => {
43
76
  await seedGame({ name: 'Baccarat', isActive: true });
44
77
  await seedGame({ name: 'Aces', isActive: true });
45
78
  await seedGame({ name: 'Retired', isActive: false });
46
- const games = await makeService().listGames();
47
- expect(games.map((g) => g.name)).toEqual(['Aces', 'Baccarat']);
79
+ const page = await makeService().listGamesPublic({ page: 1, limit: 10 });
80
+ expect(page.total).toBe(2);
81
+ expect(page.items.map((g) => g.name)).toEqual(['Aces', 'Baccarat']);
82
+ });
83
+ it('listGamesPublic searches and filters by provider and category', async () => {
84
+ const p1 = await seedProvider({ slug: 'studio-one', name: 'One' });
85
+ const p2 = await seedProvider({ slug: 'studio-two', name: 'Two' });
86
+ const slots = await seedCategory({ slug: 'slots', name: 'Slots' });
87
+ const live = await seedCategory({ slug: 'live', name: 'Live' });
88
+ await seedGame({ name: 'Gates of Olympus', slug: 'gates-of-olympus', providerId: p1.id, isActive: true }, [slots.id]);
89
+ await seedGame({ name: 'Sweet Bonanza', slug: 'sweet-bonanza', providerId: p1.id, isActive: true }, [slots.id]);
90
+ await seedGame({ name: 'Crazy Time', slug: 'crazy-time', providerId: p2.id, isActive: true }, [
91
+ live.id,
92
+ slots.id,
93
+ ]);
94
+ const svc = makeService();
95
+ expect((await svc.listGamesPublic({ page: 1, limit: 10, q: 'bonanza' })).total).toBe(1);
96
+ expect((await svc.listGamesPublic({ page: 1, limit: 10, providerId: p1.id })).items.map((g) => g.slug)).toEqual(['gates-of-olympus', 'sweet-bonanza']);
97
+ expect((await svc.listGamesPublic({ page: 1, limit: 10, categoryId: live.id })).total).toBe(1);
98
+ expect((await svc.listGamesPublic({ page: 1, limit: 10, categoryId: slots.id })).total).toBe(3);
99
+ });
100
+ it('hides an inactive category from public filtering while retaining admin filtering', async () => {
101
+ const inactive = await seedCategory({ name: 'Hidden', isActive: false });
102
+ const linked = await seedGame({ name: 'Linked Game' }, [inactive.id]);
103
+ const svc = makeService();
104
+ const publicResult = await svc.listGamesPublic({
105
+ page: 1,
106
+ limit: 10,
107
+ categoryId: inactive.id,
108
+ });
109
+ expect(publicResult).toMatchObject({ items: [], total: 0 });
110
+ const adminResult = await svc.listGamesAdmin({
111
+ page: 1,
112
+ limit: 10,
113
+ categoryId: inactive.id,
114
+ isActive: true,
115
+ });
116
+ expect(adminResult.items.map((item) => item.id)).toEqual([linked.id]);
117
+ expect(adminResult.total).toBe(1);
48
118
  });
49
119
  it('getGame returns the row for a known id and 404s an unknown one', async () => {
50
- const created = await seedGame({ name: 'Roulette', category: 'table' });
120
+ const table = await seedCategory({
121
+ slug: 'table-games',
122
+ name: 'Table Games',
123
+ translations: { de: { name: 'Tischspiele' } },
124
+ });
125
+ const blackjack = await seedCategory({ slug: 'blackjack', name: 'Blackjack', isActive: false });
126
+ const created = await seedGame({ name: 'Roulette' }, [table.id, blackjack.id]);
51
127
  const svc = makeService();
52
- expect(await svc.getGame(created.id)).toMatchObject({ name: 'Roulette', category: 'table' });
128
+ expect(await svc.getGame(created.id)).toMatchObject({
129
+ name: 'Roulette',
130
+ categories: [
131
+ { slug: 'blackjack', translations: {} },
132
+ { slug: 'table-games', translations: { de: { name: 'Tischspiele' } } },
133
+ ],
134
+ });
135
+ await expect(svc.getGame(created.id, { activeOnly: true })).resolves.toMatchObject({
136
+ categories: [{ slug: 'table-games' }],
137
+ });
53
138
  await expect(svc.getGame('00000000-0000-0000-0000-000000000000')).rejects.toBeInstanceOf(GameNotFoundError);
54
139
  });
140
+ it('getGame hides inactive games and deactivated providers only behind activeOnly', async () => {
141
+ const dark = await seedGame({ name: 'Dark', isActive: false });
142
+ const orphaned = await seedGame({ name: 'Orphaned' });
143
+ await db.drizzle.db
144
+ .update(gameProvider)
145
+ .set({ isActive: false })
146
+ .where(eq(gameProvider.id, orphaned.providerId));
147
+ const svc = makeService();
148
+ await expect(svc.getGame(dark.id, { activeOnly: true })).rejects.toBeInstanceOf(GameNotFoundError);
149
+ await expect(svc.getGame(orphaned.id, { activeOnly: true })).rejects.toBeInstanceOf(GameNotFoundError);
150
+ await expect(svc.getGame(dark.id)).resolves.toMatchObject({ name: 'Dark', isActive: false });
151
+ await expect(svc.getGame(orphaned.id)).resolves.toMatchObject({ name: 'Orphaned' });
152
+ });
55
153
  });
56
154
  const refusingLimits = () => mock({
57
155
  checkDeposit: vi.fn(),
@@ -101,6 +199,36 @@ describe('GamingService.startRound (real PG)', () => {
101
199
  await expect(svc.startRound('00000000-0000-0000-0000-000000000111', '00000000-0000-0000-0000-000000000222', 'EUR', '10')).rejects.toBeInstanceOf(GameNotFoundError);
102
200
  expect(launchGame).not.toHaveBeenCalled();
103
201
  });
202
+ it('404s an inactive game without touching the wallet or provider', async () => {
203
+ const created = await seedGame({ name: 'Dark', isActive: false });
204
+ const walletCommands = makeWalletCommands({ ok: true, newBalance: '90', currency: 'USD' });
205
+ const launchGame = vi.fn();
206
+ const svc = makeService({
207
+ provider: mock({ launchGame, endRound: vi.fn() }),
208
+ walletCommands,
209
+ });
210
+ await expect(svc.startRound('00000000-0000-0000-0000-000000000311', created.id, 'USD', '10')).rejects.toBeInstanceOf(GameNotFoundError);
211
+ expect(walletCommands.debit).not.toHaveBeenCalled();
212
+ expect(launchGame).not.toHaveBeenCalled();
213
+ expect(await db.drizzle.db.select().from(gameRound)).toHaveLength(0);
214
+ });
215
+ it('404s a game on a deactivated provider without touching the wallet or provider', async () => {
216
+ const created = await seedGame({ name: 'Orphaned' });
217
+ await db.drizzle.db
218
+ .update(gameProvider)
219
+ .set({ isActive: false })
220
+ .where(eq(gameProvider.id, created.providerId));
221
+ const walletCommands = makeWalletCommands({ ok: true, newBalance: '90', currency: 'USD' });
222
+ const launchGame = vi.fn();
223
+ const svc = makeService({
224
+ provider: mock({ launchGame, endRound: vi.fn() }),
225
+ walletCommands,
226
+ });
227
+ await expect(svc.startRound('00000000-0000-0000-0000-000000000312', created.id, 'USD', '10')).rejects.toBeInstanceOf(GameNotFoundError);
228
+ expect(walletCommands.debit).not.toHaveBeenCalled();
229
+ expect(launchGame).not.toHaveBeenCalled();
230
+ expect(await db.drizzle.db.select().from(gameRound)).toHaveLength(0);
231
+ });
104
232
  it('debits the stake and persists the round on sufficient balance', async () => {
105
233
  const created = await seedGame({ id: '00000000-0000-0000-0000-0000000000a1', name: 'Aces' });
106
234
  const walletCommands = makeWalletCommands({ ok: true, newBalance: '90', currency: 'USD' });
@@ -141,6 +269,47 @@ describe('GamingService.startRound (real PG)', () => {
141
269
  expect(await db.drizzle.db.select().from(gameRound)).toHaveLength(0);
142
270
  });
143
271
  });
272
+ describe('GamingService listGames provider gate (real PG)', () => {
273
+ it('separates the public playable gate from the admin game-active filter', async () => {
274
+ const live = await seedGame({ name: 'Live Game' });
275
+ const hidden = await seedGame({ name: 'Hidden Game' });
276
+ await db.drizzle.db
277
+ .update(gameProvider)
278
+ .set({ isActive: false })
279
+ .where(eq(gameProvider.id, hidden.providerId));
280
+ const svc = makeService();
281
+ const pub = await svc.listGamesPublic({ page: 1, limit: 10 });
282
+ expect(pub.items.map((g) => g.id)).toEqual([live.id]);
283
+ expect(pub.total).toBe(1);
284
+ const admin = await svc.listGamesAdmin({ page: 1, limit: 10, isActive: true });
285
+ expect(admin.total).toBe(2);
286
+ });
287
+ it('listGamesAdmin includes non-playable games and sorts by provider and game fields', async () => {
288
+ const alphaZulu = await seedProvider({ name: 'Alpha', slug: 'zulu' });
289
+ const alphaAlpha = await seedProvider({ name: 'Alpha', slug: 'alpha' });
290
+ const betaAlpha = await seedProvider({ name: 'Beta', slug: 'beta-alpha', isActive: false });
291
+ await seedGame({
292
+ id: '00000000-0000-4000-8000-000000000002',
293
+ name: 'Same',
294
+ providerId: alphaZulu.id,
295
+ });
296
+ await seedGame({
297
+ id: '00000000-0000-4000-8000-000000000001',
298
+ name: 'Same',
299
+ providerId: alphaZulu.id,
300
+ });
301
+ await seedGame({ name: 'Zulu', providerId: alphaAlpha.id });
302
+ await seedGame({ name: 'Aardvark', providerId: betaAlpha.id });
303
+ const result = await makeService().listGamesAdmin({ page: 1, limit: 10 });
304
+ expect(result.items.map((item) => [item.provider.name, item.provider.slug, item.name, item.id])).toEqual([
305
+ ['Alpha', 'alpha', 'Zulu', expect.any(String)],
306
+ ['Alpha', 'zulu', 'Same', '00000000-0000-4000-8000-000000000001'],
307
+ ['Alpha', 'zulu', 'Same', '00000000-0000-4000-8000-000000000002'],
308
+ ['Beta', 'beta-alpha', 'Aardvark', expect.any(String)],
309
+ ]);
310
+ expect(result.total).toBe(4);
311
+ });
312
+ });
144
313
  describe('GamingService.startRound bonus rollover completion (real PG)', () => {
145
314
  it('emits wallet.bonus_rollover.completed once per credit the bet just completed', async () => {
146
315
  const created = await seedGame({ id: '00000000-0000-0000-0000-0000000000a3', name: 'Aces' });
@@ -209,6 +378,98 @@ describe('GamingService.startRound bonus rollover completion (real PG)', () => {
209
378
  expect(events.emit).not.toHaveBeenCalledWith('wallet.bonus_rollover.completed', expect.anything());
210
379
  });
211
380
  });
381
+ describe('GamingService updateGame (real PG)', () => {
382
+ const emittedTopics = (events) => events.emit.mock.calls.map(([topic]) => topic);
383
+ it('patches scalar fields and emits an event', async () => {
384
+ const created = await seedGame({ name: 'Roulette' });
385
+ const events = makeEventBus();
386
+ const svc = makeService({ events });
387
+ const updated = await svc.updateGame({
388
+ id: created.id,
389
+ name: 'Roulette Gold',
390
+ isActive: false,
391
+ ...ACTOR,
392
+ });
393
+ expect(updated).toMatchObject({ name: 'Roulette Gold', isActive: false });
394
+ expect(emittedTopics(events)).toContain('gaming.game.updated');
395
+ });
396
+ it('replaces the category set, including clearing it with an empty array', async () => {
397
+ const table = await seedCategory({ slug: 'table-games', name: 'Table Games' });
398
+ const blackjack = await seedCategory({ slug: 'blackjack', name: 'Blackjack' });
399
+ const created = await seedGame({}, [table.id, blackjack.id]);
400
+ const svc = makeService();
401
+ const replaced = await svc.updateGame({
402
+ id: created.id,
403
+ categoryIds: [blackjack.id],
404
+ ...ACTOR,
405
+ });
406
+ expect(replaced.categories.map((c) => c.slug)).toEqual(['blackjack']);
407
+ const cleared = await svc.updateGame({ id: created.id, categoryIds: [], ...ACTOR });
408
+ expect(cleared.categories).toEqual([]);
409
+ });
410
+ it('leaves links untouched when categoryIds is omitted', async () => {
411
+ const table = await seedCategory({ slug: 'table-games', name: 'Table Games' });
412
+ const created = await seedGame({}, [table.id]);
413
+ const svc = makeService();
414
+ const updated = await svc.updateGame({ id: created.id, name: 'Renamed', ...ACTOR });
415
+ expect(updated.categories.map((c) => c.slug)).toEqual(['table-games']);
416
+ });
417
+ it('reassigns the provider and validates all references', async () => {
418
+ const created = await seedGame();
419
+ const other = await seedProvider({ slug: 'other-studio', name: 'Other' });
420
+ const svc = makeService();
421
+ const updated = await svc.updateGame({
422
+ id: created.id,
423
+ providerId: other.id,
424
+ ...ACTOR,
425
+ });
426
+ expect(updated.provider).toMatchObject({ slug: 'other-studio' });
427
+ await expect(svc.updateGame({
428
+ id: created.id,
429
+ providerId: '00000000-0000-4000-8000-000000000000',
430
+ ...ACTOR,
431
+ })).rejects.toBeInstanceOf(GameProviderNotFoundError);
432
+ await expect(svc.updateGame({
433
+ id: created.id,
434
+ categoryIds: ['00000000-0000-4000-8000-000000000000'],
435
+ ...ACTOR,
436
+ })).rejects.toBeInstanceOf(GameCategoryNotFoundError);
437
+ await expect(svc.updateGame({
438
+ id: '00000000-0000-4000-8000-000000000000',
439
+ name: 'X',
440
+ ...ACTOR,
441
+ })).rejects.toBeInstanceOf(GameNotFoundError);
442
+ });
443
+ it('rejects an aggregator the target provider is not mapped on', async () => {
444
+ const created = await seedGame();
445
+ const unmapped = await seedProvider({ slug: 'unmapped-studio', name: 'Unmapped' });
446
+ await db.drizzle.db
447
+ .delete(gameProviderAggregatorMapping)
448
+ .where(eq(gameProviderAggregatorMapping.providerId, unmapped.id));
449
+ const svc = makeService();
450
+ await expect(svc.updateGame({ id: created.id, providerId: unmapped.id, ...ACTOR })).rejects.toBeInstanceOf(GameAggregatorNotMappedError);
451
+ await expect(svc.updateGame({ id: created.id, aggregator: 'everymatrix', ...ACTOR })).rejects.toBeInstanceOf(GameAggregatorNotMappedError);
452
+ await db.drizzle.db
453
+ .insert(gameProviderAggregatorMapping)
454
+ .values({ providerId: unmapped.id, aggregator: 'everymatrix', vendorId: 'vendor-unmapped' });
455
+ const moved = await svc.updateGame({
456
+ id: created.id,
457
+ providerId: unmapped.id,
458
+ aggregator: 'everymatrix',
459
+ ...ACTOR,
460
+ });
461
+ expect(moved).toMatchObject({
462
+ provider: { slug: 'unmapped-studio' },
463
+ aggregator: 'everymatrix',
464
+ });
465
+ });
466
+ it('rejects a taken game slug', async () => {
467
+ const created = await seedGame({ slug: 'game-one' });
468
+ await seedGame({ slug: 'game-two' });
469
+ const svc = makeService();
470
+ await expect(svc.updateGame({ id: created.id, slug: 'game-two', ...ACTOR })).rejects.toBeInstanceOf(GameSlugTakenError);
471
+ });
472
+ });
212
473
  async function seedRound(gameId, userId) {
213
474
  const [row] = await db.drizzle.db
214
475
  .insert(gameRound)