@openora/core 0.4.1-canary.37 → 0.4.1-canary.39
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/dist/.tsbuildinfo +1 -1
- package/dist/admin-console/__tests__/backoffice.router.test.js +67 -51
- package/dist/admin-console/__tests__/backoffice.router.test.js.map +1 -1
- package/dist/admin-console/__tests__/backoffice.service.test.js +1 -13
- package/dist/admin-console/__tests__/backoffice.service.test.js.map +1 -1
- package/dist/analytics/__tests__/analytics.contract.test.js +12 -0
- package/dist/analytics/__tests__/analytics.contract.test.js.map +1 -1
- package/dist/analytics/__tests__/financial-analytics.service.test.js +3 -3
- package/dist/analytics/__tests__/financial-analytics.service.test.js.map +1 -1
- package/dist/analytics/service/financial-analytics.service.d.ts.map +1 -1
- package/dist/analytics/service/financial-analytics.service.js +14 -17
- package/dist/analytics/service/financial-analytics.service.js.map +1 -1
- package/dist/analytics/service/funnel-analytics.service.d.ts.map +1 -1
- package/dist/analytics/service/funnel-analytics.service.js +8 -11
- package/dist/analytics/service/funnel-analytics.service.js.map +1 -1
- package/dist/audit/__tests__/audit.service.test.js +128 -251
- package/dist/audit/__tests__/audit.service.test.js.map +1 -1
- package/dist/casino/gaming/__tests__/gaming.service.test.js +83 -89
- package/dist/casino/gaming/__tests__/gaming.service.test.js.map +1 -1
- package/dist/casino/lobby/__tests__/lobby.cache.test.js +42 -33
- package/dist/casino/lobby/__tests__/lobby.cache.test.js.map +1 -1
- package/dist/cms/__tests__/cms.reads.test.js +59 -80
- package/dist/cms/__tests__/cms.reads.test.js.map +1 -1
- package/dist/cms/__tests__/cms.router.test.js +62 -20
- package/dist/cms/__tests__/cms.router.test.js.map +1 -1
- package/dist/cms/__tests__/cms.service.test.js +116 -14
- package/dist/cms/__tests__/cms.service.test.js.map +1 -1
- package/dist/common/errors/__tests__/errors.mapper.test.d.ts +2 -0
- package/dist/common/errors/__tests__/errors.mapper.test.d.ts.map +1 -0
- package/dist/common/errors/__tests__/errors.mapper.test.js +59 -0
- package/dist/common/errors/__tests__/errors.mapper.test.js.map +1 -0
- package/dist/compliance/__tests__/assert.test.d.ts +2 -0
- package/dist/compliance/__tests__/assert.test.d.ts.map +1 -0
- package/dist/compliance/__tests__/assert.test.js +34 -0
- package/dist/compliance/__tests__/assert.test.js.map +1 -0
- package/dist/compliance/__tests__/compliance.service.test.js +149 -84
- package/dist/compliance/__tests__/compliance.service.test.js.map +1 -1
- package/dist/compliance/__tests__/kyc.service.test.js +247 -155
- package/dist/compliance/__tests__/kyc.service.test.js.map +1 -1
- package/dist/compliance/__tests__/limits.contract.test.d.ts +2 -0
- package/dist/compliance/__tests__/limits.contract.test.d.ts.map +1 -0
- package/dist/compliance/__tests__/limits.contract.test.js +43 -0
- package/dist/compliance/__tests__/limits.contract.test.js.map +1 -0
- package/dist/compliance/__tests__/rg-monitoring.service.test.js +256 -101
- package/dist/compliance/__tests__/rg-monitoring.service.test.js.map +1 -1
- package/dist/compliance/__tests__/rg.contract.test.js +1 -20
- package/dist/compliance/__tests__/rg.contract.test.js.map +1 -1
- package/dist/compliance/__tests__/rg.router.test.js +113 -65
- package/dist/compliance/__tests__/rg.router.test.js.map +1 -1
- package/dist/compliance/__tests__/rg.service.test.js +277 -235
- package/dist/compliance/__tests__/rg.service.test.js.map +1 -1
- package/dist/compliance/service/rg-monitoring.service.d.ts.map +1 -1
- package/dist/compliance/service/rg-monitoring.service.js +11 -8
- package/dist/compliance/service/rg-monitoring.service.js.map +1 -1
- package/dist/contracts/__tests__/compose-contract.test.d.ts +2 -0
- package/dist/contracts/__tests__/compose-contract.test.d.ts.map +1 -0
- package/dist/contracts/__tests__/compose-contract.test.js +27 -0
- package/dist/contracts/__tests__/compose-contract.test.js.map +1 -0
- package/dist/contracts/adapters/__tests__/token.test.d.ts +2 -0
- package/dist/contracts/adapters/__tests__/token.test.d.ts.map +1 -0
- package/dist/contracts/adapters/__tests__/token.test.js +14 -0
- package/dist/contracts/adapters/__tests__/token.test.js.map +1 -0
- package/dist/contracts/schemas/__tests__/events.test.d.ts +2 -0
- package/dist/contracts/schemas/__tests__/events.test.d.ts.map +1 -0
- package/dist/contracts/schemas/__tests__/events.test.js +51 -0
- package/dist/contracts/schemas/__tests__/events.test.js.map +1 -0
- package/dist/contracts/schemas/__tests__/igaming-config.test.d.ts +2 -0
- package/dist/contracts/schemas/__tests__/igaming-config.test.d.ts.map +1 -0
- package/dist/contracts/schemas/__tests__/igaming-config.test.js +38 -0
- package/dist/contracts/schemas/__tests__/igaming-config.test.js.map +1 -0
- package/dist/contracts/schemas/reporting.d.ts +7 -2
- package/dist/contracts/schemas/reporting.d.ts.map +1 -1
- package/dist/contracts/schemas/reporting.js +2 -1
- package/dist/contracts/schemas/reporting.js.map +1 -1
- package/dist/engagement/chat/__tests__/chat.service.test.js +524 -364
- package/dist/engagement/chat/__tests__/chat.service.test.js.map +1 -1
- package/dist/engagement/notifications/__tests__/notifications.service.test.js +125 -68
- package/dist/engagement/notifications/__tests__/notifications.service.test.js.map +1 -1
- package/dist/iam/__tests__/iam.service.test.js +367 -596
- package/dist/iam/__tests__/iam.service.test.js.map +1 -1
- package/dist/pam/identity/__tests__/admin-user-directory.test.js +144 -68
- package/dist/pam/identity/__tests__/admin-user-directory.test.js.map +1 -1
- package/dist/pam/identity/__tests__/identity.rate-limit.test.js +33 -22
- package/dist/pam/identity/__tests__/identity.rate-limit.test.js.map +1 -1
- package/dist/pam/identity/__tests__/identity.service.test.js +217 -298
- package/dist/pam/identity/__tests__/identity.service.test.js.map +1 -1
- package/dist/pam/identity/__tests__/login-enforcement.service.test.js +97 -32
- package/dist/pam/identity/__tests__/login-enforcement.service.test.js.map +1 -1
- package/dist/pam/identity/__tests__/phone-login.service.test.js +163 -134
- package/dist/pam/identity/__tests__/phone-login.service.test.js.map +1 -1
- package/dist/pam/identity/__tests__/rg-guard.test.d.ts +2 -0
- package/dist/pam/identity/__tests__/rg-guard.test.d.ts.map +1 -0
- package/dist/pam/identity/__tests__/rg-guard.test.js +21 -0
- package/dist/pam/identity/__tests__/rg-guard.test.js.map +1 -0
- package/dist/pam/player-management/__tests__/kyc-status-writer.test.js +73 -26
- package/dist/pam/player-management/__tests__/kyc-status-writer.test.js.map +1 -1
- package/dist/pam/player-management/__tests__/player.router.test.js +63 -46
- package/dist/pam/player-management/__tests__/player.router.test.js.map +1 -1
- package/dist/pam/player-management/__tests__/player.service.test.js +207 -111
- package/dist/pam/player-management/__tests__/player.service.test.js.map +1 -1
- package/dist/pam/profile/__tests__/profile.service.test.js +89 -41
- package/dist/pam/profile/__tests__/profile.service.test.js.map +1 -1
- package/dist/pam/shared/__tests__/shared-mappers.test.d.ts +2 -0
- package/dist/pam/shared/__tests__/shared-mappers.test.d.ts.map +1 -0
- package/dist/pam/shared/__tests__/shared-mappers.test.js +55 -0
- package/dist/pam/shared/__tests__/shared-mappers.test.js.map +1 -0
- package/dist/pam/tag/__tests__/get-active-tag-keys.test.js +72 -30
- package/dist/pam/tag/__tests__/get-active-tag-keys.test.js.map +1 -1
- package/dist/pam/tag/__tests__/tag-evaluation.service.test.js +308 -235
- package/dist/pam/tag/__tests__/tag-evaluation.service.test.js.map +1 -1
- package/dist/pam/tag/__tests__/tag-rule.service.test.js +136 -140
- package/dist/pam/tag/__tests__/tag-rule.service.test.js.map +1 -1
- package/dist/pam/tag/__tests__/tag.router.test.js +76 -25
- package/dist/pam/tag/__tests__/tag.router.test.js.map +1 -1
- package/dist/pam/tag/__tests__/tag.service.test.js +224 -247
- package/dist/pam/tag/__tests__/tag.service.test.js.map +1 -1
- package/dist/server/auth/__tests__/admin-guard.test.js +107 -117
- package/dist/server/auth/__tests__/admin-guard.test.js.map +1 -1
- package/dist/server/auth/__tests__/permission-levels.test.d.ts +2 -0
- package/dist/server/auth/__tests__/permission-levels.test.d.ts.map +1 -0
- package/dist/server/auth/__tests__/permission-levels.test.js +89 -0
- package/dist/server/auth/__tests__/permission-levels.test.js.map +1 -0
- package/dist/server/auth/__tests__/session-resolver.test.js +45 -13
- package/dist/server/auth/__tests__/session-resolver.test.js.map +1 -1
- package/dist/server/db/__tests__/query-helpers.test.js +72 -1
- package/dist/server/db/__tests__/query-helpers.test.js.map +1 -1
- package/dist/server/db/outbox/__tests__/relay.test.js +74 -81
- package/dist/server/db/outbox/__tests__/relay.test.js.map +1 -1
- package/dist/server/kernel/__tests__/bullmq-job-queue.test.js +204 -154
- package/dist/server/kernel/__tests__/bullmq-job-queue.test.js.map +1 -1
- package/dist/server/kernel/__tests__/cache.test.js +26 -13
- package/dist/server/kernel/__tests__/cache.test.js.map +1 -1
- package/dist/server/kernel/__tests__/domain-error.test.d.ts +2 -0
- package/dist/server/kernel/__tests__/domain-error.test.d.ts.map +1 -0
- package/dist/server/kernel/__tests__/domain-error.test.js +35 -0
- package/dist/server/kernel/__tests__/domain-error.test.js.map +1 -0
- package/dist/server/kernel/__tests__/error-reporter.test.d.ts +2 -0
- package/dist/server/kernel/__tests__/error-reporter.test.d.ts.map +1 -0
- package/dist/server/kernel/__tests__/error-reporter.test.js +52 -0
- package/dist/server/kernel/__tests__/error-reporter.test.js.map +1 -0
- package/dist/server/kernel/__tests__/event-bus.test.js +60 -20
- package/dist/server/kernel/__tests__/event-bus.test.js.map +1 -1
- package/dist/server/kernel/__tests__/orpc-error-map.test.d.ts +2 -0
- package/dist/server/kernel/__tests__/orpc-error-map.test.d.ts.map +1 -0
- package/dist/server/kernel/__tests__/orpc-error-map.test.js +54 -0
- package/dist/server/kernel/__tests__/orpc-error-map.test.js.map +1 -0
- package/dist/server/kernel/__tests__/platform-config-loader.test.d.ts +2 -0
- package/dist/server/kernel/__tests__/platform-config-loader.test.d.ts.map +1 -0
- package/dist/server/kernel/__tests__/platform-config-loader.test.js +112 -0
- package/dist/server/kernel/__tests__/platform-config-loader.test.js.map +1 -0
- package/dist/server/kernel/__tests__/rate-limiter.test.js +22 -13
- package/dist/server/kernel/__tests__/rate-limiter.test.js.map +1 -1
- package/dist/server/kernel/__tests__/realtime-transport.test.d.ts.map +1 -0
- package/dist/{testing/fakes → server/kernel}/__tests__/realtime-transport.test.js.map +1 -1
- package/dist/server/kernel/__tests__/redis-cache.test.js +34 -34
- package/dist/server/kernel/__tests__/redis-cache.test.js.map +1 -1
- package/dist/server/kernel/__tests__/redis-rate-limiter.test.js +49 -44
- package/dist/server/kernel/__tests__/redis-rate-limiter.test.js.map +1 -1
- package/dist/server/kernel/__tests__/redis-streams-broker.test.js +141 -262
- package/dist/server/kernel/__tests__/redis-streams-broker.test.js.map +1 -1
- package/dist/server/kernel/__tests__/request-context.test.d.ts +2 -0
- package/dist/server/kernel/__tests__/request-context.test.d.ts.map +1 -0
- package/dist/server/kernel/__tests__/request-context.test.js +64 -0
- package/dist/server/kernel/__tests__/request-context.test.js.map +1 -0
- package/dist/server/kernel/__tests__/router-utils.test.d.ts +2 -0
- package/dist/server/kernel/__tests__/router-utils.test.d.ts.map +1 -0
- package/dist/server/kernel/__tests__/router-utils.test.js +80 -0
- package/dist/server/kernel/__tests__/router-utils.test.js.map +1 -0
- package/dist/server/kernel/bullmq-job-queue.d.ts.map +1 -1
- package/dist/server/kernel/bullmq-job-queue.js +8 -2
- package/dist/server/kernel/bullmq-job-queue.js.map +1 -1
- package/dist/server/runtime/__tests__/create-app.test.js +26 -18
- package/dist/server/runtime/__tests__/create-app.test.js.map +1 -1
- package/dist/testing/index.d.ts +2 -6
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +2 -10
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +28 -16
- package/dist/testing/mock.d.ts.map +1 -1
- package/dist/testing/mock.js +58 -37
- package/dist/testing/mock.js.map +1 -1
- package/dist/testing/real-infra.d.ts +47 -0
- package/dist/testing/real-infra.d.ts.map +1 -0
- package/dist/testing/real-infra.js +122 -0
- package/dist/testing/real-infra.js.map +1 -0
- package/dist/wallet/__tests__/admin-reporting.test.js +201 -90
- package/dist/wallet/__tests__/admin-reporting.test.js.map +1 -1
- package/dist/wallet/__tests__/rail-for.test.d.ts +2 -0
- package/dist/wallet/__tests__/rail-for.test.d.ts.map +1 -0
- package/dist/wallet/__tests__/rail-for.test.js +22 -0
- package/dist/wallet/__tests__/rail-for.test.js.map +1 -0
- package/dist/wallet/__tests__/wallet-auto-rule.router.test.js +82 -63
- package/dist/wallet/__tests__/wallet-auto-rule.router.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-auto-withdrawal.test.js +414 -437
- package/dist/wallet/__tests__/wallet-auto-withdrawal.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-commands.service.test.js +116 -155
- package/dist/wallet/__tests__/wallet-commands.service.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-kyc-gate.test.js +63 -42
- package/dist/wallet/__tests__/wallet-kyc-gate.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet-webhook.router.test.js +129 -51
- package/dist/wallet/__tests__/wallet-webhook.router.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet.rate-limit.test.js +74 -21
- package/dist/wallet/__tests__/wallet.rate-limit.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet.router.test.js +56 -36
- package/dist/wallet/__tests__/wallet.router.test.js.map +1 -1
- package/dist/wallet/__tests__/wallet.service.test.js +623 -887
- package/dist/wallet/__tests__/wallet.service.test.js.map +1 -1
- package/package.json +1 -2
- package/dist/casino/lobby/__tests__/lobby.service.test.d.ts +0 -2
- package/dist/casino/lobby/__tests__/lobby.service.test.d.ts.map +0 -1
- package/dist/casino/lobby/__tests__/lobby.service.test.js +0 -11
- package/dist/casino/lobby/__tests__/lobby.service.test.js.map +0 -1
- package/dist/contracts/schemas/__tests__/identity-schemas.test.d.ts +0 -2
- package/dist/contracts/schemas/__tests__/identity-schemas.test.d.ts.map +0 -1
- package/dist/contracts/schemas/__tests__/identity-schemas.test.js +0 -43
- package/dist/contracts/schemas/__tests__/identity-schemas.test.js.map +0 -1
- package/dist/testing/fakes/__tests__/cache.test.d.ts +0 -2
- package/dist/testing/fakes/__tests__/cache.test.d.ts.map +0 -1
- package/dist/testing/fakes/__tests__/cache.test.js +0 -89
- package/dist/testing/fakes/__tests__/cache.test.js.map +0 -1
- package/dist/testing/fakes/__tests__/job-queue.test.d.ts +0 -2
- package/dist/testing/fakes/__tests__/job-queue.test.d.ts.map +0 -1
- package/dist/testing/fakes/__tests__/job-queue.test.js +0 -135
- package/dist/testing/fakes/__tests__/job-queue.test.js.map +0 -1
- package/dist/testing/fakes/__tests__/rate-limiter.test.d.ts +0 -2
- package/dist/testing/fakes/__tests__/rate-limiter.test.d.ts.map +0 -1
- package/dist/testing/fakes/__tests__/rate-limiter.test.js +0 -83
- package/dist/testing/fakes/__tests__/rate-limiter.test.js.map +0 -1
- package/dist/testing/fakes/__tests__/realtime-authorizer.test.d.ts +0 -2
- package/dist/testing/fakes/__tests__/realtime-authorizer.test.d.ts.map +0 -1
- package/dist/testing/fakes/__tests__/realtime-authorizer.test.js +0 -19
- package/dist/testing/fakes/__tests__/realtime-authorizer.test.js.map +0 -1
- package/dist/testing/fakes/__tests__/realtime-transport.test.d.ts.map +0 -1
- package/dist/testing/fakes/cache.d.ts +0 -14
- package/dist/testing/fakes/cache.d.ts.map +0 -1
- package/dist/testing/fakes/cache.js +0 -56
- package/dist/testing/fakes/cache.js.map +0 -1
- package/dist/testing/fakes/event-bus.d.ts +0 -17
- package/dist/testing/fakes/event-bus.d.ts.map +0 -1
- package/dist/testing/fakes/event-bus.js +0 -30
- package/dist/testing/fakes/event-bus.js.map +0 -1
- package/dist/testing/fakes/job-queue.d.ts +0 -40
- package/dist/testing/fakes/job-queue.d.ts.map +0 -1
- package/dist/testing/fakes/job-queue.js +0 -200
- package/dist/testing/fakes/job-queue.js.map +0 -1
- package/dist/testing/fakes/rate-limiter.d.ts +0 -11
- package/dist/testing/fakes/rate-limiter.d.ts.map +0 -1
- package/dist/testing/fakes/rate-limiter.js +0 -39
- package/dist/testing/fakes/rate-limiter.js.map +0 -1
- package/dist/testing/fakes/realtime-authorizer.d.ts +0 -10
- package/dist/testing/fakes/realtime-authorizer.d.ts.map +0 -1
- package/dist/testing/fakes/realtime-authorizer.js +0 -10
- package/dist/testing/fakes/realtime-authorizer.js.map +0 -1
- package/dist/testing/fakes/realtime-transport.d.ts +0 -2
- package/dist/testing/fakes/realtime-transport.d.ts.map +0 -1
- package/dist/testing/fakes/realtime-transport.js +0 -2
- package/dist/testing/fakes/realtime-transport.js.map +0 -1
- /package/dist/{testing/fakes → server/kernel}/__tests__/realtime-transport.test.d.ts +0 -0
- /package/dist/{testing/fakes → server/kernel}/__tests__/realtime-transport.test.js +0 -0
|
@@ -1,38 +1,85 @@
|
|
|
1
|
-
import { describe, it, expect,
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { eq, sql } from 'drizzle-orm';
|
|
4
|
+
import { createTestDb } from '@openora/core/testing';
|
|
5
|
+
import { player } from '@openora/core/pam/schema/profile';
|
|
6
|
+
import { migrate as migrateProfile } from '@openora/core/pam/migrate/profile';
|
|
7
|
+
import { makeEventBus } from '../../../testing/mock.js';
|
|
2
8
|
import { PlayerKycStatusWriter } from '../service/kyc-status-writer.js';
|
|
3
|
-
|
|
4
|
-
function
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return mockDb(builder);
|
|
9
|
+
let db;
|
|
10
|
+
function makeWriter() {
|
|
11
|
+
const events = makeEventBus();
|
|
12
|
+
return { writer: new PlayerKycStatusWriter(db.drizzle, events), events };
|
|
13
|
+
}
|
|
14
|
+
async function seedPlayer(overrides = {}) {
|
|
15
|
+
const [row] = await db.drizzle.db
|
|
16
|
+
.insert(player)
|
|
17
|
+
.values({ userId: randomUUID(), displayName: 'Player', ...overrides })
|
|
18
|
+
.returning();
|
|
19
|
+
return row;
|
|
15
20
|
}
|
|
16
|
-
function
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
return { writer, events };
|
|
21
|
+
async function statusOf(userId) {
|
|
22
|
+
const [row] = await db.drizzle.db.select().from(player).where(eq(player.userId, userId));
|
|
23
|
+
return row?.kycStatus;
|
|
20
24
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
beforeAll(async () => {
|
|
26
|
+
db = await createTestDb([migrateProfile]);
|
|
27
|
+
});
|
|
28
|
+
afterAll(async () => {
|
|
29
|
+
await db.drop();
|
|
30
|
+
});
|
|
31
|
+
beforeEach(async () => {
|
|
32
|
+
await db.drizzle.db.execute(sql `TRUNCATE ${player} RESTART IDENTITY CASCADE`);
|
|
33
|
+
});
|
|
34
|
+
describe('PlayerKycStatusWriter.setStatus (real PG)', () => {
|
|
35
|
+
it('writes the new status and emits the change with its previous value', async () => {
|
|
36
|
+
const { writer, events } = makeWriter();
|
|
37
|
+
const { userId } = await seedPlayer({ kycStatus: 'pending' });
|
|
38
|
+
const actorId = randomUUID();
|
|
39
|
+
await writer.setStatus(userId, 'verified', { actorId, source: 'manual' });
|
|
40
|
+
expect(await statusOf(userId)).toBe('verified');
|
|
25
41
|
expect(events.emit).toHaveBeenCalledWith('compliance.kyc.updated', {
|
|
26
|
-
userId
|
|
27
|
-
actorId
|
|
42
|
+
userId,
|
|
43
|
+
actorId,
|
|
28
44
|
status: 'verified',
|
|
29
45
|
previousStatus: 'pending',
|
|
30
46
|
});
|
|
31
47
|
});
|
|
32
|
-
it('is a no-op when the status is unchanged', async () => {
|
|
33
|
-
const { writer, events } = makeWriter(
|
|
34
|
-
|
|
48
|
+
it('is a silent no-op when the status is unchanged', async () => {
|
|
49
|
+
const { writer, events } = makeWriter();
|
|
50
|
+
const { userId } = await seedPlayer({ kycStatus: 'verified' });
|
|
51
|
+
await writer.setStatus(userId, 'verified', { actorId: null, source: 'vendor' });
|
|
35
52
|
expect(events.emit).not.toHaveBeenCalled();
|
|
36
53
|
});
|
|
54
|
+
it('is a no-op for a user with no player profile', async () => {
|
|
55
|
+
const { writer, events } = makeWriter();
|
|
56
|
+
await writer.setStatus(randomUUID(), 'verified', { actorId: null, source: 'vendor' });
|
|
57
|
+
expect(events.emit).not.toHaveBeenCalled();
|
|
58
|
+
});
|
|
59
|
+
it('leaves other players untouched', async () => {
|
|
60
|
+
const { writer } = makeWriter();
|
|
61
|
+
const target = await seedPlayer({ kycStatus: 'pending' });
|
|
62
|
+
const other = await seedPlayer({ kycStatus: 'pending' });
|
|
63
|
+
await writer.setStatus(target.userId, 'rejected', { actorId: null, source: 'vendor' });
|
|
64
|
+
expect(await statusOf(other.userId)).toBe('pending');
|
|
65
|
+
});
|
|
66
|
+
it('writes on the callers transaction when one is supplied', async () => {
|
|
67
|
+
const { writer, events } = makeWriter();
|
|
68
|
+
const { userId } = await seedPlayer({ kycStatus: 'pending' });
|
|
69
|
+
await db.drizzle.db.transaction(async (tx) => {
|
|
70
|
+
await writer.setStatus(userId, 'verified', { actorId: null, source: 'webhook' }, tx);
|
|
71
|
+
});
|
|
72
|
+
expect(await statusOf(userId)).toBe('verified');
|
|
73
|
+
expect(events.emit).toHaveBeenCalledTimes(1);
|
|
74
|
+
});
|
|
75
|
+
it('rolls the status back with the callers transaction', async () => {
|
|
76
|
+
const { writer } = makeWriter();
|
|
77
|
+
const { userId } = await seedPlayer({ kycStatus: 'pending' });
|
|
78
|
+
await expect(db.drizzle.db.transaction(async (tx) => {
|
|
79
|
+
await writer.setStatus(userId, 'verified', { actorId: null, source: 'webhook' }, tx);
|
|
80
|
+
throw new Error('caller failed after the status write');
|
|
81
|
+
})).rejects.toThrow('caller failed');
|
|
82
|
+
expect(await statusOf(userId)).toBe('pending');
|
|
83
|
+
});
|
|
37
84
|
});
|
|
38
85
|
//# sourceMappingURL=kyc-status-writer.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kyc-status-writer.test.js","sourceRoot":"","sources":["../../../../src/pam/player-management/__tests__/kyc-status-writer.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"kyc-status-writer.test.js","sourceRoot":"","sources":["../../../../src/pam/player-management/__tests__/kyc-status-writer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,57 +1,74 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import {
|
|
1
|
+
import { describe, it, expect, vi, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { eq, sql } from 'drizzle-orm';
|
|
3
4
|
import { call, ORPCError } from '@orpc/server';
|
|
5
|
+
import { createTestDb } from '@openora/core/testing';
|
|
6
|
+
import { user } from '@openora/core/pam/schema/identity';
|
|
7
|
+
import { migrate as migrateIdentity } from '@openora/core/pam/migrate/identity';
|
|
8
|
+
import { player } from '@openora/core/pam/schema/profile';
|
|
9
|
+
import { migrate as migrateProfile } from '@openora/core/pam/migrate/profile';
|
|
10
|
+
import { tag, playerTag } from '@openora/core/pam/schema/tag';
|
|
11
|
+
import { migrate as migrateTag } from '@openora/core/pam/migrate/tag';
|
|
12
|
+
import { mock, makeAdminGuard, testContext } from '../../../testing/mock.js';
|
|
4
13
|
import { createPlayerRouter } from '../router/index.js';
|
|
14
|
+
import { PlayerService } from '../service/player.service.js';
|
|
5
15
|
const CTX = testContext();
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
});
|
|
16
|
+
const CALLER = '44444444-4444-4444-8444-444444444444';
|
|
17
|
+
let db;
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
db = await createTestDb([migrateIdentity, migrateProfile, migrateTag]);
|
|
20
|
+
});
|
|
21
|
+
afterAll(async () => {
|
|
22
|
+
await db.drop();
|
|
23
|
+
});
|
|
24
|
+
beforeEach(async () => {
|
|
25
|
+
await db.drizzle.db.execute(sql `TRUNCATE ${playerTag}, ${tag}, ${player}, ${user} RESTART IDENTITY CASCADE`);
|
|
26
|
+
});
|
|
27
|
+
const guardAllowing = (allow) => makeAdminGuard({ allow, caller: { userId: CALLER } });
|
|
28
|
+
function build(adminGuard) {
|
|
29
|
+
const kycStatusWriter = mock({ setStatus: vi.fn() });
|
|
30
|
+
const service = new PlayerService(db.drizzle, kycStatusWriter);
|
|
31
|
+
return { router: createPlayerRouter(service, adminGuard), kycStatusWriter };
|
|
32
|
+
}
|
|
33
|
+
async function seedPlayer() {
|
|
34
|
+
const [account] = await db.drizzle.db
|
|
35
|
+
.insert(user)
|
|
36
|
+
.values({ name: 'Player', email: `${randomUUID()}@example.com` })
|
|
37
|
+
.returning();
|
|
38
|
+
const [row] = await db.drizzle.db
|
|
39
|
+
.insert(player)
|
|
40
|
+
.values({ userId: account.id, displayName: 'Player' })
|
|
41
|
+
.returning();
|
|
42
|
+
return row;
|
|
16
43
|
}
|
|
17
|
-
function
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
userId: PLAYER_ID,
|
|
21
|
-
displayName: 'Player',
|
|
22
|
-
email: 'p@example.com',
|
|
23
|
-
country: 'MT',
|
|
24
|
-
currency: 'EUR',
|
|
25
|
-
status: 'active',
|
|
26
|
-
kycStatus: 'verified',
|
|
27
|
-
level: 1,
|
|
28
|
-
totalWagered: '0',
|
|
29
|
-
totalDeposits: '0',
|
|
30
|
-
lastSeenAt: null,
|
|
31
|
-
createdAt: '2026-01-01T00:00:00.000Z',
|
|
32
|
-
updatedAt: '2026-01-01T00:00:00.000Z',
|
|
33
|
-
tags: [],
|
|
34
|
-
};
|
|
35
|
-
return mock({ update: vi.fn().mockResolvedValue(playerDto) });
|
|
44
|
+
async function storedPlayer(id) {
|
|
45
|
+
const [row] = await db.drizzle.db.select().from(player).where(eq(player.id, id));
|
|
46
|
+
return row;
|
|
36
47
|
}
|
|
37
48
|
describe('player router update KYC authz', () => {
|
|
38
|
-
it('rejects a kycStatus change without compliance:override-limit', async () => {
|
|
39
|
-
const
|
|
40
|
-
const router =
|
|
41
|
-
await expect(call(router.update, { playerId:
|
|
42
|
-
expect(
|
|
49
|
+
it('rejects a kycStatus change without compliance:override-limit and writes nothing', async () => {
|
|
50
|
+
const seeded = await seedPlayer();
|
|
51
|
+
const { router, kycStatusWriter } = build(guardAllowing(['player:update']));
|
|
52
|
+
await expect(call(router.update, { playerId: seeded.id, kycStatus: 'verified' }, { context: CTX })).rejects.toBeInstanceOf(ORPCError);
|
|
53
|
+
expect(kycStatusWriter.setStatus).not.toHaveBeenCalled();
|
|
54
|
+
expect((await storedPlayer(seeded.id))?.kycStatus).toBe(seeded.kycStatus);
|
|
55
|
+
});
|
|
56
|
+
it('routes a gated kycStatus change through KYC_STATUS_WRITER', async () => {
|
|
57
|
+
const seeded = await seedPlayer();
|
|
58
|
+
const { router, kycStatusWriter } = build(guardAllowing(['player:update', 'compliance:override-limit']));
|
|
59
|
+
await call(router.update, { playerId: seeded.id, kycStatus: 'verified' }, { context: CTX });
|
|
60
|
+
expect(kycStatusWriter.setStatus).toHaveBeenCalledWith(seeded.userId, 'verified', { actorId: CALLER, source: 'manual' }, expect.anything());
|
|
43
61
|
});
|
|
44
|
-
it('
|
|
45
|
-
const
|
|
46
|
-
const router =
|
|
47
|
-
await call(router.update, { playerId:
|
|
48
|
-
expect(
|
|
62
|
+
it('persists a non-KYC update with only player:update', async () => {
|
|
63
|
+
const seeded = await seedPlayer();
|
|
64
|
+
const { router } = build(guardAllowing(['player:update']));
|
|
65
|
+
const result = await call(router.update, { playerId: seeded.id, displayName: 'New' }, { context: CTX });
|
|
66
|
+
expect(result.displayName).toBe('New');
|
|
67
|
+
expect((await storedPlayer(seeded.id))?.displayName).toBe('New');
|
|
49
68
|
});
|
|
50
|
-
it('
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
await call(router.update, { playerId: PLAYER_ID, displayName: 'New' }, { context: CTX });
|
|
54
|
-
expect(svc.update).toHaveBeenCalledOnce();
|
|
69
|
+
it('maps an unknown player to NOT_FOUND', async () => {
|
|
70
|
+
const { router } = build(guardAllowing(['player:update']));
|
|
71
|
+
await expect(call(router.update, { playerId: randomUUID(), displayName: 'New' }, { context: CTX })).rejects.toMatchObject({ code: 'NOT_FOUND' });
|
|
55
72
|
});
|
|
56
73
|
});
|
|
57
74
|
//# sourceMappingURL=player.router.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"player.router.test.js","sourceRoot":"","sources":["../../../../src/pam/player-management/__tests__/player.router.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"player.router.test.js","sourceRoot":"","sources":["../../../../src/pam/player-management/__tests__/player.router.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,144 +1,240 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import { describe, it, expect, vi, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { eq, sql } from 'drizzle-orm';
|
|
4
|
+
import { createTestDb } from '@openora/core/testing';
|
|
5
|
+
import { user } from '@openora/core/pam/schema/identity';
|
|
6
|
+
import { migrate as migrateIdentity } from '@openora/core/pam/migrate/identity';
|
|
7
|
+
import { player } from '@openora/core/pam/schema/profile';
|
|
8
|
+
import { migrate as migrateProfile } from '@openora/core/pam/migrate/profile';
|
|
9
|
+
import { tag, playerTag } from '@openora/core/pam/schema/tag';
|
|
10
|
+
import { migrate as migrateTag } from '@openora/core/pam/migrate/tag';
|
|
11
|
+
import { mock } from '../../../testing/mock.js';
|
|
12
|
+
import { PlayerService, PlayerNotFoundError, DuplicateEmailError, } from '../service/player.service.js';
|
|
13
|
+
let db;
|
|
14
|
+
function makeService(writer = { setStatus: vi.fn() }) {
|
|
15
|
+
const kycStatusWriter = mock(writer);
|
|
16
|
+
return { svc: new PlayerService(db.drizzle, kycStatusWriter), kycStatusWriter };
|
|
14
17
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (prop === 'then') {
|
|
22
|
-
const result = results[callIdx++ % results.length];
|
|
23
|
-
return (res) => res(result);
|
|
24
|
-
}
|
|
25
|
-
if (prop === 'where') {
|
|
26
|
-
return (clause) => {
|
|
27
|
-
captured.where.push(clause);
|
|
28
|
-
return proxy;
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
return () => proxy;
|
|
32
|
-
},
|
|
33
|
-
apply: () => proxy,
|
|
34
|
-
});
|
|
35
|
-
return { proxy, captured };
|
|
18
|
+
async function seedUser(overrides = {}) {
|
|
19
|
+
const [row] = await db.drizzle.db
|
|
20
|
+
.insert(user)
|
|
21
|
+
.values({ name: 'Player', email: `${randomUUID()}@example.com`, ...overrides })
|
|
22
|
+
.returning();
|
|
23
|
+
return row;
|
|
36
24
|
}
|
|
37
|
-
function
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
async function seedPlayer(userId, overrides = {}) {
|
|
26
|
+
const [row] = await db.drizzle.db
|
|
27
|
+
.insert(player)
|
|
28
|
+
.values({ userId, displayName: 'Player', ...overrides })
|
|
29
|
+
.returning();
|
|
30
|
+
return row;
|
|
41
31
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
32
|
+
async function seedPlayerWithUser(userOverrides = {}, playerOverrides = {}) {
|
|
33
|
+
const account = await seedUser(userOverrides);
|
|
34
|
+
const row = await seedPlayer(account.id, playerOverrides);
|
|
35
|
+
return { account, player: row };
|
|
36
|
+
}
|
|
37
|
+
async function seedTag(key) {
|
|
38
|
+
const [row] = await db.drizzle.db.insert(tag).values({ key }).returning();
|
|
39
|
+
return row;
|
|
40
|
+
}
|
|
41
|
+
async function assignTag(playerId, tagId) {
|
|
42
|
+
await db.drizzle.db.insert(playerTag).values({
|
|
43
|
+
playerId,
|
|
44
|
+
tagId,
|
|
45
|
+
assignReason: 'test',
|
|
46
|
+
assignActor: 'manual',
|
|
47
|
+
assignActorUserId: null,
|
|
48
48
|
});
|
|
49
|
+
}
|
|
50
|
+
async function rowById(playerId) {
|
|
51
|
+
const [row] = await db.drizzle.db.select().from(player).where(eq(player.id, playerId));
|
|
52
|
+
return row;
|
|
53
|
+
}
|
|
54
|
+
beforeAll(async () => {
|
|
55
|
+
db = await createTestDb([migrateIdentity, migrateProfile, migrateTag]);
|
|
56
|
+
});
|
|
57
|
+
afterAll(async () => {
|
|
58
|
+
await db.drop();
|
|
59
|
+
});
|
|
60
|
+
beforeEach(async () => {
|
|
61
|
+
await db.drizzle.db.execute(sql `TRUNCATE ${playerTag}, ${tag}, ${player}, ${user} RESTART IDENTITY CASCADE`);
|
|
49
62
|
});
|
|
50
|
-
describe('PlayerService.remove', () => {
|
|
51
|
-
function makeRemoveService(existing) {
|
|
52
|
-
const setArgs = [];
|
|
53
|
-
const update = vi.fn(() => ({
|
|
54
|
-
set: vi.fn((patch) => {
|
|
55
|
-
setArgs.push(patch);
|
|
56
|
-
return { where: vi.fn(() => Promise.resolve()) };
|
|
57
|
-
}),
|
|
58
|
-
}));
|
|
59
|
-
const db = { select: vi.fn(() => chain(existing)), update };
|
|
60
|
-
const svc = new PlayerService({ db }, { setStatus: vi.fn() });
|
|
61
|
-
return { svc, db, setArgs };
|
|
62
|
-
}
|
|
63
|
+
describe('PlayerService.remove (real PG)', () => {
|
|
63
64
|
it('soft-deletes by closing the player, never deletes the row', async () => {
|
|
64
|
-
const { svc
|
|
65
|
-
const
|
|
65
|
+
const { svc } = makeService();
|
|
66
|
+
const { player: seeded } = await seedPlayerWithUser();
|
|
67
|
+
const result = await svc.remove(seeded.id);
|
|
66
68
|
expect(result).toEqual({ success: true });
|
|
67
|
-
expect(
|
|
68
|
-
expect(db.delete).toBeUndefined();
|
|
69
|
+
expect(await rowById(seeded.id)).toMatchObject({ status: 'closed' });
|
|
69
70
|
});
|
|
70
71
|
it('throws PlayerNotFoundError when the player does not exist', async () => {
|
|
71
|
-
const { svc } =
|
|
72
|
-
await expect(svc.remove(
|
|
72
|
+
const { svc } = makeService();
|
|
73
|
+
await expect(svc.remove(randomUUID())).rejects.toBeInstanceOf(PlayerNotFoundError);
|
|
73
74
|
});
|
|
74
75
|
});
|
|
75
|
-
describe('PlayerService.getByUserId', () => {
|
|
76
|
+
describe('PlayerService.getByUserId (real PG)', () => {
|
|
77
|
+
it('returns the player enriched with the identity email', async () => {
|
|
78
|
+
const { svc } = makeService();
|
|
79
|
+
const { account, player: seeded } = await seedPlayerWithUser({ email: 'jordan@example.com' });
|
|
80
|
+
const result = await svc.getByUserId(account.id);
|
|
81
|
+
expect(result).toMatchObject({
|
|
82
|
+
id: seeded.id,
|
|
83
|
+
userId: account.id,
|
|
84
|
+
email: 'jordan@example.com',
|
|
85
|
+
});
|
|
86
|
+
});
|
|
76
87
|
it('throws PlayerNotFoundError when no player owns the userId', async () => {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
await expect(svc.getByUserId('u-missing')).rejects.toThrow(PlayerNotFoundError);
|
|
88
|
+
const { svc } = makeService();
|
|
89
|
+
await expect(svc.getByUserId(randomUUID())).rejects.toBeInstanceOf(PlayerNotFoundError);
|
|
80
90
|
});
|
|
81
91
|
});
|
|
82
|
-
describe('PlayerService.registrationsOverTime', () => {
|
|
92
|
+
describe('PlayerService.registrationsOverTime (real PG)', () => {
|
|
83
93
|
it('returns one zero-filled bucket per day in the window', async () => {
|
|
84
|
-
const svc = makeService(
|
|
94
|
+
const { svc } = makeService();
|
|
85
95
|
const points = await svc.registrationsOverTime(7);
|
|
86
96
|
expect(points).toHaveLength(7);
|
|
87
97
|
expect(points.every((p) => p.count === 0)).toBe(true);
|
|
88
98
|
const dates = points.map((p) => p.date);
|
|
89
99
|
expect(new Set(dates).size).toBe(7);
|
|
90
100
|
expect(dates).toEqual([...dates].sort());
|
|
91
|
-
expect(dates[0]).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
|
92
101
|
});
|
|
93
|
-
it('places
|
|
102
|
+
it('places each days registrations into its matching bucket', async () => {
|
|
103
|
+
const { svc } = makeService();
|
|
94
104
|
const todayKey = new Date().toISOString().slice(0, 10);
|
|
95
|
-
|
|
105
|
+
await seedPlayer(randomUUID(), { createdAt: new Date() });
|
|
106
|
+
await seedPlayer(randomUUID(), { createdAt: new Date() });
|
|
107
|
+
await seedPlayer(randomUUID(), { createdAt: new Date() });
|
|
96
108
|
const points = await svc.registrationsOverTime(7);
|
|
97
109
|
const today = points.find((p) => p.date === todayKey);
|
|
98
110
|
expect(today?.count).toBe(3);
|
|
99
111
|
expect(points.filter((p) => p.date !== todayKey).every((p) => p.count === 0)).toBe(true);
|
|
100
112
|
});
|
|
101
113
|
});
|
|
102
|
-
describe('PlayerService.list
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
114
|
+
describe('PlayerService.list (real PG)', () => {
|
|
115
|
+
it('filters by status', async () => {
|
|
116
|
+
const { svc } = makeService();
|
|
117
|
+
await seedPlayerWithUser({}, { status: 'active' });
|
|
118
|
+
await seedPlayerWithUser({}, { status: 'suspended' });
|
|
119
|
+
const { items, total } = await svc.list({ page: 1, limit: 20, status: 'suspended' });
|
|
120
|
+
expect(total).toBe(1);
|
|
121
|
+
expect(items[0]?.status).toBe('suspended');
|
|
122
|
+
});
|
|
123
|
+
it('matches players by a display name substring', async () => {
|
|
124
|
+
const { svc } = makeService();
|
|
125
|
+
await seedPlayerWithUser({}, { displayName: 'Alice Anderson' });
|
|
126
|
+
await seedPlayerWithUser({}, { displayName: 'Bob Baker' });
|
|
127
|
+
const { items, total } = await svc.list({ page: 1, limit: 20, search: 'ander' });
|
|
128
|
+
expect(total).toBe(1);
|
|
129
|
+
expect(items[0]?.displayName).toBe('Alice Anderson');
|
|
130
|
+
});
|
|
131
|
+
it('matches players by an email substring', async () => {
|
|
132
|
+
const { svc } = makeService();
|
|
133
|
+
await seedPlayerWithUser({ email: 'searchable-target@example.com' });
|
|
134
|
+
await seedPlayerWithUser({ email: 'someone-else@example.com' });
|
|
135
|
+
const { items, total } = await svc.list({ page: 1, limit: 20, search: 'searchable-target' });
|
|
136
|
+
expect(total).toBe(1);
|
|
137
|
+
expect(items[0]?.email).toBe('searchable-target@example.com');
|
|
138
|
+
});
|
|
139
|
+
it('restricts results to players carrying any of the given tags, excluding removed assignments', async () => {
|
|
140
|
+
const { svc } = makeService();
|
|
141
|
+
const vip = await seedTag('vip');
|
|
142
|
+
const highRisk = await seedTag('high_risk');
|
|
143
|
+
const { player: tagged } = await seedPlayerWithUser();
|
|
144
|
+
const { player: removedTag } = await seedPlayerWithUser();
|
|
145
|
+
const { player: untagged } = await seedPlayerWithUser();
|
|
146
|
+
await assignTag(tagged.id, vip.id);
|
|
147
|
+
await assignTag(removedTag.id, highRisk.id);
|
|
148
|
+
await db.drizzle.db
|
|
149
|
+
.update(playerTag)
|
|
150
|
+
.set({ removedAt: new Date() })
|
|
151
|
+
.where(eq(playerTag.playerId, removedTag.id));
|
|
152
|
+
const { items, total } = await svc.list({ page: 1, limit: 20, tags: ['vip', 'high_risk'] });
|
|
153
|
+
expect(total).toBe(1);
|
|
154
|
+
expect(items.map((i) => i.id)).toEqual([tagged.id]);
|
|
155
|
+
expect(items.map((i) => i.id)).not.toContain(untagged.id);
|
|
156
|
+
});
|
|
157
|
+
it('keeps the total across the whole filtered set, not just the returned page', async () => {
|
|
158
|
+
const { svc } = makeService();
|
|
159
|
+
await seedPlayerWithUser({}, { status: 'active' });
|
|
160
|
+
await seedPlayerWithUser({}, { status: 'active' });
|
|
161
|
+
await seedPlayerWithUser({}, { status: 'active' });
|
|
162
|
+
await seedPlayerWithUser({}, { status: 'suspended' });
|
|
163
|
+
const { items, total } = await svc.list({ page: 2, limit: 2, status: 'active' });
|
|
164
|
+
expect(total).toBe(3);
|
|
165
|
+
expect(items).toHaveLength(1);
|
|
166
|
+
});
|
|
167
|
+
it('orders by the requested sort field and direction', async () => {
|
|
168
|
+
const { svc } = makeService();
|
|
169
|
+
await seedPlayerWithUser({}, { displayName: 'Charlie' });
|
|
170
|
+
await seedPlayerWithUser({}, { displayName: 'Alice' });
|
|
171
|
+
await seedPlayerWithUser({}, { displayName: 'Bob' });
|
|
172
|
+
const { items } = await svc.list({
|
|
173
|
+
page: 1,
|
|
174
|
+
limit: 20,
|
|
175
|
+
sortBy: 'displayName',
|
|
176
|
+
sortOrder: 'asc',
|
|
177
|
+
});
|
|
178
|
+
expect(items.map((i) => i.displayName)).toEqual(['Alice', 'Bob', 'Charlie']);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
describe('PlayerService.get / getExtended (real PG)', () => {
|
|
182
|
+
it('returns the player with its assigned tags', async () => {
|
|
183
|
+
const { svc } = makeService();
|
|
184
|
+
const vip = await seedTag('vip');
|
|
185
|
+
const { player: seeded } = await seedPlayerWithUser();
|
|
186
|
+
await assignTag(seeded.id, vip.id);
|
|
187
|
+
const result = await svc.get(seeded.id);
|
|
188
|
+
expect(result.tags).toEqual(['vip']);
|
|
189
|
+
});
|
|
190
|
+
it('throws PlayerNotFoundError for an unknown id', async () => {
|
|
191
|
+
const { svc } = makeService();
|
|
192
|
+
await expect(svc.get(randomUUID())).rejects.toBeInstanceOf(PlayerNotFoundError);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
describe('PlayerService.update (real PG)', () => {
|
|
196
|
+
it('persists simple field changes and returns the enriched row', async () => {
|
|
197
|
+
const { svc } = makeService();
|
|
198
|
+
const { player: seeded, account } = await seedPlayerWithUser();
|
|
199
|
+
const result = await svc.update(seeded.id, { displayName: 'Renamed', level: 5 }, account.id);
|
|
200
|
+
expect(result).toMatchObject({ displayName: 'Renamed', level: 5 });
|
|
201
|
+
expect(await rowById(seeded.id)).toMatchObject({ displayName: 'Renamed', level: 5 });
|
|
202
|
+
});
|
|
203
|
+
it('throws DuplicateEmailError when the new email is already used by a different user', async () => {
|
|
204
|
+
const { svc } = makeService();
|
|
205
|
+
const taken = await seedUser({ email: 'taken@example.com' });
|
|
206
|
+
const { player: seeded, account } = await seedPlayerWithUser();
|
|
207
|
+
await expect(svc.update(seeded.id, { email: taken.email }, account.id)).rejects.toBeInstanceOf(DuplicateEmailError);
|
|
208
|
+
});
|
|
209
|
+
it('delegates a kycStatus change to the KYC_STATUS_WRITER on the same transaction', async () => {
|
|
210
|
+
const setStatus = vi.fn(async () => undefined);
|
|
211
|
+
const { svc } = makeService({ setStatus });
|
|
212
|
+
const { player: seeded, account } = await seedPlayerWithUser({}, { kycStatus: 'pending' });
|
|
213
|
+
await svc.update(seeded.id, { kycStatus: 'verified' }, account.id);
|
|
214
|
+
expect(setStatus).toHaveBeenCalledWith(account.id, 'verified', { actorId: account.id, source: 'manual' }, expect.anything());
|
|
215
|
+
});
|
|
216
|
+
it('does not call the KYC_STATUS_WRITER when kycStatus is unchanged', async () => {
|
|
217
|
+
const setStatus = vi.fn(async () => undefined);
|
|
218
|
+
const { svc } = makeService({ setStatus });
|
|
219
|
+
const { player: seeded, account } = await seedPlayerWithUser({}, { kycStatus: 'verified' });
|
|
220
|
+
await svc.update(seeded.id, { kycStatus: 'verified' }, account.id);
|
|
221
|
+
expect(setStatus).not.toHaveBeenCalled();
|
|
222
|
+
});
|
|
223
|
+
it('rolls the whole update back when the KYC_STATUS_WRITER throws', async () => {
|
|
224
|
+
const setStatus = vi.fn(async () => {
|
|
225
|
+
throw new Error('writer down');
|
|
226
|
+
});
|
|
227
|
+
const { svc } = makeService({ setStatus });
|
|
228
|
+
const { player: seeded, account } = await seedPlayerWithUser({}, { displayName: 'Original', kycStatus: 'pending' });
|
|
229
|
+
await expect(svc.update(seeded.id, { displayName: 'Renamed', kycStatus: 'verified' }, account.id)).rejects.toThrow('writer down');
|
|
230
|
+
expect(await rowById(seeded.id)).toMatchObject({
|
|
231
|
+
displayName: 'Original',
|
|
109
232
|
kycStatus: 'pending',
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
updatedAt: new Date(),
|
|
116
|
-
},
|
|
117
|
-
email: 'p@test.com',
|
|
118
|
-
tags: [],
|
|
119
|
-
};
|
|
120
|
-
it('applies a WHERE clause when tags are provided', async () => {
|
|
121
|
-
const { proxy, captured } = makeCapturingChain([[emptyPlayerRow], [{ n: 0 }]]);
|
|
122
|
-
const db = { select: vi.fn(() => proxy) };
|
|
123
|
-
const svc = new PlayerService({ db }, { setStatus: vi.fn() });
|
|
124
|
-
await svc.list({ page: 1, limit: 20, tags: ['self_excluded', 'kyc_rejected'] }).catch(() => { });
|
|
125
|
-
// At least one .where() call must have received a non-undefined clause
|
|
126
|
-
expect(captured.where.some((c) => c !== undefined)).toBe(true);
|
|
127
|
-
});
|
|
128
|
-
it('applies no WHERE clause when tags are absent', async () => {
|
|
129
|
-
const { proxy, captured } = makeCapturingChain([[emptyPlayerRow], [{ n: 0 }]]);
|
|
130
|
-
const db = { select: vi.fn(() => proxy) };
|
|
131
|
-
const svc = new PlayerService({ db }, { setStatus: vi.fn() });
|
|
132
|
-
await svc.list({ page: 1, limit: 20 }).catch(() => { });
|
|
133
|
-
// All .where() calls should receive undefined when no filter is active
|
|
134
|
-
expect(captured.where.every((c) => c === undefined)).toBe(true);
|
|
135
|
-
});
|
|
136
|
-
it('applies no WHERE clause when tags is an empty array', async () => {
|
|
137
|
-
const { proxy, captured } = makeCapturingChain([[emptyPlayerRow], [{ n: 0 }]]);
|
|
138
|
-
const db = { select: vi.fn(() => proxy) };
|
|
139
|
-
const svc = new PlayerService({ db }, { setStatus: vi.fn() });
|
|
140
|
-
await svc.list({ page: 1, limit: 20, tags: [] }).catch(() => { });
|
|
141
|
-
expect(captured.where.every((c) => c === undefined)).toBe(true);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
it('throws PlayerNotFoundError for an unknown id', async () => {
|
|
236
|
+
const { svc } = makeService();
|
|
237
|
+
await expect(svc.update(randomUUID(), { displayName: 'X' }, randomUUID())).rejects.toBeInstanceOf(PlayerNotFoundError);
|
|
142
238
|
});
|
|
143
239
|
});
|
|
144
240
|
//# sourceMappingURL=player.service.test.js.map
|