AlertCatalogRegistryEntriesTests.swift 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import Foundation
  2. import LoopKit
  3. import Testing
  4. @testable import Trio
  5. /// Pins `AlertCatalogRegistry.entries` and the static, exact-match
  6. /// `lookup(_:)` behavior. Pump plugins emit at LoopKit's default
  7. /// (`.timeSensitive`); this table is the sole escalation source. An unknown
  8. /// `(manager, alertId)` returns nil (pass-through). The Omni hex-fault parser
  9. /// is covered separately in `AlertCatalogRegistryOmniFaultTests`.
  10. @Suite("TrioAlert: CatalogRegistry — static exact-match entries") struct AlertCatalogRegistryEntriesTests {
  11. private func id(_ manager: String, _ alertID: String) -> Alert.Identifier {
  12. Alert.Identifier(managerIdentifier: manager, alertIdentifier: alertID)
  13. }
  14. // MARK: - A: critical entries
  15. @Test(
  16. "Critical entries escalate to .critical",
  17. arguments: [
  18. ("Omni", "unexpectedAlert"),
  19. ("Minimed", "PumpReservoirEmpty"),
  20. ("Dana", "batteryZeroPercent"),
  21. ("Dana", "pumpError"),
  22. ("Dana", "occlusion"),
  23. ("Dana", "shutdown"),
  24. ("Dana", "emptyReservoir"),
  25. ("Dana", "checkShaft"),
  26. // Both spellings are registered: MedtrumKit ships the misspelled
  27. // "patch-occlussion" (double-s) today; "patch-occlusion" is
  28. // forward-compat for when the upstream typo is fixed. Both escalate.
  29. ("Medtrum", "com.nightscout.medtrumkit.patch-occlussion"), // double-s (current MedtrumKit)
  30. ("Medtrum", "com.nightscout.medtrumkit.patch-occlusion"), // single-s (future MedtrumKit)
  31. ("Medtrum", "com.nightscout.medtrumkit.patch-fault"),
  32. ("Medtrum", "com.nightscout.medtrumkit.patch-empty")
  33. ]
  34. ) func criticalEntries(manager: String, alertID: String) {
  35. #expect(AlertCatalogRegistry.lookup(id(manager, alertID))?.interruptionLevel == .critical)
  36. }
  37. // MARK: - B: representative .timeSensitive entries
  38. @Test(
  39. "Representative entries land at .timeSensitive",
  40. arguments: [
  41. ("Omni", "lowReservoir"),
  42. ("Minimed", "PumpBatteryLow"),
  43. ("Dana", "lowBattery"),
  44. ("Dana", "unknown"),
  45. ("Medtrum", "com.nightscout.medtrumkit.reservoir-low"),
  46. ("Medtrum", "com.nightscout.medtrumkit.patch-daily-limit")
  47. ]
  48. ) func timeSensitiveEntries(manager: String, alertID: String) {
  49. #expect(AlertCatalogRegistry.lookup(id(manager, alertID))?.interruptionLevel == .timeSensitive)
  50. }
  51. // MARK: - C: representative .active entries
  52. @Test(
  53. "Representative entries stay at .active",
  54. arguments: [
  55. ("Omni", "userPodExpiration"),
  56. ("Dana", "basalCompare"),
  57. ("Medtrum", "com.nightscout.medtrumkit.patch-expired")
  58. ]
  59. ) func activeEntries(manager: String, alertID: String) {
  60. #expect(AlertCatalogRegistry.lookup(id(manager, alertID))?.interruptionLevel == .active)
  61. }
  62. // MARK: - D: unknown identifiers pass through (nil)
  63. @Test(
  64. "Unknown identifiers return nil (pass-through)",
  65. arguments: [
  66. ("Dana", "totallyMadeUp"),
  67. ("NoSuchManager", "lowReservoir"),
  68. ("", "")
  69. ]
  70. ) func unknownReturnsNil(manager: String, alertID: String) {
  71. #expect(AlertCatalogRegistry.lookup(id(manager, alertID)) == nil)
  72. }
  73. // MARK: - E: wrong manager, right alert id → nil
  74. @Test(
  75. "Right alert id under the wrong manager returns nil",
  76. arguments: [
  77. ("Minimed", "unexpectedAlert"),
  78. ("Dana", "PumpReservoirEmpty")
  79. ]
  80. ) func wrongManagerRightID(manager: String, alertID: String) {
  81. #expect(AlertCatalogRegistry.lookup(id(manager, alertID)) == nil)
  82. }
  83. // MARK: - F: right manager, wrong alert id → nil
  84. // Dana's occlusion entry is the single-s "occlusion"; the double-s
  85. // "occlussion" is not a Dana key and must return nil. (The double-s
  86. // spelling IS a valid key under the Medtrum manager — see test A — so this
  87. // also confirms entries are scoped per-manager.)
  88. @Test(
  89. "Wrong alert id under the right manager returns nil",
  90. arguments: [
  91. ("Dana", "occlussion"), // double-s is not a Dana key — must NOT match
  92. ("Medtrum", "patch-fault"), // missing com.nightscout.medtrumkit. prefix
  93. ("Omni", "lowReservoir ") // trailing space
  94. ]
  95. ) func rightManagerWrongID(manager: String, alertID: String) {
  96. #expect(AlertCatalogRegistry.lookup(id(manager, alertID)) == nil)
  97. }
  98. // MARK: - G: manager-scoped lowRLBattery
  99. @Test("lowRLBattery resolves per-manager for Omni and Minimed") func lowRLBatteryIsManagerScoped() {
  100. let omni = AlertCatalogRegistry.lookup(id("Omni", "lowRLBattery"))
  101. let minimed = AlertCatalogRegistry.lookup(id("Minimed", "lowRLBattery"))
  102. #expect(omni != nil)
  103. #expect(minimed != nil)
  104. #expect(omni?.identifier.managerIdentifier == "Omni")
  105. #expect(minimed?.identifier.managerIdentifier == "Minimed")
  106. }
  107. // MARK: - H: invariant — no duplicate manager+alert keys
  108. @Test("entries have unique manager+alert identifiers") func entriesHaveUniqueKeys() {
  109. let keys = Set(AlertCatalogRegistry.entries.map(\.identifier))
  110. #expect(keys.count == AlertCatalogRegistry.entries.count)
  111. }
  112. }