diff --git a/CHANGELOG.md b/CHANGELOG.md index 10e1772..36496b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,13 @@ Dates are `YYYY-MM-DD` in the project's local timezone. - Stray `docs/mockups/preview.png` (Android emulator screenshot from a pre-SwiftUI prototype). +### Fixed +- Stale verification instructions: `docs/TESTING.md` and `CLAUDE.md` still + described the removed `check` SwiftPM command plugin (`swift test` + + `swift run coverage` replaced it), and `Package.swift` comments still + referenced the removed `scripts/bundle.sh`. Coverage table in + `docs/TESTING.md` re-measured; `ContainerImage` helpers now unit-tested. + ## [0.1.0] — 2026-06-19 Initial release. Menu-bar-first macOS companion for Apple's `container` diff --git a/CLAUDE.md b/CLAUDE.md index d6379e4..9e9eefe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -118,7 +118,7 @@ On this machine `xcodebuild`/Xcode 26 was not detected and `container-compose` i installed, so the app **cannot be compile-verified locally yet**. `container` *is* present. **Mitigation:** treat "builds in Xcode 26" as a manual gate the developer runs. There is no hosted CI (Apple's SDK graph can't build on hosted runners) — verify locally with `swift -test` / `swift package check` (the `check` SwiftPM command plugin). See `docs/TESTING.md`. +test` (coverage via `swift run coverage`). See `docs/TESTING.md`. ### R8 — Crowded ecosystem (LOW / product) Many compose tools and full GUIs already exist; the menu-bar-first niche is the diff --git a/ConsaiCore/Tests/ConsaiCoreTests/ContainerImageTests.swift b/ConsaiCore/Tests/ConsaiCoreTests/ContainerImageTests.swift new file mode 100644 index 0000000..947b916 --- /dev/null +++ b/ConsaiCore/Tests/ConsaiCoreTests/ContainerImageTests.swift @@ -0,0 +1,33 @@ +import Testing +@testable import ConsaiCore + +@Suite struct ContainerImageTests { + + @Test func idIsTheReference() { + let image = ContainerImage(reference: "alpine:3.20", digest: "sha256:abcd") + #expect(image.id == "alpine:3.20") + } + + @Test func shortDigestStripsPrefixAndTruncatesTo12() { + let image = ContainerImage(reference: "alpine", digest: "sha256:abcd1234efgh5678ijkl") + #expect(image.shortDigest == "abcd1234efgh") + } + + @Test func shortDigestHandlesBareHex() { + let image = ContainerImage(reference: "alpine", digest: "abcdef1234567890") + #expect(image.shortDigest == "abcdef123456") + } + + @Test func formattedSizeIsNilWithoutAUsableSize() { + #expect(ContainerImage(reference: "a", digest: "d", sizeBytes: nil).formattedSize == nil) + #expect(ContainerImage(reference: "a", digest: "d", sizeBytes: 0).formattedSize == nil) + #expect(ContainerImage(reference: "a", digest: "d", sizeBytes: -5).formattedSize == nil) + } + + @Test func formattedSizeUsesSIBinaryBoundaries() { + #expect(formatImageBytes(2_500_000_000) == "2.50 GB") + #expect(formatImageBytes(1_000_000_000) == "1.00 GB") + #expect(formatImageBytes(234_000_000) == "234 MB") + #expect(formatImageBytes(512_000) == "512 KB") + } +} diff --git a/Package.swift b/Package.swift index 08fc66a..9024703 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription // integration fails to wire up the container SDK's transitive package modules // (ServiceContextModule, SwiftASN1, ...); SwiftPM builds the same graph cleanly. // Open this Package.swift directly in Xcode for GUI development, or use `swift build` -// + scripts/bundle.sh to produce Consai.app. +// + `swift run bundle` to produce Consai.app. let package = Package( name: "Consai", platforms: [.macOS(.v26)], @@ -32,7 +32,7 @@ let package = Package( dependencies: ["ConsaiCore"], path: "ConsaiKit/Sources/ConsaiKit" ), - // The menu bar app (SwiftUI). Bundled into Consai.app by scripts/bundle.sh. + // The menu bar app (SwiftUI). Bundled into Consai.app by `swift run bundle`. .executableTarget( name: "Consai", dependencies: ["ConsaiCore", "ConsaiKit"], diff --git a/docs/TESTING.md b/docs/TESTING.md index 9b13ab0..ccc31c2 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -8,41 +8,36 @@ differently, and coverage should be read per tier — not as a single blended nu Everything builds and runs **locally** — there is no GitHub Actions / hosted CI (Apple's `container` SDK can't build on hosted runners, and the project targets a developer's -macOS 26 / Xcode 26 machine). The pre-flight check is a native SwiftPM command plugin -(`Plugins/Check`), not a shell script: - -```bash -swift package --disable-sandbox check # build + test -swift package --disable-sandbox --allow-writing-to-package-directory check coverage # + llvm-cov report -``` - -(`--disable-sandbox` lets the plugin spawn `swift`/`xcrun`; the write flag is needed only -for the `coverage` run, which writes profdata under `.build`.) - -Or invoke the toolchain directly — `swift test` is itself the native test runner: +macOS 26 / Xcode 26 machine). `swift test` is the native test runner; the coverage +report is a small Swift executable (`Tools/coverage`) that shells out to `xcrun +llvm-cov` — it never re-invokes SwiftPM, so it can't hit the build-lock deadlock a +command plugin does: ```bash swift test # fast, no daemon required swift test --enable-code-coverage # adds llvm-cov instrumentation +swift run coverage # prints the logic-tier llvm-cov report +``` +Or invoke llvm-cov directly: PROF=$(find .build -name default.profdata | head -1) BIN=$(find .build -name ConsaiPackageTests -type f | head -1) xcrun llvm-cov report "$BIN" -instr-profile "$PROF" ConsaiCore/Sources ConsaiKit/Sources ``` -## Tier 1 — pure logic (unit-tested here, ~93% line coverage) +## Tier 1 — pure logic (unit-tested here, ~90% line coverage) No daemon, no subprocess, no UI. All I/O sits behind protocols, so these are tested with in-memory fakes and a `SpyProcessRunner` that records argv/cwd instead of spawning. | Area | File | Line cov | |------|------|---------:| -| App orchestration | `ConsaiKit/AppState.swift` | ~93% | +| App orchestration | `ConsaiKit/AppState.swift` | ~86% | | Stack assembly | `ConsaiCore/Engines/ProjectRegistry.swift` | ~94% | | Registry persistence | `ConsaiCore/Engines/RegistryStore.swift` | ~93% | | Service health | `ConsaiCore/Engines/CLIServiceHealth.swift` | ~91% | | Compose CLI wrapper | `ConsaiCore/Engines/CLIComposeEngine.swift` | ~88% | -| Container creation | `ConsaiCore/Engines/CLIContainerCreator.swift` | ~84% | +| Container creation | `ConsaiCore/Engines/CLIContainerCreator.swift` | ~95% | | Vitals math | `ConsaiCore/Engines/VitalsSampler.swift` | 100% | | Models | `ConsaiCore/Models/*` | 90–100% |