Files
lotteryLaravel/e2e/README.md
kang 3f29229499
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled
fix: 部分收付累计释额并扩展结算 E2E 与 CI
账期收付改为按累计已付同步 credit_ledger,修复多笔 partial_paid 与坏账核销重复释额;新增 API E2E(占成、权限、部分收付/坏账)与 GitHub Actions e2e-api job。

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

226 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# lotterLaravel E2E 测试
> 端到端测试,**真实** Postgres + Redis + Laravel + Reverb与 Feature 测试SQLite 内存库 + mock解耦。
## 与 Feature 测试的边界
| 维度 | Feature | E2E这套 |
|------|---------|------------|
| 数据库 | SQLite `:memory:` + `RefreshDatabase`(事务回滚) | 真 PGdocker compose 15432事务落库 |
| Redis | `array` 驱动(不真连) | 真 Redis16379Lua/广播全真实 |
| 队列 | `sync`(同步执行) | `redis`(异步,启 `queue:work` |
| 鉴权 | `actingAs` 直接注入 | 真 HTTP 调 `auth/login` 拿 token |
| 广播 | `Event::fake()` 拦截 | 真走 Reverb8080 |
| 入口 | `app()->handle($request)` | 真 `php artisan serve`8000 |
| 时延 | 数百 ms | 数十秒(启服务、跑 migrate |
| 跑哪 | 每次 push / PR | 上线前 + 重大改动后 |
**业务逻辑**交给 Feature**部署/启动/集成**问题归 e2e。
## 目录结构
```
e2e/
├── docker-compose.yml # postgres + redis仅 e2e 用)
├── .env.e2e # 模板 .env
├── package.json # playwright 依赖
├── playwright.config.ts
├── tests/ # 测试用例API mode
│ ├── fixtures.ts # 共享 HTTP 客户端 / login / 拿 draw
│ ├── api/
│ │ ├── _helper.spec.ts
│ │ ├── 01.health.spec.ts
│ │ ├── 02.player-auth.spec.ts
│ │ ├── 03.wallet-ticket.spec.ts
│ │ ├── 04.admin-auth.spec.ts
│ │ ├── 05.wallet-transfer.spec.ts # 转账 + 幂等 + 1001/1010
│ │ ├── 06.wallet-logs.spec.ts # 流水一致性 + 过滤 + 分页
│ │ ├── 07.admin-player.spec.ts # 创建/冻结/解冻玩家
│ │ ├── 08.draw-publish-settle.spec.ts # 开奖结算派彩poll无 skip
│ │ ├── 09.credit-bet.spec.ts # 信用盘下注
│ │ ├── 10.agent-settlement.spec.ts # 代理账期关账
│ │ ├── 11.sso-mainsite.spec.ts # SSO + 主站 mock 异常 + happy path
│ │ ├── 12.broadcast.spec.ts # Reverb balance.update
│ │ ├── 13.credit-settlement-win.spec.ts # 信用盘中奖释额
│ │ ├── 14.settlement-payment.spec.ts # 账期 confirm + 收付
│ │ ├── 15.reconcile-job.spec.ts # pending_reconcile 扫描
│ │ ├── 16.agent-share-bill.spec.ts # 代理占成账单
│ │ ├── 17.settlement-partial-payment.spec.ts # 部分收付
│ │ ├── 18.settlement-permissions.spec.ts # 站点财务/代理权限
│ │ ├── 19.settlement-bad-debt-partial.spec.ts # 部分收付后坏账
│ │ ├── _helper.ts # 共享步骤
│ │ └── helpers/draw-settlement.ts # 结算流水线
│ └── ui/
│ ├── admin-login.spec.ts
│ ├── admin-settlement-center.spec.ts
│ ├── front-login-hall.spec.ts
│ └── front-place-bet.spec.ts
├── database/seeders/
│ └── E2EPlayerSeeder.php # 建可登录玩家E2E\Seeders 命名空间)
├── routes/e2e.php # 仅 /api/v1/_e2e/* 路由
├── providers/
│ └── E2EServiceProvider.php # 仅在 LOTTERY_E2E=true 时挂载路由
├── scripts/
│ ├── run.sh # 一键起 stack + mock + UI + playwright
│ └── mock-wallet-server.mjs # 主站钱包 mock5555
└── README.md # 本文件
# E2E 控制器(仅路由被挂载才暴露,物理上位于 app/ 下便于 Laravel 自动加载)
app/Http/Controllers/Api/V1/E2E/
├── E2ECaptchaPeekController.php # captcha bypass 提示
├── E2EPlayerStateController.php # 玩家 reset / set-balance / unlock / inspect
├── E2EDrawController.php # close-now / finish-cooldown / tick
├── E2EInspectController.php # credit-ledger / wallet-txns / ticket-items
└── E2EProvisionController.php # credit-player / SSO mint / wallet mock 配置
# E2E 服务提供者注册入口(仅 E2E 时注册)
bootstrap/providers.php # 在末尾追加 E2EServiceProvider
# 生产代码最小入侵点
app/Services/AdminCaptchaService.php # 多了一个 `LOTTERY_E2E_BYPASS` 分支env 关闭时不生效)
composer.json # autoload-dev 加 E2E\Seeders\, E2E\Providers\
```
## 前置依赖
- DockerDocker Desktop / OrbStack / Colima 任一)
- Node.js 20+(跑 Playwright
- PHP 8.3+、Composer
- 第一次跑:`npx playwright install chromium`(自动;本仓库 e2e 用 API 模式不依赖浏览器壳,但安装包仍需)
## 一键跑macOS / Linux
```bash
./e2e/scripts/run.sh
```
会自动:
1. `docker compose up -d`PG :15432 + Redis :16379
2. 等 PG/Redis health
3. `composer install`(缺 vendor 时)
4. 复制 `e2e/.env.e2e``.env`,注入强随机 `LOTTERY_NATIVE_JWT_SECRET` / `REVERB_APP_SECRET`
5. `php artisan key:generate`(缺时)
6. **`php artisan lottery:db-init --fresh`** ⚠️ 重建 `lottery_e2e` 库(**只**作用于此库,绝不碰其他库)
7.`E2EPlayerSeeder`(建可登录玩家)
8.`php artisan serve`8000`queue:work redis`(默认队列)、`reverb:start`8080
9. UI 测试默认用本机 **Google Chrome**`playwright.config.ts` `channel: 'chrome'`**无需**下载 Playwright Chromium
10. `npx playwright test`(若要用自带 Chromium`PLAYWRIGHT_USE_BUNDLED_CHROMIUM=1 ./e2e/scripts/run.sh`
跑完按 Ctrl+C 自动停 serve/queue/reverb。`docker compose down -v` 自行决定(脚本不删 volume下次跑快
## 单独跑(不重起 stack
```bash
# 起 stack不跑测试
docker compose -f e2e/docker-compose.yml up -d
DB_DATABASE=lottery_e2e php artisan serve --port=8000
DB_DATABASE=lottery_e2e php artisan queue:work redis
DB_DATABASE=lottery_e2e php artisan reverb:start --port=8080
# 跑测试
cd e2e
PLAYWRIGHT_API_URL=http://127.0.0.1:8000 \
E2E_PLAYER_USERNAME=demo_player E2E_PLAYER_PASSWORD=12345678 \
npx playwright test --headed # 想要 UI 调试时
```
## e2e 账号
| 角色 | 账号 | 密码 |
|------|------|------|
| 超管 | `admin` | `12345678` |
## e2e 玩家账号
| 字段 | 值 |
|------|---|
| `site_code` | `demo` |
| `username` | `demo_player` |
| `password` | `12345678` |
| `auth_source` | `lottery_native` |
| `funding_mode` | `wallet` |
| 初始余额 | `1,250,000 minor`NPR 125.00,由 `DEV_SEED_WALLET_BALANCE_MINOR` 改) |
## 覆盖矩阵(截至当前)
| 链路 | spec | 用例数 | 状态 |
|------|------|--------|------|
| 健康/ping/captcha/公开接口 | 01.health | 4 | ✅ |
| 玩家登录 / 失败 / 锁定 | 02.player-auth | 4 | ✅ |
| 玩家钱包 + 下注 + 幂等 | 03.wallet-ticket | 3 | ✅ |
| 超管登录 / dashboard / 401 | 04.admin-auth | 3 | ✅ |
| 玩家 transfer-in/out + 1001/1010 | 05.wallet-transfer | 7 | ✅ |
| 钱包流水一致性 + 过滤 + 分页 | 06.wallet-logs | 4 | ✅ |
| 超管创建/冻结/解冻/查玩家 | 07.admin-player | 6 | ✅ |
| 开奖+结算+派彩 完整链路 | 08.draw-publish-settle | 1 | ✅ 确定性 poll |
| 信用盘下注占用授信 | 09.credit-bet | 1 | ✅ |
| 代理账期关账出账单 | 10.agent-settlement | 1 | ✅ |
| SSO JWT + 主站钱包异常 + happy path | 11.sso-mainsite | 4 | ✅ |
| Reverb balance.update | 12.broadcast | 1 | ✅ |
| 信用盘中奖释额 game_settlement_win | 13.credit-settlement-win | 1 | ✅ |
| 账期 confirm + 登记收付闭环 | 14.settlement-payment | 1 | ✅ |
| pending_reconcile → reconcile-jobs | 15.reconcile-job | 1 | ✅ |
| 代理占成账单 share_profit | 16.agent-share-bill | 1 | ✅ |
| 部分收付 partial_paid → settled | 17.settlement-partial-payment | 2 | ✅ |
| 站点财务收付 / 绑定代理禁坏账 | 18.settlement-permissions | 1 | ✅ |
| 部分收付后坏账核销 | 19.settlement-bad-debt-partial | 1 | ✅ |
| 管理端 UI 登录 | ui/admin-login | 1 | ✅ |
| 管理端 UI 结算中心 | ui/admin-settlement-center | 1 | ✅ |
| 玩家端 UI 登录进大厅 | ui/front-login-hall | 1 | ✅ |
| 玩家端 UI 下注提交 | ui/front-place-bet | 1 | ✅ |
合计 ~43 个用例覆盖 e2e 关键链路。
## 已知未覆盖(需外部依赖或重构成本高)
- **玩家提现链路**:与 transfer-out 重叠度 90%,增量价值低
- **报表 / settings 实时生效**:表单字段多,断言脆,价值中
## 跑特定 spec
```bash
cd e2e
npx playwright test 05.wallet-transfer.spec.ts --headed
```
## 关键安全设计
- **生产环境零侵入**`AdminCaptchaService::verify``env('LOTTERY_E2E')` 开关,生产 `.env` 留空 → bypass 分支 dead code。
- **E2E 路由不挂载到生产**`E2EServiceProvider::boot``LOTTERY_E2E !== true` 时直接 return`/api/v1/_e2e/*` 在生产完全 404。
- **数据隔离**e2e 库名固定 `lottery_e2e`,与 `lottery` 主库**端口不同**15432 vs 5432
- **migrate:fresh 是库内操作**:脚本注释里明确"只作用于此 docker 库",并对应 AGENTS.md 规定。
- **JWT / Reverb 密钥运行时随机生成**:避免与生产/主站共用 secret。
## 添加新用例
1.`e2e/tests/api/` 新建 `XX.something.spec.ts`
2. 复用 `fixtures.ts``playerLogin` / `adminLogin` / `playerCtx` / `adminCtx`
3. 任何要复用 e2e-only 路由的辅助,往 `E2EServiceProvider` 挂的路由里加,**绝不**往生产路由加
4. 跑:`cd e2e && npx playwright test 05.new.spec.ts`
## 排障
- **API 启动失败**`tail -n 100 e2e/logs/serve.log` / `queue.log` / `reverb.log`
- **PG/Redis 起不来**`docker compose -f e2e/docker-compose.yml logs`
- **测试卡住**:脚本的 `cleanup` 钩子 Ctrl+C 会停 artisan/queue/reverb
- **player 登录 401**`curl -X POST http://127.0.0.1:8000/api/v1/_e2e/reset-player` 重置玩家
## CI 集成
仓库已包含 `.github/workflows/e2e.yml`push/PR 自动跑 API 项目;`E2E_UI=0` 不启前后端 dev server
本地跑全量(含 UI
```bash
./e2e/scripts/run.sh
```
仅 API与 CI 一致):
```bash
E2E_UI=0 ./e2e/scripts/run.sh
```
失败产物:`e2e/artifacts/``e2e/logs/`