feat: 添加E2E测试环境配置支持
Some checks failed
lotterLaravel CI / test (push) Has been cancelled

添加LOTTERY_E2E环境变量来控制E2E测试相关功能,
包括绕过验证码、登录限制和钱包API URL验证,
同时更新composer.json以包含E2E专用的提供者和服务。
This commit is contained in:
2026-06-18 14:42:22 +08:00
parent 6b2ea39ea1
commit 6ec9634704
45 changed files with 3702 additions and 6 deletions

View File

@@ -0,0 +1,129 @@
/**
* 开奖 → 结算 → 派彩 确定性流水线poll + tick避免 test.skip
*/
import { expect, type APIRequestContext } from '@playwright/test';
import {
buildAllResultItems,
finishDrawCooldown,
forceCloseDraw,
resolveDrawId,
tickDraws,
waitForDrawStatus,
} from '../_helper';
const CURRENCY = (process.env.LOTTERY_DEFAULT_CURRENCY ?? 'NPR').toUpperCase();
export interface DrawSettlementResult {
drawNo: string;
drawId: number;
batchId: number;
balanceBefore?: number;
balanceAfter?: number;
}
export async function runWalletDrawSettlement(params: {
adminToken: string;
playerToken: string;
drawNo: string;
betNumber?: string;
betAmount?: number;
balanceBefore?: number;
expectWalletIncrease?: boolean;
}): Promise<DrawSettlementResult> {
const drawNo = params.drawNo;
const betNumber = params.betNumber ?? '1234';
const betAmount = params.betAmount ?? 10_000;
const player = await pwPlayerCtx(params.playerToken);
let balanceBefore: number | undefined;
if (params.expectWalletIncrease !== false) {
balanceBefore =
params.balanceBefore ??
Number((await (await player.get('/api/v1/wallet/balance')).json()).data.balance);
}
const place = await player.post('/api/v1/ticket/place', {
data: {
draw_id: drawNo,
currency_code: CURRENCY,
client_trace_id: `e2e-settle-${Date.now()}`,
lines: [{ number: betNumber, play_code: 'straight', amount: betAmount }],
},
});
expect(place.ok(), `place status=${place.status()}`).toBeTruthy();
const placeBody = (await place.json()) as any;
expect(placeBody.code).toBe(0);
await player.dispose();
await forceCloseDraw(drawNo);
await waitForDrawStatus(drawNo, ['closed', 'review', 'cooldown', 'settling', 'settled'], 15);
const admin = await pwAdminCtx(params.adminToken);
const drawId = await resolveDrawId(admin, drawNo);
const store = await admin.post(`/api/v1/admin/draws/${drawId}/result-batches`, {
data: { items: buildAllResultItems(betNumber) },
});
expect(store.ok(), `store batch status=${store.status()}`).toBeTruthy();
const storeBody = (await store.json()) as any;
expect(storeBody.code).toBe(0);
const batchId: number = storeBody.data.batch.id;
const pub = await admin.post(`/api/v1/admin/draws/${drawId}/result-batches/${batchId}/publish`);
expect(pub.ok(), `publish status=${pub.status()}`).toBeTruthy();
const pubBody = (await pub.json()) as any;
expect(pubBody.code).toBe(0);
await finishDrawCooldown(drawNo);
await waitForDrawStatus(drawNo, ['settling', 'settled'], 25);
const insp = await waitForDrawStatus(drawNo, ['settled'], 25);
if (String(insp.data?.status ?? insp.status) !== 'settled') {
const settle = await admin.post(`/api/v1/admin/draws/${drawId}/settlement/run`);
const settleBody = (await settle.json()) as any;
if (settleBody.code !== 0) {
await tickDraws();
}
await waitForDrawStatus(drawNo, ['settled'], 25);
}
const list = await admin.get(`/api/v1/admin/settlement-batches?size=20`);
const lb = (await list.json()) as any;
const ourBatch = lb.data.items.find((b: any) => b.draw_id === drawId);
if (ourBatch && ourBatch.status === 'pending_review') {
await admin.post(`/api/v1/admin/settlement-batches/${ourBatch.id}/approve`, {
data: { remark: 'e2e approve' },
});
await admin.post(`/api/v1/admin/settlement-batches/${ourBatch.id}/payout`);
} else {
await tickDraws();
await waitForDrawStatus(drawNo, ['settled'], 15);
}
await admin.dispose();
let balanceAfter: number | undefined;
if (params.expectWalletIncrease !== false) {
const player2 = await pwPlayerCtx(params.playerToken);
const bal1 = (await (await player2.get('/api/v1/wallet/balance')).json()) as any;
balanceAfter = Number(bal1.data.balance);
await player2.dispose();
}
return { drawNo, drawId, batchId, balanceBefore, balanceAfter };
}
async function pwAdminCtx(token: string): Promise<APIRequestContext> {
const { request: pwRequest } = await import('@playwright/test');
return pwRequest.newContext({
extraHTTPHeaders: { Authorization: `Bearer ${token}` },
});
}
async function pwPlayerCtx(token: string): Promise<APIRequestContext> {
const { request: pwRequest } = await import('@playwright/test');
return pwRequest.newContext({
extraHTTPHeaders: { Authorization: `Bearer ${token}` },
});
}