test(ios): close T-iOS-32's end-to-end half — real worktree lifecycle against a real server

T-iOS-32's acceptance is "builder 测试 + 端到端一次". The builder half was green,
but grep for projects/worktree across the whole test tree returned nothing — no
test had ever created or removed a real git worktree against a real server.

Six tests on a throwaway git fixture: create/remove/prune verified against both
the filesystem and git's own `worktree list --porcelain` + .git/worktrees/, and
asserting the values the SERVER derives rather than echoing our input (branch
feature/e2e-worktree becomes directory feature-e2e-worktree; the raw branch name
must never appear in the path). Includes a differential leg for the guarded-route
rule — the same request without Origin is 403 with zero side effects, and with it
is 200 — which needs raw URLSession, since APIClient structurally cannot emit an
Origin-less guarded request.

GET /sessions gets its own HOME-isolated server: history.ts reads
os.homedir()/.claude/projects, so against the shared harness the assertions would
land on the developer's real Claude history, and in CI the directory is absent so
it degrades to [] and proves nothing.

Integration tests 26 -> 32, independently re-run.
This commit is contained in:
Yaojia Wang
2026-07-30 16:58:22 +02:00
parent 5cc755b0b6
commit ddab77b337
10 changed files with 1577 additions and 23 deletions

View File

