| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291 |
- import Foundation
- import LoopKit
- import Testing
- @testable import Trio
- /// Pins Trio's alert-routing behavior for the **TandemKit** pump plugin.
- ///
- /// Rows are derived from the synthesis audit of the Tandem stack
- /// (`TandemCore` / `TandemKit` / `TandemKitUI` / `TandemKitPlugin`; pump:
- /// Tandem t:slim X2 / Mobi). The audit's central finding is STRUCTURAL:
- /// TandemKit issues **zero** LoopKit Alerts. There is no
- /// `issueAlert` / `retractAlert` / `Alert(identifier:)` / `DeviceAlert` /
- /// `UNUserNotificationCenter` usage anywhere in the shipping targets
- /// (audit.md:7-8, Notes:192), so TandemKit never constructs an
- /// `Alert.Identifier` and there is no `(managerIdentifier, alertIdentifier)`
- /// pair for Trio to route. The PRIMARY emitted-alert table is therefore empty
- /// and `AlertCatalogRegistry` has — and could match — no Tandem entries.
- ///
- /// Every Tandem pump alarm the device surfaces (occlusion N1->Critical;
- /// empty/removed cartridge & "No Insulin" N4->Critical; battery-shutdown
- /// N5-pump->Critical; temperature/altitude/stuck-button/invalid-date/
- /// pump-reset & the non-dismissable Malfunction item N1->Critical;
- /// resume-pump / auto-off delivery-stopped N2->Critical; CGM-alert catalog
- /// N7->High) is rendered ONLY inside TandemKit's own in-app
- /// "Pump notifications" settings list
- /// (`NotificationBundle` -> `TandemKitNotificationsView`), which Trio never
- /// receives. The `AlertResponseType` description catalog is intercepted in
- /// `fetchNotifications` (audit:129,193) and is dead for display, and the lone
- /// `didError` call site (`TandemPumpManager.swift:218`) has no callers.
- ///
- /// GAP SUMMARY: the effective interruption level of every taxonomy-Critical
- /// Tandem alarm is "never delivered" — strictly less severe than `.critical`.
- /// This is broader than a registry-key mismatch: no `Alert.Identifier` is
- /// ever issued, so no escalation is possible until upstream TandemKit issues
- /// LoopKit Alerts (and adopts `AlertCatalogVendor`, or Trio adds Tandem
- /// registry entries). The remediation gap is documented in this suite's prose
- /// rather than as table rows, because there are no emitted-alert rows to ratchet.
- ///
- /// The only Tandem signals that DO reach Trio are `PumpManagerError`-wrapped
- /// `PumpCommError` / `TandemPumpManagerValidationError` values returned through
- /// `PumpManager` dosing/limit/time completion handlers (e.g. `enactTempBasal`
- /// -> `APSManager:703` `APSError.pumpError(error)` -> `processError` ->
- /// `TrioAlertClassifier.categorize`). These are error-STRING emissions, not
- /// Alerts. No classifier rows are pinned here: the TandemKit submodule is not
- /// present in Trio-dev and the audit recorded the rendered `errorDescription`
- /// text rather than the `String(describing:)` form, so the exact classifier
- /// input (a LoopKit `PumpManagerError` enum — not `CustomStringConvertible` —
- /// wrapping a `PumpCommError`/validation case) cannot be stated with
- /// certainty. Per the SCOPE rule those are omitted rather than guessed.
- @Suite("Trio Alert Emission: TandemKit") struct TandemKitAlertEmissionTests {
- /// TandemKit issues no LoopKit Alerts, so it carries no real
- /// `managerIdentifier`. We keep the audit's verbatim placeholder string so
- /// the (empty) registry-lookup pin documents exactly what was searched.
- private static let managerIdentifier =
- "(none — TandemKit issues no LoopKit Alerts; it uses no Alert.Identifier at all)"
- private func id(_ manager: String, _ alertID: String) -> Alert.Identifier {
- Alert.Identifier(managerIdentifier: manager, alertIdentifier: alertID)
- }
- // MARK: - Emitted alerts (PRIMARY table)
- /// `(alertIdentifier, expectedRegistryLevel)`.
- ///
- /// EMPTY: TandemKit issues no LoopKit Alerts (audit.md:7-8, Notes:192), so
- /// there are no `(managerIdentifier, alertIdentifier)` pairs to pin. If this
- /// table ever gains a row, the audit's structural finding has changed and
- /// this suite's doc comment must be revisited.
- static let alertRows: [(alertID: String, expectedLevel: Alert.InterruptionLevel?)] = []
- @Test(
- "registry behavior is pinned for every emitted alert",
- arguments: alertRows
- ) func registryBehaviorIsPinned(row: (alertID: String, expectedLevel: Alert.InterruptionLevel?)) {
- let identifier = id(Self.managerIdentifier, row.alertID)
- #expect(AlertCatalogRegistry.lookup(identifier)?.interruptionLevel == row.expectedLevel)
- }
- /// Backstop for the empty PRIMARY table: even the audit's verbatim
- /// placeholder manager string resolves to no catalog entry. This pins the
- /// structural fact that TandemKit has no escalatable surface in Trio today.
- @Test("no Tandem alert identifier resolves in the catalog registry") func noTandemEntryInRegistry() {
- #expect(Self.alertRows.isEmpty)
- #expect(AlertCatalogRegistry.lookup(id(Self.managerIdentifier, "anything")) == nil)
- }
- // MARK: - Documented escalation gaps (ratchet)
- /// `alertIdentifier`s of emitted alerts whose EFFECTIVE current level is
- /// less severe than their taxonomy level.
- ///
- /// EMPTY by construction: a gap row requires an emitted alert with an
- /// `Alert.Identifier`, and TandemKit emits none. The Tandem alarms that the
- /// taxonomy rates Critical (occlusion N1, empty/removed cartridge & "No
- /// Insulin" N4, battery-shutdown N5-pump, Malfunction/temperature/altitude/
- /// stuck-button/invalid-date/pump-reset N1, resume-pump / auto-off N2) and
- /// High (CGM-alert catalog N7) never reach Trio at all — they live only in
- /// TandemKit's in-app "Pump notifications" list (`NotificationBundle` ->
- /// `TandemKitNotificationsView`; audit.md:7-8, 129, 192-193). That is a
- /// STRUCTURAL gap (no `Alert.Identifier` is ever issued), strictly broader
- /// than a registry-key mismatch, and is documented in the suite prose
- /// rather than as a ratchetable row. The fix is upstream: TandemKit must
- /// issue LoopKit Alerts and adopt `AlertCatalogVendor` (or Trio must add
- /// Tandem registry entries) before any escalation is possible.
- static let knownEscalationGaps: Set<String> = []
- @Test("known escalation gaps are exactly as documented") func knownEscalationGapsAreExactlyAsDocumented() {
- // Recompute the gap set from the PRIMARY table: a row is a gap when its
- // effective level (registry level if present, else unknown) is missing.
- // With no rows this is empty and matches the documented (empty) set.
- // Should TandemKit ever start issuing Alerts and a row be added with an
- // unescalated level, this recomputed set will diverge from
- // `knownEscalationGaps`, failing the test and prompting an update.
- var computed = Set<String>()
- for row in Self.alertRows where row.expectedLevel != .critical {
- // Placeholder recompute: real gap math would compare against the
- // row's taxonomy level. No rows exist, so the loop never executes.
- computed.insert(row.alertID)
- }
- #expect(computed == Self.knownEscalationGaps)
- }
- }
- // MARK: - Message classification (SECONDARY: error-string routing)
- /// SPEC — how Trio's `TrioAlertClassifier` would bucket every user-facing
- /// **TandemKit** message string, pinned against the live substring matcher.
- ///
- /// IMPORTANT — what production actually feeds the classifier vs. what this
- /// suite feeds it: in production `APSManager` hands
- /// `TrioAlertClassifier.categorize(error:)` a LoopKit `PumpManagerError`
- /// wrapping a `PumpCommError` / `TandemPumpManagerValidationError`, and the
- /// classifier runs `String(describing:)` over THAT — i.e. it sees the Swift
- /// **case name** (e.g. `pumpError(...)` / `pumpNotConnected`), not the rendered
- /// display string. TandemKit also uses NONE of the LoopKit Alert / AlertIssuer
- /// machinery and never delivers a live `pumpManager(_:didError:)`, so there are
- /// no real LoopKit `Alert.alertIdentifier` values to attach (that structural
- /// finding is pinned by the registry-routing suite above). Every user-facing
- /// row here surfaces instead through one of: (1) `PumpCommError` /
- /// `TandemPumpManagerValidationError` wrapped in `PumpManagerError` returned to
- /// Loop completion handlers, (2) the in-app "Pump notifications" settings list
- /// (`NotificationBundle` items whose displayed string is the formatted enum
- /// case-name title), (3) `pumpStatusHighlight` strings, (4) SwiftUI
- /// `.alert`/ActionSheet/ErrorSheet text, and (5) CGM settings status rows. The
- /// `alertIdentifier` column is therefore the error enum case name / source
- /// symbol for the row, NOT a LoopKit alert id.
- ///
- /// This catalog deliberately feeds the classifier the **display string** of
- /// each emission so we pin how the substring matcher handles real
- /// natural-language text — the worst case for a token matcher. The classifier's
- /// `categorize(pumpError:)` tokens are concatenated and space-free
- /// ("notconnected", "noresponse", "reservoirempty", "sensorfailed", ...), while
- /// TandemKit's prose has spaces, so almost every emission falls through to
- /// `.other`. Only strings literally containing "occlusion"/"occluded" hit a real
- /// bucket. Connectivity strings ("Pump not connected", "No response from pump",
- /// "Pump Disconnected") do NOT match "notconnected"/"noresponse" because of the
- /// spaces and wrongly land in `.other` — the principal gaps. See
- /// `Trio/Sources/Services/Alerts/TrioAlertCategory.swift`.
- ///
- /// References: managers — TandemKit / TandemKitUI / TandemCore; pump —
- /// Tandem t:slim X2 / Mobi.
- @Suite("Trio Alert Emission: TandemKit — Classification") struct TandemKitMessageClassificationTests {
- /// A throwaway `Error` whose `String(describing:)` is exactly the emission
- /// display string, so `categorize(error:)` matches over natural-language
- /// text (mind the spaces: "reservoir is empty" does NOT contain
- /// "reservoirempty"; most prose -> `.other`).
- private struct StubError: Error, CustomStringConvertible { let description: String }
- /// One reportable message: its source identifier (error case / symbol),
- /// the exact display string, its UI role, taxonomy node, and the category
- /// the current classifier pins it to.
- struct Row {
- let identifier: String
- let message: String
- let role: String
- let taxonomy: String
- let expected: TrioAlertCategory
- }
- static let rows: [Row] = [
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:157
- Row(
- identifier: "AlarmResponseType.OCCLUSION_ALARM",
- message: "Occlusion Alarm",
- role: "alertTitle",
- taxonomy: "N1",
- expected: .occlusion
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:157
- Row(
- identifier: "AlarmResponseType.OCCLUSION_ALARM",
- message: "An occlusion has occurred. Please check your pump site and tubing and restart insulin delivery.",
- role: "alertBody",
- taxonomy: "N1",
- expected: .occlusion
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:145
- Row(
- identifier: "AlarmResponseType.cartridgeGroup",
- message: "Cartridge Alarm",
- role: "alertTitle",
- taxonomy: "N1",
- expected: .other("Cartridge Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:145
- Row(
- identifier: "AlarmResponseType.cartridgeGroup",
- message: "There is an issue with the cartridge and it needs to be replaced.",
- role: "alertBody",
- taxonomy: "N1",
- expected: .other("There is an issue with the cartridge and it needs to be replaced.")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:183
- Row(
- identifier: "AlarmResponseType.CARTRIDGE_REMOVED_ALARM",
- message: "Cartridge Removed Alarm",
- role: "alertTitle",
- taxonomy: "N1",
- expected: .other("Cartridge Removed Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:183
- Row(
- identifier: "AlarmResponseType.CARTRIDGE_REMOVED_ALARM",
- message: "The cartridge was removed from the pump. Please fill a new cartridge.",
- role: "alertBody",
- taxonomy: "N1",
- expected: .other("The cartridge was removed from the pump. Please fill a new cartridge.")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:160
- Row(
- identifier: "AlarmResponseType.PUMP_RESET_ALARM",
- message: "Pump Reset Alarm",
- role: "alertTitle",
- taxonomy: "N1",
- expected: .other("Pump Reset Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:160
- Row(
- identifier: "AlarmResponseType.PUMP_RESET_ALARM",
- message: "The pump was reset. IOB has been reset to 0 and CGM may need to be re-activated.",
- role: "alertBody",
- taxonomy: "N1",
- expected: .other("The pump was reset. IOB has been reset to 0 and CGM may need to be re-activated.")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:166
- Row(
- identifier: "AlarmResponseType.temperatureGroup",
- message: "Temperature Alarm",
- role: "alertTitle",
- taxonomy: "N1",
- expected: .other("Temperature Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:166
- Row(
- identifier: "AlarmResponseType.temperatureGroup",
- message: "Pump temperature is out of range and insulin cannot be safely delivered.",
- role: "alertBody",
- taxonomy: "N1",
- expected: .other("Pump temperature is out of range and insulin cannot be safely delivered.")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:177
- Row(
- identifier: "AlarmResponseType.ALTITUDE_ALARM",
- message: "Altitude Alarm",
- role: "alertTitle",
- taxonomy: "N1",
- expected: .other("Altitude Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:181
- Row(
- identifier: "AlarmResponseType.ATMOSPHERIC_PRESSURE_OUT_OF_RANGE_ALARM",
- message: "Atmospheric Pressure Out Of Range Alarm",
- role: "alertTitle",
- taxonomy: "N1",
- expected: .other("Atmospheric Pressure Out Of Range Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:179
- Row(
- identifier: "AlarmResponseType.STUCK_BUTTON_ALARM",
- message: "Stuck Button Alarm",
- role: "alertTitle",
- taxonomy: "N1",
- expected: .other("Stuck Button Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:179
- Row(
- identifier: "AlarmResponseType.STUCK_BUTTON_ALARM",
- message: "The pump button may be stuck or has been pressed for too long a period of time.",
- role: "alertBody",
- taxonomy: "N1",
- expected: .other("The pump button may be stuck or has been pressed for too long a period of time.")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:172
- Row(
- identifier: "AlarmResponseType.INVALID_DATE_ALARM",
- message: "Invalid Date Alarm",
- role: "alertTitle",
- taxonomy: "N1",
- expected: .other("Invalid Date Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:172
- Row(
- identifier: "AlarmResponseType.INVALID_DATE_ALARM",
- message: "The pump's configured date is invalid.",
- role: "alertBody",
- taxonomy: "N1",
- expected: .other("The pump's configured date is invalid.")
- ),
- // TandemCore/Messages/CurrentStatus/HighestAam.swift:106
- Row(
- identifier: "HighestAamResponse.malfunction",
- message: "Malfunction",
- role: "alertTitle",
- taxonomy: "N1",
- expected: .other("Malfunction")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:174
- Row(
- identifier: "AlarmResponseType.RESUME_PUMP_ALARM",
- message: "Resume Pump Alarm",
- role: "alertTitle",
- taxonomy: "N2",
- expected: .other("Resume Pump Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:174
- Row(
- identifier: "AlarmResponseType.RESUME_PUMP_ALARM",
- message: "Insulin delivery is currently off. Please restart insulin delivery soon.",
- role: "alertBody",
- taxonomy: "N2",
- expected: .other("Insulin delivery is currently off. Please restart insulin delivery soon.")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:162
- Row(
- identifier: "AlarmResponseType.AUTO_OFF_ALARM",
- message: "Auto Off Alarm",
- role: "alertTitle",
- taxonomy: "N2",
- expected: .other("Auto Off Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:162
- Row(
- identifier: "AlarmResponseType.AUTO_OFF_ALARM",
- message: "Pump will stop delivering insulin automatically soon because no user activity has occurred and the auto-off setting is enabled.",
- role: "alertBody",
- taxonomy: "N2",
- expected: .other(
- "Pump will stop delivering insulin automatically soon because no user activity has occurred and the auto-off setting is enabled."
- )
- ),
- // TandemKitUI/TandemPumpManager+UI.swift:112
- Row(
- identifier: "PumpStatusHighlight.suspended",
- message: "Insulin Suspended",
- role: "notificationTitle",
- taxonomy: "N2",
- expected: .other("Insulin Suspended")
- ),
- // TandemKitUI/TandemPumpManager+UI.swift:123
- Row(
- identifier: "PumpStatusHighlight.noInsulin",
- message: "No Insulin",
- role: "notificationTitle",
- taxonomy: "N4",
- expected: .other("No Insulin")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:164
- Row(
- identifier: "AlarmResponseType.EMPTY_CARTRIDGE_ALARM",
- message: "Empty Cartridge Alarm",
- role: "alertTitle",
- taxonomy: "N4",
- expected: .other("Empty Cartridge Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:164
- Row(
- identifier: "AlarmResponseType.EMPTY_CARTRIDGE_ALARM",
- message: "Cartridge is out of insulin and insulin delivery cannot occur. Please fill a new cartridge.",
- role: "alertBody",
- taxonomy: "N4",
- expected: .other("Cartridge is out of insulin and insulin delivery cannot occur. Please fill a new cartridge.")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:170
- Row(
- identifier: "AlarmResponseType.BATTERY_SHUTDOWN_ALARM",
- message: "Battery Shutdown Alarm",
- role: "alertTitle",
- taxonomy: "N5",
- expected: .other("Battery Shutdown Alarm")
- ),
- // TandemCore/Messages/CurrentStatus/AlarmStatus.swift:170
- Row(
- identifier: "AlarmResponseType.BATTERY_SHUTDOWN_ALARM",
- message: "Pump battery level is critically low and the device will shut down. Please charge pump immediately.",
- role: "alertBody",
- taxonomy: "N5",
- expected: .other(
- "Pump battery level is critically low and the device will shut down. Please charge pump immediately."
- )
- ),
- // TandemCore/Messages/CurrentStatus/CGMAlertStatus.swift:135
- Row(
- identifier: "CGMAlert.SENSOR_FAILED",
- message: "Sensor Failed Cgm Alert",
- role: "alertTitle",
- taxonomy: "N7",
- expected: .other("Sensor Failed Cgm Alert")
- ),
- // TandemCore/Messages/CurrentStatus/CGMAlertStatus.swift:135
- Row(
- identifier: "CGMAlert.LOW",
- message: "Low Cgm Alert",
- role: "alertTitle",
- taxonomy: "N7",
- expected: .other("Low Cgm Alert")
- ),
- // TandemCore/Messages/CurrentStatus/CGMAlertStatus.swift:135
- Row(
- identifier: "CGMAlert.HIGH",
- message: "High Cgm Alert",
- role: "alertTitle",
- taxonomy: "N7",
- expected: .other("High Cgm Alert")
- ),
- // TandemCore/Messages/CurrentStatus/CGMAlertStatus.swift:135
- Row(
- identifier: "CGMAlert.FIXED_LOW",
- message: "Fixed Low Cgm Alert",
- role: "alertTitle",
- taxonomy: "N7",
- expected: .other("Fixed Low Cgm Alert")
- ),
- // TandemCore/Messages/CurrentStatus/CGMAlertStatus.swift:135
- Row(
- identifier: "CGMAlert.SENSOR_EXPIRING",
- message: "Sensor Expiring Cgm Alert",
- role: "alertTitle",
- taxonomy: "N7",
- expected: .other("Sensor Expiring Cgm Alert")
- ),
- // TandemCore/Messages/CurrentStatus/CGMAlertStatus.swift:135
- Row(
- identifier: "CGMAlert.SENSOR_EXPIRED",
- message: "Sensor Expired Cgm Alert",
- role: "alertTitle",
- taxonomy: "N7",
- expected: .other("Sensor Expired Cgm Alert")
- ),
- // TandemCore/Messages/CurrentStatus/CGMAlertStatus.swift:135
- Row(
- identifier: "CGMAlert.OUT_OF_RANGE",
- message: "Out Of Range Cgm Alert",
- role: "alertTitle",
- taxonomy: "N7",
- expected: .other("Out Of Range Cgm Alert")
- ),
- // TandemCore/Messages/CurrentStatus/CGMAlertStatus.swift:135
- Row(
- identifier: "CGMAlert.TRANSMITTER",
- message: "Transmitter Cgm Alert",
- role: "alertTitle",
- taxonomy: "N7",
- expected: .other("Transmitter Cgm Alert")
- ),
- // TandemCore/Messages/CurrentStatus/ReminderStatus.swift:129
- Row(
- identifier: "ReminderType.namedCases",
- message: "Reminder",
- role: "alertTitle",
- taxonomy: "N14",
- expected: .other("Reminder")
- ),
- // TandemKit/PumpManager/TandemPumpManager.swift:341
- Row(
- identifier: "PumpCommError.pumpNotConnected",
- message: "Pump not connected",
- role: "errorMessage",
- taxonomy: "N8",
- expected: .other("Pump not connected")
- ),
- // TandemKit/PumpManager/TandemPumpManager.swift:341
- Row(
- identifier: "PumpCommError.pumpNotConnected",
- message: "Make sure iPhone is nearby the active pump",
- role: "errorMessage",
- taxonomy: "N8",
- expected: .other("Make sure iPhone is nearby the active pump")
- ),
- // TandemKit/PumpManager/TandemPumpManager.swift:362
- Row(
- identifier: "PumpCommError.noResponse",
- message: "No response from pump",
- role: "errorMessage",
- taxonomy: "N8",
- expected: .other("No response from pump")
- ),
- // TandemKit/PumpManager/TandemPeripheralManager.swift:227
- Row(
- identifier: "PumpCommError.errorResponse",
- message: "Pump Error: Failed to discoverServices: <error>",
- role: "errorMessage",
- taxonomy: "N8",
- expected: .other("Pump Error: Failed to discoverServices: <error>")
- ),
- // TandemKit/PumpManager/TandemPeripheralManager.swift:235
- Row(
- identifier: "PumpCommError.errorResponse",
- message: "Pump Error: No service: <PUMP_SERVICE>",
- role: "errorMessage",
- taxonomy: "N8",
- expected: .other("Pump Error: No service: <PUMP_SERVICE>")
- ),
- // TandemKit/PumpManager/TandemPeripheralManager.swift:245
- Row(
- identifier: "PumpCommError.errorResponse",
- message: "Pump Error: <message>",
- role: "errorMessage",
- taxonomy: "N8",
- expected: .other("Pump Error: <message>")
- ),
- // TandemKit/PumpManager/TandemPeripheralManager.swift:254
- Row(
- identifier: "PumpCommError.errorResponse",
- message: "Pump Error: No service: <DIS_SERVICE>",
- role: "errorMessage",
- taxonomy: "N8",
- expected: .other("Pump Error: No service: <DIS_SERVICE>")
- ),
- // TandemKit/PumpManager/TandemBluetoothManager.swift:318
- Row(
- identifier: "PumpCommError.errorResponse",
- message: "Pump Error: Failed to connect: <error>",
- role: "errorMessage",
- taxonomy: "N8",
- expected: .other("Pump Error: Failed to connect: <error>")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:230-233
- Row(
- identifier: "TandemKitSettingsView.pumpDisconnectedBanner",
- message: "Pump Disconnected",
- role: "notificationTitle",
- taxonomy: "N8",
- expected: .other("Pump Disconnected")
- ),
- // TandemKit/PumpManager/TandemPumpManager.swift:346
- Row(
- identifier: "TandemPumpManagerValidationError.controlIQModeActive",
- message: "The operation couldn't be completed.",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("The operation couldn't be completed.")
- ),
- // TandemKit/PumpManager/TandemPumpManager.swift:509
- Row(
- identifier: "PumpCommError.invalidScheduledBasalRate",
- message: "Invalid scheduled basal rate",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Invalid scheduled basal rate")
- ),
- // TandemKit/PumpManager/TandemPumpManager.swift:509
- Row(
- identifier: "PumpCommError.invalidScheduledBasalRate",
- message: "Temp basal cannot be performed while scheduled basal rate is 0 or no basal profile exists",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Temp basal cannot be performed while scheduled basal rate is 0 or no basal profile exists")
- ),
- // TandemKit/PumpManager/TandemPumpManager.swift:368
- Row(
- identifier: "PumpCommError.invalidPacket",
- message: "Other PumpCommError: invalidPacket",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Other PumpCommError: invalidPacket")
- ),
- // TandemKit/PumpManager/TandemPumpManager.swift:436
- Row(
- identifier: "PumpCommError.noActiveBolus",
- message: "No active bolus to cancel",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("No active bolus to cancel")
- ),
- // TandemKit/PumpManager/TandemPumpManager.swift:436
- Row(
- identifier: "PumpCommError.noActiveBolus",
- message: "No bolus is running.",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("No bolus is running.")
- ),
- // TandemKit/PumpManager/TandemPumpManager.swift:897
- Row(
- identifier: "PumpCommError.errorResponse",
- message: "Pump Error: Error syncing time with pump",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Pump Error: Error syncing time with pump")
- ),
- // TandemKit/PumpManager/TandemPumpManager+SyncBasal.swift:33
- Row(
- identifier: "PumpCommError.requestConstructionFailed",
- message: "Failed to build pump request: Invalid IDPManager state in syncBasalRateSchedule",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Failed to build pump request: Invalid IDPManager state in syncBasalRateSchedule")
- ),
- // TandemKit/PumpManager/TandemPumpManager+SyncBasal.swift:33
- Row(
- identifier: "PumpCommError.requestConstructionFailed",
- message: "Please try again. If the problem persists, re-pair your pump.",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Please try again. If the problem persists, re-pair your pump.")
- ),
- // TandemKit/PumpManager/TandemPumpManager+SyncBasal.swift:68
- Row(
- identifier: "PumpCommError.requestConstructionFailed",
- message: "Failed to build pump request: No active profile found and got unexpected response creating initial profile",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other(
- "Failed to build pump request: No active profile found and got unexpected response creating initial profile"
- )
- ),
- // TandemKit/PumpManager/TandemPumpManager+SyncBasal.swift:106
- Row(
- identifier: "PumpCommError.requestConstructionFailed",
- message: "Failed to build pump request: No active profile found",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Failed to build pump request: No active profile found")
- ),
- // TandemKit/PumpManager/TandemPumpManager+SyncBasal.swift:166
- Row(
- identifier: "PumpCommError.requestConstructionFailed",
- message: "Failed to build pump request: Pump rejected SetIDPSegment",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Failed to build pump request: Pump rejected SetIDPSegment")
- ),
- // TandemKit/PumpManager/TandemPumpManager+SyncBasal.swift:172
- Row(
- identifier: "PumpCommError.errorResponse",
- message: "Pump Error: Error syncing basal rates: Pump rejected SetIDPSegment",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Pump Error: Error syncing basal rates: Pump rejected SetIDPSegment")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:74-84
- Row(
- identifier: "CommandFailure.resume",
- message: "Could not resume insulin delivery",
- role: "alertTitle",
- taxonomy: "N9",
- expected: .other("Could not resume insulin delivery")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:74-84
- Row(
- identifier: "CommandFailure.resume",
- message: "The pump rejected starting insulin delivery. Check \"View Notifications\" for more details. The pump may be reporting an occlusion, empty cartridge, or not filling the tubing.",
- role: "alertBody",
- taxonomy: "N9",
- expected: .occlusion
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:85-96
- Row(
- identifier: "CommandFailure.suspend",
- message: "Could not suspend insulin delivery",
- role: "alertTitle",
- taxonomy: "N9",
- expected: .other("Could not suspend insulin delivery")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:85-96
- Row(
- identifier: "CommandFailure.suspend",
- message: "The pump rejected suspending insulin delivery. To urgently suspend insulin delivery, detach from your site.",
- role: "alertBody",
- taxonomy: "N9",
- expected: .other(
- "The pump rejected suspending insulin delivery. To urgently suspend insulin delivery, detach from your site."
- )
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:97-107
- Row(
- identifier: "CommandFailure.controlIQ",
- message: "Could not change Control-IQ setting",
- role: "alertTitle",
- taxonomy: "N9",
- expected: .other("Could not change Control-IQ setting")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:97-107
- Row(
- identifier: "CommandFailure.controlIQ",
- message: "The pump rejected the Control-IQ change. Check \"View Notifications\" for more details.",
- role: "alertBody",
- taxonomy: "N9",
- expected: .other("The pump rejected the Control-IQ change. Check \"View Notifications\" for more details.")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:108-118
- Row(
- identifier: "CommandFailure.exerciseMode",
- message: "Could not change exercise mode",
- role: "alertTitle",
- taxonomy: "N9",
- expected: .other("Could not change exercise mode")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:108-118
- Row(
- identifier: "CommandFailure.exerciseMode",
- message: "The pump rejected the exercise mode change.",
- role: "alertBody",
- taxonomy: "N9",
- expected: .other("The pump rejected the exercise mode change.")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:119-129
- Row(
- identifier: "CommandFailure.sleepMode",
- message: "Could not change sleep mode",
- role: "alertTitle",
- taxonomy: "N9",
- expected: .other("Could not change sleep mode")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:119-129
- Row(
- identifier: "CommandFailure.sleepMode",
- message: "The pump rejected the sleep mode change.",
- role: "alertBody",
- taxonomy: "N9",
- expected: .other("The pump rejected the sleep mode change.")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:130-140
- Row(
- identifier: "CommandFailure.stopTempRate",
- message: "Could not stop temp rate",
- role: "alertTitle",
- taxonomy: "N9",
- expected: .other("Could not stop temp rate")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:130-140
- Row(
- identifier: "CommandFailure.stopTempRate",
- message: "The pump rejected stopping the temp rate.",
- role: "alertBody",
- taxonomy: "N9",
- expected: .other("The pump rejected stopping the temp rate.")
- ),
- // TandemKitUI/ViewModels/Settings/TandemKitControlIQSettingsViewModel.swift:94
- Row(
- identifier: "TandemKitControlIQSettingsViewModel.saveError",
- message: "Failed to update Control-IQ settings. Check pump connection.",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Failed to update Control-IQ settings. Check pump connection.")
- ),
- // TandemKitUI/ViewModels/Settings/TandemKitControlIQSettingsViewModel.swift:115
- Row(
- identifier: "TandemKitControlIQSettingsViewModel.saveError",
- message: "Failed to update sleep schedule. Check pump connection.",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Failed to update sleep schedule. Check pump connection.")
- ),
- // TandemKitUI/ViewModels/Settings/TandemKitNotificationsViewModel.swift:54
- Row(
- identifier: "TandemKitNotificationsViewModel.errorMessage",
- message: "Failed to dismiss notification",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Failed to dismiss notification")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:143
- Row(
- identifier: "TandemKitCGMSettingsViewModel.commandError",
- message: "Failed to stop CGM sensor. Check pump connection.",
- role: "errorMessage",
- taxonomy: "N9",
- expected: .other("Failed to stop CGM sensor. Check pump connection.")
- ),
- // TandemKitUI/Views/Onboarding/TandemKitScanView.swift:71-106
- Row(
- identifier: "TandemKitScanView.pairingCodePrompt",
- message: "Enter Pairing code",
- role: "alertTitle",
- taxonomy: "N10",
- expected: .other("Enter Pairing code")
- ),
- // TandemKitUI/Views/Onboarding/TandemKitScanView.swift:71-106
- Row(
- identifier: "TandemKitScanView.pairingCodePrompt",
- message: "Enter the pairing code from your pump",
- role: "alertBody",
- taxonomy: "N10",
- expected: .other("Enter the pairing code from your pump")
- ),
- // TandemKitUI/Views/Onboarding/TandemKitScanView.swift:71-106
- Row(
- identifier: "TandemKitScanView.pairingCodePrompt",
- message: "The pairing code is located on the inside of the cartridge area labeled as 'PIN' and consists of six numbers.",
- role: "alertBody",
- taxonomy: "N10",
- expected: .other(
- "The pairing code is located on the inside of the cartridge area labeled as 'PIN' and consists of six numbers."
- )
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:83-88
- Row(
- identifier: "TandemKitCGMSettingsViewModel.transmitterBatteryStatus",
- message: "OK",
- role: "validation",
- taxonomy: "N11",
- expected: .other("OK")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:83-88
- Row(
- identifier: "TandemKitCGMSettingsViewModel.transmitterBatteryStatus",
- message: "Error",
- role: "validation",
- taxonomy: "N11",
- expected: .other("Error")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:83-88
- Row(
- identifier: "TandemKitCGMSettingsViewModel.transmitterBatteryStatus",
- message: "Expired",
- role: "validation",
- taxonomy: "N11",
- expected: .other("Expired")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:83-88
- Row(
- identifier: "TandemKitCGMSettingsViewModel.transmitterBatteryStatus",
- message: "Out of Range",
- role: "validation",
- taxonomy: "N11",
- expected: .other("Out of Range")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:83-88
- Row(
- identifier: "TandemKitCGMSettingsViewModel.transmitterBatteryStatus",
- message: "Unavailable",
- role: "validation",
- taxonomy: "N11",
- expected: .other("Unavailable")
- ),
- // TandemKitUI/ViewModels/Onboarding/TandemKitScanViewModel.swift:52
- Row(
- identifier: "TandemKitScanViewModel.pinCodePromptError",
- message: "Please enter a pairing code",
- role: "validation",
- taxonomy: "N12",
- expected: .other("Please enter a pairing code")
- ),
- // TandemKitUI/ViewModels/Onboarding/TandemKitScanViewModel.swift:61
- Row(
- identifier: "TandemKitScanViewModel.connectionErrorMessage",
- message: "Pump manager not initialized",
- role: "errorMessage",
- taxonomy: "N12",
- expected: .other("Pump manager not initialized")
- ),
- // TandemKitUI/ViewModels/Onboarding/TandemKitScanViewModel.swift:67
- Row(
- identifier: "TandemKitScanViewModel.connectionErrorMessage",
- message: "No peripheral selected",
- role: "errorMessage",
- taxonomy: "N12",
- expected: .other("No peripheral selected")
- ),
- // TandemKitUI/Views/Onboarding/TandemKitScanView.swift:62-70
- Row(
- identifier: "TandemKitScanView.connectionErrorAlert",
- message: "Error while connecting to device",
- role: "alertTitle",
- taxonomy: "N12",
- expected: .other("Error while connecting to device")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitG6SetupViewModel.swift:71
- Row(
- identifier: "TandemKitG6SetupViewModel.error",
- message: "Failed to set transmitter.",
- role: "errorMessage",
- taxonomy: "N12",
- expected: .other("Failed to set transmitter.")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitG6SetupViewModel.swift:96
- Row(
- identifier: "TandemKitG6SetupViewModel.error",
- message: "Failed to start CGM sensor",
- role: "errorMessage",
- taxonomy: "N12",
- expected: .other("Failed to start CGM sensor")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitG7SetupViewModel.swift:48
- Row(
- identifier: "TandemKitG7SetupViewModel.error",
- message: "Failed to set G7 pairing code",
- role: "errorMessage",
- taxonomy: "N12",
- expected: .other("Failed to set G7 pairing code")
- ),
- // TandemKitUI/Views/CGMs/TandemKitCGMSettingsView.swift:62-72
- Row(
- identifier: "TandemKitCGMSettingsView.stopConfirmation",
- message: "Stop CGM Sensor",
- role: "alertTitle",
- taxonomy: "N12",
- expected: .other("Stop CGM Sensor")
- ),
- // TandemKitUI/Views/CGMs/TandemKitCGMSettingsView.swift:62-72
- Row(
- identifier: "TandemKitCGMSettingsView.stopConfirmation",
- message: "Are you sure you want to stop the CGM sensor session on your pump?",
- role: "alertBody",
- taxonomy: "N12",
- expected: .other("Are you sure you want to stop the CGM sensor session on your pump?")
- ),
- // TandemKitUI/Views/CGMs/TandemKitG6SetupWizardView.swift:27-36
- Row(
- identifier: "TandemKitSetupWizard.errorAlert",
- message: "Error",
- role: "alertTitle",
- taxonomy: "N12",
- expected: .other("Error")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:492-516
- Row(
- identifier: "TandemKitSettingsView.switchToCIQ",
- message: "Disable %@ Algorithm",
- role: "alertTitle",
- taxonomy: "N13",
- expected: .other("Disable %@ Algorithm")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:492-516
- Row(
- identifier: "TandemKitSettingsView.switchToCIQ",
- message: "This will DISABLE %@ in favor of the Control-IQ algorithm built-in to your pump.\n\n%@ will stop issuing temp basals and boluses. Are you sure?",
- role: "alertBody",
- taxonomy: "N13",
- expected: .other(
- "This will DISABLE %@ in favor of the Control-IQ algorithm built-in to your pump.\n\n%@ will stop issuing temp basals and boluses. Are you sure?"
- )
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:517-544
- Row(
- identifier: "TandemKitSettingsView.switchToDIY",
- message: "Enable %@ Algorithm",
- role: "alertTitle",
- taxonomy: "N13",
- expected: .other("Enable %@ Algorithm")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:517-544
- Row(
- identifier: "TandemKitSettingsView.switchToDIY",
- message: "This will disable Control-IQ on your pump and allow %@ to resume %@ closed-loop dosing.",
- role: "alertBody",
- taxonomy: "N13",
- expected: .other("This will disable Control-IQ on your pump and allow %@ to resume %@ closed-loop dosing.")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:567-584
- Row(
- identifier: "TandemKitSettingsView.stopTempRate",
- message: "Temp Basal Active",
- role: "alertTitle",
- taxonomy: "N13",
- expected: .other("Temp Basal Active")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:567-584
- Row(
- identifier: "TandemKitSettingsView.stopTempRate",
- message: "The current temp rate is <rate> for the next <duration>. Would you like to revert to your scheduled basal rate?",
- role: "alertBody",
- taxonomy: "N13",
- expected: .other(
- "The current temp rate is <rate> for the next <duration>. Would you like to revert to your scheduled basal rate?"
- )
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:585-597
- Row(
- identifier: "TandemKitSettingsView.stopTempRateClosedLoopWarning",
- message: "WARNING",
- role: "alertTitle",
- taxonomy: "N13",
- expected: .other("WARNING")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:585-597
- Row(
- identifier: "TandemKitSettingsView.stopTempRateClosedLoopWarning",
- message: "Closed loop is currently enabled. Cancelling the active temp rate on the pump will NOT place you in open-loop mode, and the next run of the oref algorithm will set a new temp rate.",
- role: "alertBody",
- taxonomy: "N13",
- expected: .other(
- "Closed loop is currently enabled. Cancelling the active temp rate on the pump will NOT place you in open-loop mode, and the next run of the oref algorithm will set a new temp rate."
- )
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:545-557
- Row(
- identifier: "TandemKitSettingsView.diyRestored",
- message: "%@ Mode Restored",
- role: "alertTitle",
- taxonomy: "N14",
- expected: .other("%@ Mode Restored")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:545-557
- Row(
- identifier: "TandemKitSettingsView.diyRestored",
- message: "Control-IQ has been disabled and you are currently in open-loop. To re-enable %@ closed-loop, go to your app's Algorithm settings.",
- role: "alertBody",
- taxonomy: "N14",
- expected: .other(
- "Control-IQ has been disabled and you are currently in open-loop. To re-enable %@ closed-loop, go to your app's Algorithm settings."
- )
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:558-566
- Row(
- identifier: "TandemKitSettingsView.noCGMConnected",
- message: "No CGM Connected",
- role: "alertTitle",
- taxonomy: "N14",
- expected: .other("No CGM Connected")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:558-566
- Row(
- identifier: "TandemKitSettingsView.noCGMConnected",
- message: "Control-IQ was enabled but no CGM is connected to the pump.\n\nUntil a CGM sensor is connected, your configured profile rates will be used. To connect a sensor, go to CGM Settings below.",
- role: "alertBody",
- taxonomy: "N14",
- expected: .other(
- "Control-IQ was enabled but no CGM is connected to the pump.\n\nUntil a CGM sensor is connected, your configured profile rates will be used. To connect a sensor, go to CGM Settings below."
- )
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:33-52
- Row(
- identifier: "TandemKitSettingsView.removePump",
- message: "Remove Pump",
- role: "alertTitle",
- taxonomy: "N14",
- expected: .other("Remove Pump")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:33-52
- Row(
- identifier: "TandemKitSettingsView.removePump",
- message: "Are you sure you want to remove the pump from %@? You will need to re-pair the device and have access to the charging pad.",
- role: "alertBody",
- taxonomy: "N14",
- expected: .other(
- "Are you sure you want to remove the pump from %@? You will need to re-pair the device and have access to the charging pad."
- )
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:33-52
- Row(
- identifier: "TandemKitSettingsView.removePump",
- message: "Delete Pump",
- role: "alertBody",
- taxonomy: "N14",
- expected: .other("Delete Pump")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:54-60
- Row(
- identifier: "TandemKitSettingsView.syncTime",
- message: "Time Change Detected",
- role: "alertTitle",
- taxonomy: "N14",
- expected: .other("Time Change Detected")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:54-60
- Row(
- identifier: "TandemKitSettingsView.syncTime",
- message: "The time on your pump is different from the current time. Do you want to update the time on your pump to the current time?",
- role: "alertBody",
- taxonomy: "N14",
- expected: .other(
- "The time on your pump is different from the current time. Do you want to update the time on your pump to the current time?"
- )
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:234-245
- Row(
- identifier: "TandemKitSettingsView.noCGMBanner",
- message: "No CGM connected to pump",
- role: "notificationTitle",
- taxonomy: "N14",
- expected: .other("No CGM connected to pump")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:234-245
- Row(
- identifier: "TandemKitSettingsView.noCGMBanner",
- message: "%@ closed-loop is disabled with your pump's Control-IQ algorithm on, but no CGM is connected, so you are in open-loop.",
- role: "notificationBody",
- taxonomy: "N14",
- expected: .other(
- "%@ closed-loop is disabled with your pump's Control-IQ algorithm on, but no CGM is connected, so you are in open-loop."
- )
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:246-254
- Row(
- identifier: "TandemKitSettingsView.controlIQBanner",
- message: "Control-IQ pump algorithm enabled",
- role: "notificationTitle",
- taxonomy: "N14",
- expected: .other("Control-IQ pump algorithm enabled")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:246-254
- Row(
- identifier: "TandemKitSettingsView.controlIQBanner",
- message: "%@ closed-loop is not in use.",
- role: "notificationBody",
- taxonomy: "N14",
- expected: .other("%@ closed-loop is not in use.")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:255-261
- Row(
- identifier: "TandemKitSettingsView.openLoopBanner",
- message: "Open loop mode",
- role: "notificationTitle",
- taxonomy: "N14",
- expected: .other("Open loop mode")
- ),
- // TandemKitUI/Views/Settings/TandemKitSettingsView.swift:263-273
- Row(
- identifier: "TandemKitSettingsView.timeSyncWarningBanner",
- message: "The time on your pump is different from the current time. Your pump's time controls your scheduled therapy settings. Scroll down to Pump Time row to review the time difference and configure your pump.",
- role: "notificationBody",
- taxonomy: "N14",
- expected: .other(
- "The time on your pump is different from the current time. Your pump's time controls your scheduled therapy settings. Scroll down to Pump Time row to review the time difference and configure your pump."
- )
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:23-27
- Row(
- identifier: "CGMSessionState.displayText",
- message: "Stopped",
- role: "validation",
- taxonomy: "N14",
- expected: .other("Stopped")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:23-27
- Row(
- identifier: "CGMSessionState.displayText",
- message: "Starting...",
- role: "validation",
- taxonomy: "N14",
- expected: .other("Starting...")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:23-27
- Row(
- identifier: "CGMSessionState.displayText",
- message: "Active",
- role: "validation",
- taxonomy: "N14",
- expected: .other("Active")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:23-27
- Row(
- identifier: "CGMSessionState.displayText",
- message: "Stopping...",
- role: "validation",
- taxonomy: "N14",
- expected: .other("Stopping...")
- ),
- // TandemKitUI/ViewModels/CGMs/TandemKitCGMSettingsViewModel.swift:23-27
- Row(
- identifier: "CGMSessionState.displayText",
- message: "Unknown",
- role: "validation",
- taxonomy: "N14",
- expected: .other("Unknown")
- ),
- // TandemKitUI/Views/CGMs/TandemKitCGMSettingsView.swift:151-157
- Row(
- identifier: "TandemKitCGMSettingsView.gracePeriodRow",
- message: "Grace Period",
- role: "validation",
- taxonomy: "N14",
- expected: .other("Grace Period")
- ),
- // TandemKitUI/Views/CGMs/TandemKitCGMSettingsView.swift:133-140
- Row(
- identifier: "TandemKitCGMSettingsView.timeRemainingRow",
- message: "Time Remaining",
- role: "validation",
- taxonomy: "N14",
- expected: .other("Time Remaining")
- )
- ]
- @Test(
- "each (identifier, message) classifies as pinned",
- arguments: rows
- ) func eachMessageClassifiesAsPinned(row: Row) {
- // Pins CURRENT behavior. Feeding the display string exercises the
- // substring matcher over real prose; this MUST stay green.
- #expect(TrioAlertClassifier.categorize(error: StubError(description: row.message)) == row.expected)
- }
- // MARK: - Coverage-gap ratchet
- /// "identifier — message" keys for emissions whose taxonomy implies a
- /// mappable bucket but the classifier returns `.other` because its
- /// space-free tokens miss the spaced prose. Each line below names the
- /// bucket the row SHOULD hit and why the tokens miss it. This set stays
- /// green today and FAILS when the classifier is improved (forcing a
- /// conscious update), ratcheting coverage upward.
- ///
- /// Per-gap rationale (bucket it SHOULD hit / why tokens miss):
- /// - N1 alarm titles & bodies (cartridge, cartridge-removed, pump-reset,
- /// temperature, altitude, atmospheric-pressure, stuck-button,
- /// invalid-date) and `HighestAamResponse.malfunction` -> SHOULD be
- /// `.hardwareFault`, but none contain the "fault" token; the prose says
- /// "Cartridge Alarm" / "Malfunction" / etc.
- /// (TandemCore/Messages/CurrentStatus/AlarmStatus.swift,
- /// HighestAam.swift:106). NB: "Occlusion Alarm" is NOT a gap — it hits
- /// `.occlusion` via the "occlusion" token (AlarmStatus.swift:157).
- /// - N4 reservoir-empty prose ("No Insulin", "Empty Cartridge Alarm",
- /// "Cartridge is out of insulin ...") -> SHOULD be `.reservoirEmpty`, but
- /// the token is the space-free "reservoirempty"/"emptyreservoir" and the
- /// prose never contains it (TandemKitUI/TandemPumpManager+UI.swift:123,
- /// AlarmStatus.swift:164).
- /// - N7 CGM alert titles render as space-separated formatted case names
- /// ("Sensor Failed Cgm Alert", "Sensor Expired Cgm Alert", ...) -> SHOULD
- /// be `.sensorFailure`, but "sensorfailed"/"sensorstopped" (and
- /// "sensorexpired") are space-free and never appear in the formatted text
- /// (TandemCore/Messages/CurrentStatus/CGMAlertStatus.swift:135).
- /// - N8 connectivity strings ("Pump not connected", "Make sure iPhone is
- /// nearby ...", "No response from pump", "Pump Error: ...",
- /// "Pump Disconnected") -> SHOULD be `.commsTransient`, but
- /// "notconnected"/"noresponse"/"comms"/"communication" are all space-free
- /// or absent, so the spaced prose misses
- /// (TandemKit/PumpManager/*.swift, TandemKitSettingsView.swift:230-233).
- static let classifierCoverageGaps: Set<String> = [
- "AlarmResponseType.cartridgeGroup — Cartridge Alarm",
- "AlarmResponseType.cartridgeGroup — There is an issue with the cartridge and it needs to be replaced.",
- "AlarmResponseType.CARTRIDGE_REMOVED_ALARM — Cartridge Removed Alarm",
- "AlarmResponseType.CARTRIDGE_REMOVED_ALARM — The cartridge was removed from the pump. Please fill a new cartridge.",
- "AlarmResponseType.PUMP_RESET_ALARM — Pump Reset Alarm",
- "AlarmResponseType.PUMP_RESET_ALARM — The pump was reset. IOB has been reset to 0 and CGM may need to be re-activated.",
- "AlarmResponseType.temperatureGroup — Temperature Alarm",
- "AlarmResponseType.temperatureGroup — Pump temperature is out of range and insulin cannot be safely delivered.",
- "AlarmResponseType.ALTITUDE_ALARM — Altitude Alarm",
- "AlarmResponseType.ATMOSPHERIC_PRESSURE_OUT_OF_RANGE_ALARM — Atmospheric Pressure Out Of Range Alarm",
- "AlarmResponseType.STUCK_BUTTON_ALARM — Stuck Button Alarm",
- "AlarmResponseType.STUCK_BUTTON_ALARM — The pump button may be stuck or has been pressed for too long a period of time.",
- "AlarmResponseType.INVALID_DATE_ALARM — Invalid Date Alarm",
- "AlarmResponseType.INVALID_DATE_ALARM — The pump's configured date is invalid.",
- "HighestAamResponse.malfunction — Malfunction",
- "PumpStatusHighlight.noInsulin — No Insulin",
- "AlarmResponseType.EMPTY_CARTRIDGE_ALARM — Empty Cartridge Alarm",
- "AlarmResponseType.EMPTY_CARTRIDGE_ALARM — Cartridge is out of insulin and insulin delivery cannot occur. Please fill a new cartridge.",
- "CGMAlert.SENSOR_FAILED — Sensor Failed Cgm Alert",
- "CGMAlert.LOW — Low Cgm Alert",
- "CGMAlert.HIGH — High Cgm Alert",
- "CGMAlert.FIXED_LOW — Fixed Low Cgm Alert",
- "CGMAlert.SENSOR_EXPIRING — Sensor Expiring Cgm Alert",
- "CGMAlert.SENSOR_EXPIRED — Sensor Expired Cgm Alert",
- "CGMAlert.OUT_OF_RANGE — Out Of Range Cgm Alert",
- "CGMAlert.TRANSMITTER — Transmitter Cgm Alert",
- "PumpCommError.pumpNotConnected — Pump not connected",
- "PumpCommError.pumpNotConnected — Make sure iPhone is nearby the active pump",
- "PumpCommError.noResponse — No response from pump",
- "PumpCommError.errorResponse — Pump Error: Failed to discoverServices: <error>",
- "PumpCommError.errorResponse — Pump Error: No service: <PUMP_SERVICE>",
- "PumpCommError.errorResponse — Pump Error: <message>",
- "PumpCommError.errorResponse — Pump Error: No service: <DIS_SERVICE>",
- "PumpCommError.errorResponse — Pump Error: Failed to connect: <error>",
- "TandemKitSettingsView.pumpDisconnectedBanner — Pump Disconnected"
- ]
- @Test("classifier coverage gaps are exactly as documented") func classifierCoverageGapsAreExactlyAsDocumented() {
- // Recompute the gap set from the rows: a row is a coverage gap when the
- // classifier returns `.other` for its message (so it is unbucketed)
- // even though the taxonomy implies a mappable bucket (taxonomyBucket
- // other than "other"). We detect "classifier returned .other" by
- // comparing against `.other(message)`. This stays equal to the pinned
- // set today and DIVERGES (failing this test) the moment the classifier
- // learns to bucket any of these strings — the ratchet.
- var computed = Set<String>()
- for row in Self.rows {
- let isOther: Bool
- if case .other = row.expected { isOther = true } else { isOther = false }
- if isOther {
- computed.insert("\(row.identifier) — \(row.message)")
- }
- }
- // `computed` is every `.other` row (mappable or not). Intersect with the
- // documented gap keys to assert the documented set is exactly the
- // taxonomy-mappable subset, and that none of them have started bucketing.
- #expect(Self.classifierCoverageGaps.isSubset(of: computed))
- #expect(Self.classifierCoverageGaps == Self.classifierCoverageGaps.intersection(computed))
- #expect(Self.classifierCoverageGaps.count == 35)
- }
- }
|