blob: 96aea721332d4b338124c1ad9d9f2d6623f774ec (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
# fund-lab QA(3c-1)
仓库内可复现的端到端浏览器 QA 入口。一条命令启动一次性 Postgres、API(桩行情)与 Vite,然后用无头 Chromium 驱动页面断言 3c-1 行为。
## 运行
```bash
bash qa/run.sh
```
前提:`docker`(含本地 `postgres:16-alpine` 镜像)、`dotnet`、仓库内 `.tools/node-v22.23.2`(`node`/`npm`)。首次运行会在 `qa/driver/` 安装 `playwright-core`(node_modules 已被 .gitignore 覆盖)。
`run.sh` 的行为约定:
- 端口均为非默认:API `5095`、Vite `5175`、Postgres `55434`;容器名 `fund-lab-qa-pg-3c1`。
- 启动前预检端口/容器占用,被占用即中止,绝不复用或杀掉既有进程(拒绝路径有 `qa/lifecycle-test.sh` 回归保障)。
- 每次运行使用 `mktemp -d /tmp/opencode/fund-lab-qa-*` 专属临时目录,日志、截图、env 互不覆盖,失败现场完整保留。
- Postgres 密码随机生成,仅写入本次临时目录的 `pg-env`(0600),不回显、不进仓库。
- API 环境变量:`FUND_LAB_AUTH_TOKEN=qa-token`、`FUND_LAB_AKSHARE_PYTHON=<repo>/qa/stub/fund-lab-python`。
- 退出时清理:杀掉自身启动的 API/Vite PID;仅删除本次 `docker run` 创建的容器(按容器 ID 与名称双重归属校验,拒绝路径绝不触碰既有容器);删除临时 env 文件。日志与截图保留在临时目录中供排查。
## 生命周期回归(qa/lifecycle-test.sh)
```bash
bash qa/lifecycle-test.sh
```
验证 `run.sh` 的拒绝路径不破坏既有资源:预置同名容器或占用端口时 `run.sh` 必须拒绝退出,且既有容器与端口占用进程原样保留。脚本自身也只管理它创建的资源。
## 行情桩(stub)
`qa/stub/fund-lab-python` 是一个假解释器:`MarketDataService` 以 `FUND_LAB_AKSHARE_PYTHON` 调用它并传 `akshare_collector.py --operation search|nav ...`,它按 collector JSON 契约向 stdout 输出 envelope:
- `schema_version="fund-lab.akshare.v1"`、`operation`、`source="stub-synthetic"`、`source_revision`、`collected_at`
- 每个载荷都带 `_synthetic:true`,来源标记为 stub(合成数据不做真实披露)
## 浏览器场景(qa/driver/browser-test.js)
- A 初始渲染:创建面板、名称/现金输入框、按钮文案。
- B 校验:空名称、非法金额(`abc`、`-5.00`)被拒绝且不发出任何 POST。
- C 创建成功:双击仅 1 次 POST;`Idempotency-Key` 为 32 位十六进制;请求体金额为 decimal 字符串(`initialCash="12345.67"`、`initialUnitNav="1.00000000"`,不经 JS float);`isSynthetic=false`;档案展示服务端返回值,份额诚实显示 0。
- D 重新读取:恰好 1 次 GET,金额持久化复核。
- E token 失效:换 token 立即清空档案;拦截并延迟的旧 GET 响应落地后被忽略(stale 防护)。
- F 幂等重试:首 POST 被掐断显示错误横幅,重试复用同一 `Idempotency-Key` 成功。
- G 全程无浏览器 console/page 错误。
## 真实行情模式
QA 默认走合成桩。需要真实 AKShare 时,不运行 `run.sh`,而是自行启动 Postgres 并把 API 的 `FUND_LAB_AKSHARE_PYTHON` 指向装有 akshare 的解释器,例如本机已验证可用:
```bash
PYTHONPATH=/tmp/opencode/fund-lab-akshare-site /usr/bin/python3 -c "import akshare" # 1.18.96
```
真实模式输出的 envelope 不含 `_synthetic`,来源为真实数据源;前端“数据来源”行相应显示“真实建档”。
## 不变量
- 金额永远以 decimal 字符串提交/展示;浏览器端不经过任何 float 运算(见 `qa/driver/browser-test.js` 场景 C3 对请求体的断言)。
- 网络失败后的重试复用同一 `Idempotency-Key`;输入变更或创建成功后才换新 key。
|