@@ -540,7 +540,7 @@ W5 验收(report-only, 并行)
| T-iOS-13 SessionList | `[x]` | `Screens/SessionListScreen.swift``Components/TelemetryChips.swift``ViewModels/SessionListViewModel.swift` + Tests |
| T-iOS-14 Gate/Digest UI | `[x]` | `Components/{GateBanner,PlanGateSheet,AwayDigestView}.swift``ViewModels/GateViewModel.swift` + `GateViewModelTests` |
| T-iOS-15 App 接线+生命周期 | `[x]` | `Wiring/{RootView,AppCoordinator,PrivacyShade,ColdStartPolicy,TerminalContainerView}.swift` + `PrivacyShadeTests`/`ColdStartPolicyTests` |
| T-iOS-16 集成 CI | `[x]` | `IntegrationTests/**` **26** `@Test`10 基线 + 8 令牌端到端 + 8 令牌策略漂移守卫)+ `scripts/coverage-gate.sh`(现门 **5** 个包,含 ClientTLS+ `ios.yml` 六个 job含 iPad 单测腿/iPad UI 腿/iOS-17 底线腿)+ 新增 `android.yml``ServerHarness.locateTsx` 改为逐级向上解析,故 worktree 内无 `node_modules` 也能自举。**未核实**GH Actions 平台侧的运行结果——本仓库唯一 remote 是自建 Gitea两个 workflow **从未在任何 runner 上跑过**;本地已逐条复跑其命令 |
| T-iOS-16 集成 CI | `[x]` | `IntegrationTests/**` **32** `@Test`10 基线 + 8 令牌端到端 + 8 令牌策略漂移守卫 + 6 worktree 生命周期端到端+ `scripts/coverage-gate.sh`(现门 **5** 个包,含 ClientTLS+ `ios.yml` 六个 job含 iPad 单测腿/iPad UI 腿/iOS-17 底线腿)+ 新增 `android.yml``ServerHarness.locateTsx` 改为逐级向上解析,故 worktree 内无 `node_modules` 也能自举。**未核实**GH Actions 平台侧的运行结果——本仓库唯一 remote 是自建 Gitea两个 workflow **从未在任何 runner 上跑过**;本地已逐条复跑其命令 |
| T-iOS-17 ntfy 桥验证+文档 | `[x]` | `ios/README.md` ntfy 章节(逐条引 `setup-hooks.mjs` 行号,只读验证,未动用户 hook 配置)。**DEFERRED**:手机端到端 |
| T-iOS-18 F 走查(真机) | `[~]` | 机器可执行项已执行并指认测试名P0 收官条目)。**缺口**F-iOS 的真机项QR 扫码/IME/震动/切换器遮罩目检/ntfy 端到端)仍 DEFERRED手工清单在 LOG |
| T-iOS-19 安全核对 | `[~]` | P0 面已逐条核Origin 单点、G/RO 分界、五段 CIDR + 零 ArbitraryLoads、Keychain 属性。P2 新增的 `NSMicrophoneUsageDescription`/`NSSpeechRecognitionUsageDescription` **已在产物层复核**(真机构建产物的 Info.plist 实测有这两键)。**剩余缺口**release ipa 层核对仍未做(免费 team 无分发通道) |
@@ -558,7 +558,7 @@ W5 验收(report-only, 并行)
| T-iOS-29 new-in-cwd + 退出清理 | `[x]` | `TerminalScreen`/`TerminalContainerView` 增量 + `NewSessionInCwdTests` |
| T-iOS-30 P1 验收+安全复核 | `[x]` | report-only 双 PASS 零 findingsLOG 条目)。**DEFERRED**:真机锁屏/unread 目视等,手工清单在 LOG |
| T-iOS-31 语音 PTT | `[x]` | `Components/{VoicePTT,VoicePTTBanner,SpeechDictation}.swift` + `KeyBar` 🎤 键 + `VoicePTTTests` **40** `@Test`。**DEFERRED**:真机口述→确认→注入 |
| T-iOS-32 Worktree + `--resume` 历史 | `[~]` | `Screens/{WorktreeSheet,ResumeHistorySheet}.swift``ViewModels/{WorktreeViewModel,ResumeHistoryViewModel}.swift` + `WorktreeViewModelTests`(22)/`ResumeHistoryViewModelTests`(12)/`ProjectResumeLaunchTests`(7)。**缺口**Accept 的"端到端一次"(对真服务器真建/真删 worktree无自动化腿、本环境也未手工跑 |
| T-iOS-32 Worktree + `--resume` 历史 | `[x]` | `Screens/{WorktreeSheet,ResumeHistorySheet}.swift``ViewModels/{WorktreeViewModel,ResumeHistoryViewModel}.swift` + `WorktreeViewModelTests`(22)/`ResumeHistoryViewModelTests`(12)/`ProjectResumeLaunchTests`(7)。**Accept 的"端到端一次"已闭合**(收尾波):`IntegrationTests/WorktreeLifecycleTests.swift` **6 例**在临时 git fixture 仓上对真服务器真建/真删/真 prune——断言的是服务端**推导**出的路径与分支(`feature/e2e-worktree` → 目录 `feature-e2e-worktree`,原始分支名绝不出现在路径里),并用 `git worktree list --porcelain``.git/worktrees/` 双向核对;含 G 路由纪律的差分腿(无 `Origin` → 403 且零副作用,同一请求加 `Origin` → 200与非法分支名不创建;`GET /sessions` 用 **HOME 隔离**的专用服务器逐字段解码(否则会打到开发者真实 Claude 历史CI 上又退化成 `[]` 什么都证明不了) |
| T-iOS-33 终端内搜索 | `[x]` | `Components/TerminalSearchBar.swift` + `TerminalScreen` 绑定 SwiftTerm 搜索 API + `TerminalSearchTests` **18**(含 2 条真 view 高亮断言) |
| T-iOS-34 主题 + Dynamic Type | `[x]` | `DesignSystem/{AppTheme,TerminalPalette}.swift``Screens/SettingsScreen.swift``Tokens/Typography` 浅色档 + `AppThemeTests`(24)/`DynamicTypeLayoutTests`(**14**)/`KeyBarTests`(**12**)。**缺口已闭合**(收尾波):`KeyBarMetrics.barHeight` 由常量 52pt 改为按字号档推导,`withKnownIssue` 已删、换成正向断言AX5 不裁切 · 全 12 档不裁切且 ≥44pt · XSXL 逐点仍 52pt 零回归 · 未封顶 AX5 溢出旧固定高的护栏)。**取舍**:键帽字体封顶在 `DS.Typography.numericClamp`(.accessibility2),故 AX3AX5 的字号不再增长(不封顶时 AX5 要 108.24pt,会吃掉终端);该封顶由测试钉死不得漂移 |
| T-iOS-35 web `?join=` 互通 | `[x]` | `DeepLinkRouter.swift``.joinShared` 分支 + `DeepLinkJoinTests` **19**(含改写后的既有拒绝用例) |
@@ -588,7 +588,7 @@ W5 验收(report-only, 并行)
另补 iPad UI-test 腿(同 commit
- **签名解锁**`DEVELOPMENT_TEAM` + target 级 `CODE_SIGN_IDENTITY` + `WEBTERM_PUSH_ENTITLEMENTS` env 开关
commit `c4f8b5b`;真机构建在免费 team 上 `BUILD SUCCEEDED`7 天临时 profile
- **收尾波2026-07-30闭合了其中三条**① AX5 键栏裁切 → 已修(见 T-iOS-34 行);
- **收尾波2026-07-30把四条全部闭合**:④ worktree 端到端 → 见 T-iOS-32 行;① AX5 键栏裁切 → 已修(见 T-iOS-34 行);
② 端到端令牌腿 → `IntegrationTests/AccessTokenGateTests.swift` **8 例**对真服务器(带 `WEBTERM_TOKEN` 自举)
跑通:对令牌放行 / 错令牌 401 终态且 **connect 计数 == 1**(另有可重试失败的对照组证明"零重试"不是计时假象)/
令牌不替代 Origin合法 cookie + 外域 Origin 仍拒)/ `POST /auth` 的 204+Set-Cookie、401、204-无-Set-Cookie 三态;