refactor(daemon): relocate the session-lifecycle tests by what they assert
Summary
Batch session-lifecycle (plus the device, replay-suite and replay-target leftovers) of the daemon test redesign (no test exports of any kind). 13 root tests under src/__tests__/daemon-composition/{session-lifecycle,device,replay-suite,replay-target}/ are placed by what they assert; the directories are gone. Stacked on #3666 (and through it #3652).
Daemon halves (a) run against the package fakes and a new lifecycle-operations-spy.ts (the canned lifecycle port with finalizeApplicationClose as a spy). Assertions about what the real platform adapters or command code do (c) moved beside that code. The device-ready mocks and the patch of platform-runtime-apple-application-tools.ts are gone.
a / c split and test-count parity
Before = exact count at the old path (vitest JSON reporter on a clean tree); after = run count per destination on this head.
| Source (before) | Daemon half (a) | Adapter / command half (c) |
|---|---|---|
session-close-error-precedence (2) | session-lifecycle/internal/__tests__/session-close-error-precedence.test.ts (3 → 5) | packages/platform-apple/src/lifecycle-physical-close.test.ts (runner stop before a physical-device close) |
session-close-save-script (1) | .../session-close-save-script.test.ts (1 → 2) | covered by platform-apple/lifecycle.test.ts |
session-close-shutdown-platform (2) | .../session-close-shutdown-platform.test.ts (5 → 7) | platform-apple/lifecycle-physical-close.test.ts, platform-android/lifecycle.test.ts (+1): close --shutdown gating |
session-teardown-resources (2) | .../session-teardown-resources.test.ts (7 → 9) | retention policy: src/platform-runtime-resource-cleanup-retention.test.ts in #3666 |
session-close-runtime-hints (4) | .../session-close-lifecycle-runtime.test.ts (5 → 9) | src/platform-runtime-android-application-tools.test.ts (2) |
session-open-surface (1), session-open-target (4) | none | src/platform-runtime-open-target.test.ts (18) |
session-open-existing (8) | .../session-open-existing.test.ts (2 → 10) | platform-runtime-open-target.test.ts; src/platform-runtime-apple-application-tools.test.ts (3, macOS frontmost) |
session-open-post-open-observation (3) | .../session-open-execution-runtime.test.ts (14 → 24, shared) | platform-runtime-apple-application-tools.test.ts (runner prewarm forwarding) |
session-open-url-prewarm (16) | session-open-execution-runtime.test.ts (6 prewarm), sessions/handlers/__tests__/session-prepare.test.ts (3 → 8) | platform-runtime-open-target.test.ts (5 URL identity), packages/platform-apple/src/lifecycle-open-prewarm.test.ts (5: cold-boot cache prewarm, Maestro simulator reset, blocking prewarm failure, no-bundle-id skip), apple tools prepareRunner |
device/device-ready (5) | core/device/device-ready.test.ts (6; +1: an unhandled device is not cached) | src/platform-runtime-device-ready.test.ts (4) |
replay-suite/session-test-suite-command-flag-policy (6) | none | src/commands/replay/index.test.ts (19 → 20, schema parity); packages/replay-port/src/daemon-port/test-command-flag-policy.test.ts (5, raw-wire rejections, no daemon) |
replay-target/session-replay-target-guard (7) | none | git mv to src/commands/interaction/runtime/replay-target-guard.test.ts (5); replay-port/.../__tests__/target-classification.test.ts (17 → 19) |
61 tests before; 229 pass across the destinations including pre-existing tests in the extended twins. The old session-open-url-prewarm URL-identity cases map one to one: web URL on a simulator with a stale app id → platform-runtime-open-target.test.ts "a web URL on an iOS simulator targets Safari whatever the active app is" plus the session-open-existing.test.ts row "a web URL on an iOS simulator session with a stale app bundle id"; URL on a macOS session clears the stale id → "a URL on a macOS session clears the stale app bundle id"; URL on an iOS device keeps the app → "a web URL on an iOS device keeps the active app context"; custom URL on an existing simulator → "a custom URL on %s keeps the active app bundle id"; custom URL on a fresh simulator infers from the scheme → "a custom URL on a fresh iOS simulator infers the app bundle id from the URL scheme". No case disappeared without a named owner.
Selection
All destinations match packages/*/src/**/*.test.ts or src/**/*.test.ts, so unit-core selects them (pnpm test:unit, test:coverage); the sources were already in unit-core. check:affected --run runs the vitest related leg over them.
vi.mock (each reads what its mock provided or recorded)
platform-runtime-android-application-tools.test.ts,./platform-runtime-runtime-hints.ts:runtimeHints.evaluatedis false after building the tools and true afterclearRuntimeHints;clearRuntimeHintValuesis called once with the device and app.platform-runtime-apple-application-tools.test.ts,platform-apple/macos: the frontmost-app case returns the mocked{ appBundleId: 'com.apple.TextEdit', appName: 'TextEdit' }.- same file,
platform-apple/runner/operations:prewarmIosRunnerSessioncalled withpropagateError: falseand the request id;prepareIosRunnercalled with thecleanStaleBundlesand timeout fields. platform-runtime-open-target.test.ts,platform-android/mechanicsresolveAndroidPackageForOpen,platform-apple/app-resolutionresolveIosAppandresolveIosSimulatorDeepLinkBundleId: each result is read through the returned id.platform-runtime-device-ready.test.ts:host-kit/command(runCmdcalled once; its written JSON consumed),platform-apple/simulator(ensureBootedSimulatorcalled with the device),platform-android/mechanics(waitForAndroidBootcalled with the id).- Existing twin mocks are untouched; the root
device-readymocks (×3) are gone.
Subpath exports
| Package | Before (#3666) | After |
|---|---|---|
daemon | 89 | 85 |
Deleted: __tests__/application-lifecycle-runtime-harness, __tests__/device-selection-stub, handlers/session-replay-command, session-lifecycle/internal/__tests__/*. No other package changed; no new subpath, barrel or export *.
Notes
packages/platform-apple/src/lifecycle.test.tsis back to its merge-base size (968 lines): the cases this batch adds live in two new files besidelifecycle.ts(lifecycle-open-prewarm.test.ts,lifecycle-physical-close.test.ts), because the test-file size ratchet fails a file over 1,000 lines that grows (46 + 5 + 2 = 53 tests, all passing).- Edits beyond
packages/daemonand the daemon-composition tree are additive tests inplatform-apple,platform-android,replay-portandsrc/commands/**, plus newsrc/platform-runtime-*.test.tsfiles; they carry rules the root fixture only wrapped. Say if any belongs to another worker. - The agent's own
platform-runtime-resource-cleanup.test.tsretention tests were dropped in favor of #3666's equivalent-retentionfile (same rules).
Validation
check:quick, check:layering, check:fallow and check:gate-manifest pass on this head; check:affected --run fails only test-file-size-ratchet on the pre-split head (fixed by the split above); test-file-size-ratchet passes after it.
🤖 Generated with Claude Code