5.3 KiB
5.3 KiB
PWA 跨版本缓存升级验证:技术设计
1. 判定对象
本任务验证的是一个状态迁移,不是两个独立页面截图:
same loopback origin + same BrowserContext
-> serve build A
-> A page loads
-> A SW ready + controller + precache proven
-> close A page, retain context/profile
-> atomically switch origin to build B
-> revisit same protected route
-> real SW update code runs
-> document/controller/cache/session converge to B
任何直接从空 context 打开 B 的路径都只能是 harness sanity,不参与最终判定。
2. 产物生成
- runner 在 OS 临时目录创建
build-a/、build-b/,不覆盖client/dist。 - 两次均从当前 checkout 构建,只注入不同的 synthetic 12 位 SHA。构建后读取
index.html与生成资产,断言:- document/runtime 可观察到各自 SHA;
sw.js存在;- A/B hashed JS/CSS 至少有一个差集。
- runner 使用
trap/finally 精确清理本轮临时目录;失败日志只输出相对路径和计数。
选择同源码不同 SHA,而不是 checkout 历史 commit,原因是本轮要隔离“SW 缓存版本切换”这个变量。build SHA 会进入 core bundle 并改变 Workbox revision;历史代码差异会同时引入 API、路由、fixture 与依赖变量。
3. Loopback HTTP harness
Playwright spec 进程内启动只监听 127.0.0.1 的 ephemeral HTTP server,持有一个 activeBuild=A|B 状态:
/sw.js、/index.html与 SPA navigation:从 active dist 读取;HTML/SW 使用Cache-Control: no-cache。- hashed JS/CSS/image:从 active dist 读取并使用 immutable cache header。
/ready:返回 active synthetic build SHA,Cache-Control: no-store。/api/auth/refresh、/api/me、navigation、notifications 与 portal 最小接口:返回合成 envelope,不触碰数据库。- 路径解析必须防止越出 active dist;未知静态文件 404,未知 API 返回受控合成结果。
switchToB()为测试进程内同步状态切换,不暴露公网管理端点;同一 server/socket/origin 始终不变。
请求 ledger 只保留 method/path/status/build,并单独统计 JS/CSS 404、refresh/me 与 main-frame navigation。
4. 浏览器 witness
Phase A:建立旧 profile
- 创建唯一 BrowserContext,显式
serviceWorkers: "allow"。 - 安装 synthetic
has_sessioncookie;API refresh 返回合成 token,/api/me返回合成员工。 - 访问
/portal,确认 document SHA=A、route 保持/portal。 - 等待
navigator.serviceWorker.ready;A 阶段允许且仅允许一次显式page.reload(),使首次安装 worker 成为 controller。 - 断言 controller 存在、A precache/cache entry 存在;保存 A-only hashed asset 相对路径集合。
- 关闭 A page,但不关闭 context。
Phase B:部署后重访
- server 原位
switchToB(),同时切换/ready和所有静态文件。 - 在同一 context 创建新 page,重新访问
/portal。 - 从这一刻起禁止测试代码调用 reload/clear/unregister;只等待应用真实 autoUpdate/clientsClaim/更新回调。
- 记录
controllerchange,必要时通过 Chromium CDP ServiceWorker domain 对比 A/B active worker versionId;scriptURL预期始终为同 origin/sw.js,不得把恒定 URL 误写成版本证明。 - 将 B 阶段 no-cache 获取到的
/sw.js内容 SHA-256 与 build B 文件指纹对账,并联合断言 document SHA=B、B-only chunk 已成功加载、route 仍为/portal、refresh/me 成功、controller 存在。 - 安全记录
hyb-oa:pwa-update-ready触发次数;若 autoUpdate 流程实际进入onNeedRefresh,保留阶段与后续行为证据。 - 读取 Cache Storage 的 URL pathname 白名单,断言 A-only JS/CSS 消失,B asset 已进入或已成功请求。
- 断言无
/login、无 JS/CSS 404、无 chunk/preload/page error、导航次数有上限。
5. 不使用 Playwright route mocking
本 witness 的核心就是观察 Service Worker 拥有网络请求时的真实行为。若用 page.route mock API,会重新引入上一任务已证明的 fixture 冲突。因此所有 synthetic API 都由 loopback HTTP harness 提供,浏览器和 worker看到的是同一真实 origin 响应。
6. 结果状态
| 状态 | 判据 | 后续 |
|---|---|---|
PASS |
A 前置成立,B 在禁止强刷条件下完整收敛,所有负向断言通过 | 固化 test-only harness |
PRODUCT_FINDING |
A 前置成立,但 B 长期旧 shell、回登录、旧 chunk 404/残留或 reload loop | 停止,另建 PWA 产品修复 task |
PAUSED |
A controller/precache 无法稳定建立、构建产物不可区分或环境不支持 SW | 记录恢复条件,不猜测产品结论 |
7. 风险与回滚
- 只新增/修改 QA runner、E2E spec 与当前 task;
client/src/pwa.ts、vite.config.ts在验证阶段只读。 - 所有 server、BrowserContext 和临时目录用 finally/trap 清理;失败后不得留下监听端口或 build 目录。
- harness 若证明不了旧 profile 前置,删除/回滚本轮 test-side probe,但保留 PAUSED 证据。
- 第一次 B witness 失败即为冻结点;不得通过手工或自动追加 reload/重访改变失败性质。只有先保存安全 ledger、回到规划并获批后,才允许开展新的诊断实验。
- 不接触生产、数据库、部署脚本或真实浏览器 profile。