import Foundation import Testing @testable import Trio @Suite("Trio Alerts: GlucoseAlert") struct GlucoseAlertTests { // MARK: - Group A: shouldEvaluate @Test("urgentLow evaluates even when isEnabled is false") func urgentLowDisabledStillEvaluates() { var a = GlucoseAlert(type: .urgentLow) a.isEnabled = false #expect(a.shouldEvaluate == true) } @Test("urgentLow evaluates when isEnabled is true") func urgentLowEnabledEvaluates() { var a = GlucoseAlert(type: .urgentLow) a.isEnabled = true #expect(a.shouldEvaluate == true) } @Test("low does not evaluate when disabled") func lowDisabledDoesNotEvaluate() { var a = GlucoseAlert(type: .low) a.isEnabled = false #expect(a.shouldEvaluate == false) } @Test("low evaluates when enabled") func lowEnabledEvaluates() { var a = GlucoseAlert(type: .low) a.isEnabled = true #expect(a.shouldEvaluate == true) } @Test("high does not evaluate when disabled") func highDisabledDoesNotEvaluate() { var a = GlucoseAlert(type: .high) a.isEnabled = false #expect(a.shouldEvaluate == false) } @Test("high evaluates when enabled") func highEnabledEvaluates() { var a = GlucoseAlert(type: .high) a.isEnabled = true #expect(a.shouldEvaluate == true) } @Test("forecastedLow does not evaluate when disabled") func forecastedLowDisabledDoesNotEvaluate() { var a = GlucoseAlert(type: .forecastedLow) a.isEnabled = false #expect(a.shouldEvaluate == false) } @Test("forecastedLow evaluates when enabled") func forecastedLowEnabledEvaluates() { var a = GlucoseAlert(type: .forecastedLow) a.isEnabled = true #expect(a.shouldEvaluate == true) } @Test("carbsRequired does not evaluate when disabled") func carbsRequiredDisabledDoesNotEvaluate() { var a = GlucoseAlert(type: .carbsRequired) a.isEnabled = false #expect(a.shouldEvaluate == false) } @Test("carbsRequired evaluates when enabled") func carbsRequiredEnabledEvaluates() { var a = GlucoseAlert(type: .carbsRequired) a.isEnabled = true #expect(a.shouldEvaluate == true) } // MARK: - Group A.5: type metadata @Test("carbsRequired defaults: threshold 10 g, bloop.caf, no override") func carbsRequiredDefaults() { let a = GlucoseAlert(type: .carbsRequired) #expect(a.thresholdMgDL == 10) #expect(a.soundFilename == "bloop.caf") #expect(a.overridesSilenceAndDND == false) } @Test("isReadingDriven: true for low family + high, false for forecast + carbs") func isReadingDriven() { #expect(GlucoseAlertType.urgentLow.isReadingDriven) #expect(GlucoseAlertType.low.isReadingDriven) #expect(GlucoseAlertType.high.isReadingDriven) #expect(!GlucoseAlertType.forecastedLow.isReadingDriven) #expect(!GlucoseAlertType.carbsRequired.isReadingDriven) } // MARK: - Group B: decoder defaults @Test("Omitted isEnabled defaults to true") func decodeOmittedIsEnabledDefaultsTrue() throws { let json = """ { "id": "11111111-1111-1111-1111-111111111111", "type": "low", "name": "Low Glucose", "thresholdMgDL": 72 } """ let decoded = try JSONDecoder().decode(GlucoseAlert.self, from: Data(json.utf8)) #expect(decoded.isEnabled == true) } @Test("urgentLow with isEnabled false decodes false but still evaluates") func decodeUrgentLowDisabledStillEvaluates() throws { let json = """ { "id": "22222222-2222-2222-2222-222222222222", "type": "urgentLow", "name": "Urgent Low Glucose", "isEnabled": false, "thresholdMgDL": 54 } """ let decoded = try JSONDecoder().decode(GlucoseAlert.self, from: Data(json.utf8)) #expect(decoded.isEnabled == false) #expect(decoded.shouldEvaluate == true) } @Test("high with optional fields omitted uses type defaults") func decodeHighDefaults() throws { let json = """ { "id": "33333333-3333-3333-3333-333333333333", "type": "high", "name": "High Glucose", "thresholdMgDL": 270 } """ let decoded = try JSONDecoder().decode(GlucoseAlert.self, from: Data(json.utf8)) #expect(decoded.soundFilename == GlucoseAlertType.high.defaultSoundFilename) #expect(decoded.playsSound == true) #expect(decoded.overridesSilenceAndDND == false) #expect(decoded.activeOption == .always) #expect(decoded.snoozedUntil == nil) #expect(decoded.isEnabled == true) } @Test("urgentLow with overridesSilenceAndDND omitted uses type defaults") func decodeUrgentLowOverrideDefault() throws { let json = """ { "id": "44444444-4444-4444-4444-444444444444", "type": "urgentLow", "name": "Urgent Low Glucose", "thresholdMgDL": 54 } """ let decoded = try JSONDecoder().decode(GlucoseAlert.self, from: Data(json.utf8)) #expect(decoded.overridesSilenceAndDND == true) #expect(decoded.soundFilename == GlucoseAlertType.urgentLow.defaultSoundFilename) } // MARK: - Group C: round-trip @Test("Fully-populated value survives encode/decode round-trip") func roundTrip() throws { var original = GlucoseAlert(type: .low) original.name = "Custom Low Alarm" original.isEnabled = false original.thresholdMgDL = 65 original.soundFilename = "custom_sound.caf" original.playsSound = false original.overridesSilenceAndDND = true original.activeOption = .night original.snoozedUntil = Date(timeIntervalSinceReferenceDate: 1_000_000) let data = try JSONEncoder().encode(original) let decoded = try JSONDecoder().decode(GlucoseAlert.self, from: data) #expect(decoded == original) } }