Add a watchOS test target

New Workouts Watch AppTests bundle wired into the watch scheme. Extracts
the phone-to-watch cache apply/prune into a pure, session-free
WatchCacheApplier seam and makes the HR-zone bucketing a nonisolated
static, so both can be unit-tested off the main actor without a live
WatchConnectivity session.

Claude-Session: https://claude.ai/code/session_01HJDQQDA9QdP8zByg43H5v3
This commit is contained in:
2026-07-08 07:57:24 -04:00
parent a4ed4df756
commit 1b399ee7ba
6 changed files with 387 additions and 14 deletions
@@ -142,7 +142,7 @@ final class WorkoutSessionManager: NSObject {
if let maxHeartRate, let prevHR = currentHeartRate, let last = lastHRSampleDate {
let dt = now.timeIntervalSince(last)
if dt > 0, dt < 60 { hrZoneSeconds[zoneIndex(for: prevHR, maxHR: maxHeartRate)] += dt }
if dt > 0, dt < 60 { hrZoneSeconds[Self.zoneIndex(for: prevHR, maxHR: maxHeartRate)] += dt }
}
if let newHR { currentHeartRate = newHR }
lastHRSampleDate = now
@@ -151,7 +151,10 @@ final class WorkoutSessionManager: NSObject {
.sumQuantity()?.doubleValue(for: .kilocalorie())
}
private func zoneIndex(for hr: Double, maxHR: Double) -> Int {
/// Bucket an instantaneous heart rate into one of five zones (04) by its fraction of the
/// user's max HR: <60% 0, 6070% 1, 7080% 2, 8090% 3, 90% 4. Pure (no session
/// or sensor state), so it's `nonisolated static` and unit-testable off the main actor.
nonisolated static func zoneIndex(for hr: Double, maxHR: Double) -> Int {
let ratio = hr / maxHR
let thresholds = [0.6, 0.7, 0.8, 0.9]
return thresholds.reduce(0) { $0 + (ratio >= $1 ? 1 : 0) }