feat(ios): W4 app wiring — DI graph, event fan-out, lifecycle, privacy shade
T-iOS-15: AppEnvironment production DI (real Keychain/probe/transports), EventFanOut (engine.events → TerminalVM + GateVM + activity bridge), scenePhase lifecycle (.background→close, suspend→rebuild+reopen reclaims size), privacy shade on != .active, spike screen deleted, cold-start routing Walkthrough proxies: hosted live-server smoke over the production DI graph (probe→store→ attach→echo→close) + real-Keychain kSecAttrAccessible assertion (closes T-iOS-7 deferral) Deviation closed: TerminalViewModel.lastSentDims (valid-only) + alive-engine .active → notifyForegrounded(dims:) (orchestrator fix on behalf of T-iOS-11 owner) Env finding: repo under ~/Documents → TCC blocks sim-spawned node; SimServerHarness dual-mode (self-bootstrap / WEBTERM_SERVER_URL via simctl launchctl setenv) Verified: 214 pkg + 76 app + 10 integration tests green; verify agent 8/8 PASS
This commit is contained in:
29
ios/App/WebTermTests/PrivacyShadeTests.swift
Normal file
29
ios/App/WebTermTests/PrivacyShadeTests.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
import SwiftUI
|
||||
import Testing
|
||||
@testable import WebTerm
|
||||
|
||||
/// T-iOS-15 · Privacy-shade state mapping (plan §7 T-iOS-15, security-critical).
|
||||
///
|
||||
/// The rule under test is EXACT: the shade covers whenever
|
||||
/// `scenePhase != .active` — NOT just `.inactive`. Entering the app switcher
|
||||
/// is `.inactive`, but the moment iOS writes the switcher snapshot to disk is
|
||||
/// `.background`; covering only one of the two leaks terminal content
|
||||
/// (API keys / tokens / source) into the on-disk snapshot.
|
||||
@MainActor
|
||||
@Suite("PrivacyShadePolicy")
|
||||
struct PrivacyShadeTests {
|
||||
@Test("scenePhase == .active → 遮罩隐藏(恢复终端)")
|
||||
func activeShowsTerminal() {
|
||||
#expect(!PrivacyShadePolicy.isShadeVisible(for: .active))
|
||||
}
|
||||
|
||||
@Test("scenePhase == .inactive → 遮罩可见(切换器入口态)")
|
||||
func inactiveIsCovered() {
|
||||
#expect(PrivacyShadePolicy.isShadeVisible(for: .inactive))
|
||||
}
|
||||
|
||||
@Test("scenePhase == .background → 遮罩可见(快照写盘时刻)")
|
||||
func backgroundIsCovered() {
|
||||
#expect(PrivacyShadePolicy.isShadeVisible(for: .background))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user