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

208
e2e/README.md Normal file
View File

@@ -0,0 +1,208 @@
# 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 扫描
│ │ ├── _helper.ts # 共享步骤
│ │ └── helpers/draw-settlement.ts # 结算流水线
│ └── ui/
│ ├── admin-login.spec.ts
│ └── front-login-hall.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. `npx playwright install chromium`(首次)
10. `npx playwright test`
跑完按 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 (+2 skip) | ✅ |
| 钱包流水一致性 + 过滤 + 分页 | 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 | ✅ |
| 管理端 UI 登录 | ui/admin-login | 1 | ✅ |
| 玩家端 UI 登录进大厅 | ui/front-login-hall | 1 | ✅ |
合计 ~36 个用例覆盖 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 集成(参考)
```yaml
# .github/workflows/e2e.yml
- name: e2e
run: ./e2e/scripts/run.sh
- uses: actions/upload-artifact@v4
if: failure()
with:
name: e2e-artifacts
path: e2e/artifacts/
```