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>
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
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 } });
|
||||
}
|
||||
Reference in New Issue
Block a user