Files
thebet365/apps/api/src/domains/operations/smoke-tests/smoke-test.bet-flow.fixture.ts
Mars d5e7c8edb3 feat: add smoke tests, agent credit ledger, and player cashback page
Introduce admin smoke-test suite with API probes, agent credit transaction history, and player cashback records; fix SmokeTestModule DI and polish admin/player UI assets.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 16:05:48 +08:00

216 lines
6.3 KiB
TypeScript

import { Decimal } from '@prisma/client/runtime/library';
import type { PrismaService } from '../../../shared/prisma/prisma.service';
import type { WalletService } from '../../ledger/wallet.service';
export interface BetFlowFixtureIds {
runId: string;
operatorId: bigint;
playerId: bigint;
agentId?: bigint;
matchId: bigint;
marketId: bigint;
homeSelectionId: bigint;
homeOddsVersion: bigint;
drawSelectionId: bigint;
drawOddsVersion: bigint;
awaySelectionId: bigint;
awayOddsVersion: bigint;
leagueId: bigint;
homeTeamId: bigint;
awayTeamId: bigint;
}
export async function createBetFlowFixture(
prisma: PrismaService,
wallet: WalletService,
opts?: { initialBalance?: number; withAgent?: boolean },
): Promise<BetFlowFixtureIds> {
const runId = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
const operator = await prisma.user.create({
data: {
username: `smoke_op_${runId}`,
userType: 'ADMIN',
auth: { create: { passwordHash: 'smoke-test' } },
},
});
const league = await prisma.league.create({
data: {
code: `SMK_L_${runId}`,
sportType: 'FOOTBALL',
},
});
const homeTeam = await prisma.team.create({
data: { code: `SMK_H_${runId}`, sportType: 'FOOTBALL' },
});
const awayTeam = await prisma.team.create({
data: { code: `SMK_A_${runId}`, sportType: 'FOOTBALL' },
});
const startTime = new Date(Date.now() + 48 * 60 * 60 * 1000);
const match = await prisma.match.create({
data: {
sportType: 'FOOTBALL',
leagueId: league.id,
homeTeamId: homeTeam.id,
awayTeamId: awayTeam.id,
startTime,
status: 'PUBLISHED',
publishTime: new Date(),
},
});
const market = await prisma.market.create({
data: {
matchId: match.id,
marketType: 'FT_1X2',
period: 'FT',
status: 'OPEN',
selections: {
create: [
{
selectionCode: 'HOME',
selectionName: 'Home',
odds: new Decimal('2.00'),
oddsVersion: BigInt(1),
status: 'OPEN',
sortOrder: 0,
},
{
selectionCode: 'DRAW',
selectionName: 'Draw',
odds: new Decimal('3.00'),
oddsVersion: BigInt(1),
status: 'OPEN',
sortOrder: 1,
},
{
selectionCode: 'AWAY',
selectionName: 'Away',
odds: new Decimal('4.00'),
oddsVersion: BigInt(1),
status: 'OPEN',
sortOrder: 2,
},
],
},
},
include: { selections: true },
});
let agentId: bigint | undefined;
if (opts?.withAgent) {
const agent = await prisma.user.create({
data: {
username: `smoke_ag_${runId}`,
userType: 'AGENT',
auth: { create: { passwordHash: 'smoke-test' } },
agentProfile: {
create: { level: 1, creditLimit: new Decimal(100000) },
},
},
});
await prisma.agentClosure.create({
data: { ancestorId: agent.id, descendantId: agent.id, depth: 0 },
});
agentId = agent.id;
}
const player = await prisma.user.create({
data: {
username: `smoke_pl_${runId}`,
userType: 'PLAYER',
parentId: agentId,
auth: { create: { passwordHash: 'smoke-test' } },
wallet: { create: { currency: 'USD' } },
},
});
const balance = opts?.initialBalance ?? 1000;
if (balance > 0) {
await wallet.deposit(player.id, balance, operator.id, 'smoke test seed');
}
const home = market.selections.find((s) => s.selectionCode === 'HOME')!;
const draw = market.selections.find((s) => s.selectionCode === 'DRAW')!;
const away = market.selections.find((s) => s.selectionCode === 'AWAY')!;
return {
runId,
operatorId: operator.id,
playerId: player.id,
agentId,
matchId: match.id,
marketId: market.id,
homeSelectionId: home.id,
homeOddsVersion: home.oddsVersion,
drawSelectionId: draw.id,
drawOddsVersion: draw.oddsVersion,
awaySelectionId: away.id,
awayOddsVersion: away.oddsVersion,
leagueId: league.id,
homeTeamId: homeTeam.id,
awayTeamId: awayTeam.id,
};
}
export async function teardownBetFlowFixture(
prisma: PrismaService,
fx: BetFlowFixtureIds,
): Promise<void> {
const bets = await prisma.bet.findMany({
where: { userId: fx.playerId },
select: { id: true },
});
const betIds = bets.map((b) => b.id);
const batches = await prisma.settlementBatch.findMany({
where: { matchId: fx.matchId },
select: { id: true },
});
const batchIds = batches.map((b) => b.id);
if (batchIds.length) {
await prisma.settlementItem.deleteMany({ where: { batchId: { in: batchIds } } });
await prisma.settlementBatch.deleteMany({ where: { id: { in: batchIds } } });
}
if (betIds.length) {
await prisma.betSelection.deleteMany({ where: { betId: { in: betIds } } });
await prisma.bet.deleteMany({ where: { id: { in: betIds } } });
}
await prisma.walletTransaction.deleteMany({ where: { userId: fx.playerId } });
await prisma.wallet.deleteMany({ where: { userId: fx.playerId } });
await prisma.matchScore.deleteMany({ where: { matchId: fx.matchId } });
const markets = await prisma.market.findMany({
where: { matchId: fx.matchId },
select: { id: true },
});
const marketIds = markets.map((m) => m.id);
if (marketIds.length) {
await prisma.marketSelection.deleteMany({ where: { marketId: { in: marketIds } } });
await prisma.market.deleteMany({ where: { id: { in: marketIds } } });
}
await prisma.match.deleteMany({ where: { id: fx.matchId } });
if (fx.agentId) {
await prisma.agentClosure.deleteMany({
where: { OR: [{ ancestorId: fx.agentId }, { descendantId: fx.agentId }] },
});
await prisma.agentProfile.deleteMany({ where: { userId: fx.agentId } });
await prisma.userAuth.deleteMany({ where: { userId: fx.agentId } });
await prisma.user.deleteMany({ where: { id: fx.agentId } });
}
await prisma.userAuth.deleteMany({
where: { userId: { in: [fx.playerId, fx.operatorId] } },
});
await prisma.user.deleteMany({ where: { id: { in: [fx.playerId, fx.operatorId] } } });
await prisma.team.deleteMany({ where: { id: { in: [fx.homeTeamId, fx.awayTeamId] } } });
await prisma.league.deleteMany({ where: { id: fx.leagueId } });
}