Files
lotteryLaravel/e2e
kang c83343e989
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled
fix(settlement): 操作记录分页、流水动作与坏账幂等加固
- 新增 settlement-operations 合并列表 API,收付/调账接口改为标准分页
- actionable_only 流水在内存过滤后正确分页
- 已结账单流水不再展示补差/冲正快捷动作
- 坏账核销支持 idempotency_key 并写入 result_bill_id
- 补充操作记录、流水与坏账幂等相关测试
2026-06-26 16:40:46 +08:00
..

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 serve8000
时延 数百 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

./e2e/scripts/run.sh

会自动:

  1. docker compose up -dPG :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 serve8000queue:work redis(默认队列)、reverb:start8080
  9. UI 测试默认用本机 Google Chromeplaywright.config.ts channel: 'chrome'无需下载 Playwright Chromium
  10. npx playwright test(若要用自带 ChromiumPLAYWRIGHT_USE_BUNDLED_CHROMIUM=1 ./e2e/scripts/run.sh

跑完按 Ctrl+C 自动停 serve/queue/reverb。docker compose down -v 自行决定(脚本不删 volume下次跑快

单独跑(不重起 stack

# 起 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 minorNPR 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

cd e2e
npx playwright test 05.wallet-transfer.spec.ts --headed

关键安全设计

  • 生产环境零侵入AdminCaptchaService::verifyenv('LOTTERY_E2E') 开关,生产 .env 留空 → bypass 分支 dead code。
  • E2E 路由不挂载到生产E2EServiceProvider::bootLOTTERY_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.tsplayerLogin / 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 登录 401curl -X POST http://127.0.0.1:8000/api/v1/_e2e/reset-player 重置玩家

CI 集成

仓库已包含 .github/workflows/e2e.ymlpush/PR 自动跑 API 项目;E2E_UI=0 不启前后端 dev server

本地跑全量(含 UI

./e2e/scripts/run.sh

仅 API与 CI 一致):

E2E_UI=0 ./e2e/scripts/run.sh

失败产物:e2e/artifacts/e2e/logs/