import Foundation import Testing @testable import Trio @Suite("Trio Alerts: GlucoseAlertCoordinator breach/retract") struct GlucoseAlertCoordinatorTests { @Test("low breaches at or below threshold 70") func lowBreach() { #expect(GlucoseAlertCoordinator.breached(type: .low, latestMgDL: 70, thresholdMgDL: 70)) #expect(!GlucoseAlertCoordinator.breached(type: .low, latestMgDL: 71, thresholdMgDL: 70)) } @Test("low retracts only at threshold + margin (70 + 5)") func lowRetract() { #expect(!GlucoseAlertCoordinator.shouldRetract( type: .low, latestMgDL: 74, thresholdMgDL: 70, recoveryMarginMgDL: 5 )) #expect(GlucoseAlertCoordinator.shouldRetract( type: .low, latestMgDL: 75, thresholdMgDL: 70, recoveryMarginMgDL: 5 )) } @Test("urgentLow breaches at or below threshold 54") func urgentLowBreach() { #expect(GlucoseAlertCoordinator.breached(type: .urgentLow, latestMgDL: 54, thresholdMgDL: 54)) #expect(!GlucoseAlertCoordinator.breached(type: .urgentLow, latestMgDL: 55, thresholdMgDL: 54)) } @Test("high breaches at or above threshold 270") func highBreach() { #expect(GlucoseAlertCoordinator.breached(type: .high, latestMgDL: 270, thresholdMgDL: 270)) #expect(!GlucoseAlertCoordinator.breached(type: .high, latestMgDL: 269, thresholdMgDL: 270)) } @Test("high retracts only at threshold - margin (270 - 5)") func highRetract() { #expect(!GlucoseAlertCoordinator.shouldRetract( type: .high, latestMgDL: 266, thresholdMgDL: 270, recoveryMarginMgDL: 5 )) #expect(GlucoseAlertCoordinator.shouldRetract( type: .high, latestMgDL: 265, thresholdMgDL: 270, recoveryMarginMgDL: 5 )) } @Test("type priority order: urgentLow < low < forecastedLow < high < carbsRequired") func priorityOrder() { #expect(GlucoseAlertType.urgentLow.priority == 0) #expect(GlucoseAlertType.low.priority == 1) #expect(GlucoseAlertType.forecastedLow.priority == 2) #expect(GlucoseAlertType.high.priority == 3) #expect(GlucoseAlertType.carbsRequired.priority == 4) #expect(GlucoseAlertType.urgentLow.priority < GlucoseAlertType.low.priority) } @Test("shouldRetract uses the default 5 mg/dL margin when omitted") func defaultMarginWiring() { #expect(GlucoseAlertCoordinator.shouldRetract(type: .low, latestMgDL: 75, thresholdMgDL: 70)) #expect(!GlucoseAlertCoordinator.shouldRetract(type: .low, latestMgDL: 74, thresholdMgDL: 70)) } // MARK: - Carbs Required predicates (mg/dL-based gates are no-ops) /// Carbs Required is determination-driven (`evaluateCarbsRequired(_:)`), /// not reading-driven. The shared mg/dL `breached` predicate must never /// return true for it, regardless of value / threshold combination. @Test("carbsRequired never breaches via the mg/dL predicate") func carbsRequiredNeverBreaches() { #expect(!GlucoseAlertCoordinator.breached(type: .carbsRequired, latestMgDL: 0, thresholdMgDL: 0)) #expect(!GlucoseAlertCoordinator.breached(type: .carbsRequired, latestMgDL: 200, thresholdMgDL: 10)) #expect(!GlucoseAlertCoordinator.breached(type: .carbsRequired, latestMgDL: 10, thresholdMgDL: 200)) } @Test("carbsRequired never retracts via the mg/dL predicate") func carbsRequiredNeverRetractsViaMgDL() { #expect(!GlucoseAlertCoordinator.shouldRetract( type: .carbsRequired, latestMgDL: 100, thresholdMgDL: 10, recoveryMarginMgDL: 5 )) #expect(!GlucoseAlertCoordinator.shouldRetract( type: .carbsRequired, latestMgDL: 0, thresholdMgDL: 50, recoveryMarginMgDL: 5 )) } }