Extract AppState into testable ConsaiKit + comprehensive unit tests#20
Merged
Conversation
Move AppState, mock engines, and the Terminal shell launcher out of the App target into a new UI-free ConsaiKit library so the app's orchestration layer can be unit-tested without SwiftUI. App views now import ConsaiKit. Add 86 unit tests across the pure logic tier with in-memory fakes and a process-spy: - AppState: refresh/vitals carry-forward, container & stack actions, compose up/down gating, image & infra mutations, service start/stop, detail, error paths, link/forget, polling lifecycle, project-name parsing - CLI engines (compose, creator, service health): argv/cwd, exit-code and missing-binary handling, status parsing, binary resolution - RegistryStore: save/load round-trip, empty + corrupt file handling - Models: ports, mounts, networks, volumes, stacks, cpu/byte math Logic tier now ~93% line coverage (AppState 93%, ProjectRegistry 94%, models 90-100%). The I/O-boundary layer (SDK XPC engines, ProcessRunner, LogStreamer, ContainerShell) stays at E2E/manual QA since it requires a live daemon; see docs/TESTING.md for the two-tier rationale.
This project builds and tests entirely on the developer's macOS 26 / Xcode 26 machine — Apple's container SDK graph can't build on hosted runners, and macOS runners are billed minutes. Replace the workflow with a local pre-flight script: scripts/check.sh # swift build + swift test scripts/check.sh coverage # + llvm-cov report for the logic layers Update docs/TESTING.md to document the local-only flow.
No shell scripts: tests run via the native `swift test`, and the llvm-cov report is a small Swift executable target (Tools/coverage) invoked with `swift run coverage`. swift test --enable-code-coverage # native test runner swift run coverage # prints the logic-tier llvm-cov report The tool only shells out to `xcrun llvm-cov` (never re-invokes swift), so it avoids the build-lock deadlock a SwiftPM command plugin hits when it tries to spawn a nested swift build/test. Drops scripts/check.sh and updates docs.
Owner
Author
|
Follow-up on this branch: removed the GitHub Actions workflow (hosted runners can't build Apple's
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Extracts the app's orchestration layer (
AppState, mock engines, the Terminal shell launcher) out of theApptarget into a new UI-freeConsaiKitlibrary so it can be unit-tested without SwiftUI, then adds a comprehensive unit-test suite across the pure logic tier.Why
The largest piece of behavior —
AppState(refresh cadence, optimistic actions, stack grouping, error surfacing) — lived in the SwiftUI target and couldn't be tested in isolation. Moving it to a library makes the whole orchestration layer testable with in-memory fakes.Tests added (86 total, all green)
In-memory fakes + a
SpyProcessRunner(records argv/cwd, never spawns):Coverage
Logic tier ≈ 93% line / 89% region:
AppStateProjectRegistryRegistryStoreCLIServiceHealthCLIComposeEngineCLIContainerCreatorVitalsSampler/ modelsThe I/O-boundary layer (
SDKContainerEngine/SDKImageEngine/SDKInfraEngineXPC,ProcessRunner,LogStreamer,ContainerShell) is intentionally left to theE2ETeststarget + manual QA — it requires a livecontainerdaemon and real subprocesses. Their pure inputs (argv builders, parsers, tokenizer, name validation) are unit-tested. Seedocs/TESTING.mdfor the two-tier rationale.Verification
swift test— 86 tests in 23 suites pass.