@oneuptime/common 14.0.0 → 14.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1270) hide show
  1. package/Models/DatabaseModels/AlertEpisodeMember.ts +8 -1
  2. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +63 -0
  3. package/Models/DatabaseModels/IncidentEpisodeMember.ts +8 -1
  4. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +9 -2
  5. package/Models/DatabaseModels/IncidentPublicNote.ts +9 -2
  6. package/Models/DatabaseModels/Index.ts +4 -0
  7. package/Models/DatabaseModels/KubernetesCluster.ts +429 -0
  8. package/Models/DatabaseModels/Monitor.ts +48 -0
  9. package/Models/DatabaseModels/Runner.ts +5 -4
  10. package/Models/DatabaseModels/RunnerJob.ts +66 -0
  11. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +10 -2
  12. package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
  13. package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
  14. package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
  15. package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
  16. package/Models/DatabaseModels/WorkflowVariable.ts +479 -2
  17. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +79 -25
  18. package/Server/API/AIInvestigationAPI.ts +341 -0
  19. package/Server/API/AutoRemediationAPI.ts +184 -2
  20. package/Server/API/CommonAPI.ts +70 -0
  21. package/Server/API/KubernetesClusterAiAccessAPI.ts +793 -0
  22. package/Server/API/MicrosoftTeamsAPI.ts +770 -315
  23. package/Server/API/MonitorAPI.ts +140 -0
  24. package/Server/API/OnCallScheduleTimelineAPI.ts +5 -21
  25. package/Server/API/SlackAPI.ts +447 -99
  26. package/Server/API/StatusPageAPI.ts +146 -26
  27. package/Server/API/TelemetryAPI.ts +216 -5
  28. package/Server/API/UserTwoFactorBackupCodeAPI.ts +1 -0
  29. package/Server/API/WorkflowVariableAPI.ts +156 -0
  30. package/Server/Infrastructure/GlobalCache.ts +35 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.ts +97 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.ts +129 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.ts +25 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.ts +20 -0
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.ts +38 -0
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
  39. package/Server/Middleware/MasterAdminAuthorization.ts +28 -0
  40. package/Server/Middleware/TelemetryIngest.ts +47 -2
  41. package/Server/Middleware/UserAuthorization.ts +56 -2
  42. package/Server/Services/AIAgentService.ts +1 -0
  43. package/Server/Services/AccessTokenService.ts +121 -48
  44. package/Server/Services/AlertEpisodeMemberService.ts +9 -0
  45. package/Server/Services/AlertEpisodeService.ts +14 -1
  46. package/Server/Services/AlertService.ts +134 -22
  47. package/Server/Services/AutoRemediationRuleEngineService.ts +968 -39
  48. package/Server/Services/AutoRemediationRuleService.ts +118 -0
  49. package/Server/Services/BillingService.ts +1 -0
  50. package/Server/Services/DatabaseService.ts +10 -0
  51. package/Server/Services/InboundEmail/Providers/SendGridInboundProvider.ts +16 -22
  52. package/Server/Services/IncidentEpisodeMemberService.ts +9 -0
  53. package/Server/Services/IncidentEpisodePublicNoteService.ts +66 -0
  54. package/Server/Services/IncidentEpisodeRoleMemberService.ts +27 -0
  55. package/Server/Services/IncidentEpisodeService.ts +14 -1
  56. package/Server/Services/IncidentGroupingEngineService.ts +11 -0
  57. package/Server/Services/IncidentMemberService.ts +21 -0
  58. package/Server/Services/IncidentPublicNoteService.ts +63 -0
  59. package/Server/Services/IncidentService.ts +120 -20
  60. package/Server/Services/IncidentStateTimelineService.ts +15 -0
  61. package/Server/Services/IncidentTemplateService.ts +83 -3
  62. package/Server/Services/Index.ts +4 -0
  63. package/Server/Services/KubernetesClusterAiAccessService.ts +1943 -0
  64. package/Server/Services/KubernetesClusterService.ts +1570 -1
  65. package/Server/Services/LogAggregationService.ts +14 -6
  66. package/Server/Services/MonitorService.ts +654 -54
  67. package/Server/Services/MonitorStatusTimelineService.ts +639 -2
  68. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -0
  69. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -0
  70. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -0
  71. package/Server/Services/OnCallDutyPolicyScheduleService.ts +3 -0
  72. package/Server/Services/OnCallNotificationAlertingService.ts +36 -12
  73. package/Server/Services/OnCallSetupReminderService.ts +1 -0
  74. package/Server/Services/ProbeService.ts +7 -1
  75. package/Server/Services/ProjectService.ts +11 -1
  76. package/Server/Services/RunbookCredentialService.ts +26 -1
  77. package/Server/Services/RunbookSecretService.ts +36 -0
  78. package/Server/Services/RunnerJobService.ts +941 -23
  79. package/Server/Services/RunnerService.ts +439 -6
  80. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +66 -0
  81. package/Server/Services/ScheduledMaintenanceService.ts +124 -19
  82. package/Server/Services/ScheduledMaintenanceTemplateService.ts +82 -3
  83. package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
  84. package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
  85. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
  86. package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
  87. package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
  88. package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
  89. package/Server/Services/ServiceLevelObjectiveService.ts +42 -1
  90. package/Server/Services/StatusPagePrivateUserService.ts +1 -0
  91. package/Server/Services/StatusPageService.ts +301 -74
  92. package/Server/Services/StatusPageSubscriberService.ts +4 -0
  93. package/Server/Services/TeamMemberService.ts +309 -7
  94. package/Server/Services/TraceAggregationService.ts +10 -6
  95. package/Server/Services/UserEmailService.ts +1 -0
  96. package/Server/Services/UserNotificationMethodAdminService.ts +1 -0
  97. package/Server/Services/UserNotificationRuleAdminService.ts +1 -0
  98. package/Server/Services/UserNotificationRuleService.ts +4 -0
  99. package/Server/Services/UserService.ts +122 -1
  100. package/Server/Services/WorkflowVariableService.ts +593 -8
  101. package/Server/Services/WorkspaceNotificationRuleService.ts +588 -8
  102. package/Server/Services/WorkspaceProjectAuthTokenService.ts +170 -1
  103. package/Server/Types/Database/QueryHelper.ts +22 -0
  104. package/Server/Types/Markdown.ts +502 -27
  105. package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
  106. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +182 -0
  107. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +525 -0
  108. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +854 -0
  109. package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +22 -2
  110. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1916 -307
  111. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1601 -74
  112. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +139 -7
  113. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +58 -1
  114. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +54 -1
  115. package/Server/Utils/AI/Toolbox/AIActionTools.ts +14 -6
  116. package/Server/Utils/AI/Toolbox/ResourceTools.ts +4 -2
  117. package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
  118. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +1341 -150
  119. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +375 -4
  120. package/Server/Utils/Database/AffectedResourceRelations.ts +127 -0
  121. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +240 -1
  122. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +1 -0
  123. package/Server/Utils/Monitor/AffectedResourceList.ts +31 -73
  124. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
  125. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
  126. package/Server/Utils/Monitor/MonitorAlert.ts +83 -9
  127. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +181 -136
  128. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +5 -1
  129. package/Server/Utils/Monitor/MonitorIncident.ts +104 -9
  130. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +124 -7
  131. package/Server/Utils/Monitor/ProbeMonitorsNotification.ts +407 -0
  132. package/Server/Utils/Monitor/RootCauseList.ts +139 -0
  133. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +1 -0
  134. package/Server/Utils/PushNotificationUtil.ts +45 -0
  135. package/Server/Utils/Rules/OwnerRuleAssignment.ts +40 -3
  136. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
  137. package/Server/Utils/Runbook/RunbookExecutePermission.ts +56 -6
  138. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +223 -33
  139. package/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.ts +559 -0
  140. package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
  141. package/Server/Utils/StartServer.ts +4 -3
  142. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +528 -0
  143. package/Server/Utils/TwoFactorBackupCodeNotification.ts +1 -0
  144. package/Server/Utils/UserPermission/UserPermission.ts +16 -0
  145. package/Server/Utils/Workflow/OAuth2TokenClient.ts +704 -0
  146. package/Server/Utils/Workflow/WorkflowVariableOAuthToken.ts +318 -0
  147. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +47 -7
  148. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +57 -16
  149. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +193 -214
  150. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +57 -13
  151. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +54 -17
  152. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +179 -161
  153. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +281 -167
  154. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.ts +114 -0
  155. package/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.ts +895 -0
  156. package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +7 -9
  157. package/Server/Utils/Workspace/Slack/Actions/Alert.ts +96 -227
  158. package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +93 -214
  159. package/Server/Utils/Workspace/Slack/Actions/Auth.ts +30 -3
  160. package/Server/Utils/Workspace/Slack/Actions/Authorization.ts +99 -0
  161. package/Server/Utils/Workspace/Slack/Actions/Incident.ts +117 -283
  162. package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +101 -196
  163. package/Server/Utils/Workspace/Slack/Actions/ReactionNote.ts +329 -0
  164. package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +111 -287
  165. package/Server/Utils/Workspace/Slack/Slack.ts +86 -17
  166. package/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  167. package/Server/Utils/Workspace/Workspace.ts +171 -54
  168. package/Server/Utils/Workspace/WorkspaceActionAuthorization.ts +252 -0
  169. package/Server/Utils/Workspace/WorkspaceBase.ts +39 -0
  170. package/Server/Utils/Workspace/WorkspaceOAuthState.ts +280 -0
  171. package/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.ts +96 -0
  172. package/Server/Utils/Workspace/WorkspaceReactionNote.ts +620 -0
  173. package/Tests/App/Dashboard/AttributesJSONDashboardSurfaces.test.tsx +496 -0
  174. package/Tests/App/Dashboard/AutoRemediationRulesTableCommandRunners.test.tsx +152 -0
  175. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +32 -0
  176. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +794 -0
  177. package/Tests/App/Dashboard/CreateOAuthWorkflowVariableModal.test.tsx +840 -0
  178. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +653 -0
  179. package/Tests/App/Dashboard/DatabaseMonitorStepForm.test.tsx +197 -0
  180. package/Tests/App/Dashboard/DatabaseMonitorView.test.tsx +376 -0
  181. package/Tests/App/Dashboard/DisabledWarningRobustness.test.tsx +262 -0
  182. package/Tests/App/Dashboard/EventNotes.test.tsx +2080 -0
  183. package/Tests/App/Dashboard/EventNotesPages.test.tsx +486 -0
  184. package/Tests/App/Dashboard/EventNotesUtil.test.ts +933 -0
  185. package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +218 -0
  186. package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
  187. package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
  188. package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
  189. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +665 -0
  190. package/Tests/App/Dashboard/IncidentEpisodeFeedPublicNoteNotifyDefault.test.tsx +494 -0
  191. package/Tests/App/Dashboard/IncidentEpisodeOverviewNotifyDefault.test.tsx +706 -0
  192. package/Tests/App/Dashboard/IncidentFeedPublicNoteNotifyDefault.test.tsx +472 -0
  193. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +858 -0
  194. package/Tests/App/Dashboard/IncomingEmailAddressSettings.test.tsx +567 -0
  195. package/Tests/App/Dashboard/InvestigationClusterToolFormat.test.ts +241 -0
  196. package/Tests/App/Dashboard/InvestigationEvidenceKubectl.test.tsx +322 -0
  197. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
  198. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +606 -3
  199. package/Tests/App/Dashboard/InvestigationPanelClusterAccess.test.tsx +476 -0
  200. package/Tests/App/Dashboard/InvestigationRunDetailsKubectl.test.tsx +383 -0
  201. package/Tests/App/Dashboard/KubectlActivitySummary.test.tsx +366 -0
  202. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +2886 -0
  203. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +2688 -0
  204. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +915 -0
  205. package/Tests/App/Dashboard/LayerDateTimeFieldTimezoneName.test.tsx +66 -0
  206. package/Tests/App/Dashboard/LogMonitorPreview.test.tsx +174 -0
  207. package/Tests/App/Dashboard/MonitorFeedRefresh.test.tsx +279 -0
  208. package/Tests/App/Dashboard/MonitorMetricQueryConfig.test.ts +244 -0
  209. package/Tests/App/Dashboard/MonitorOpenWorkCard.test.tsx +381 -0
  210. package/Tests/App/Dashboard/MonitorOpenWorkHook.test.tsx +784 -0
  211. package/Tests/App/Dashboard/MonitorOverviewData.test.tsx +2199 -0
  212. package/Tests/App/Dashboard/MonitorOverviewHero.test.tsx +1169 -0
  213. package/Tests/App/Dashboard/MonitorOverviewInput.test.ts +1127 -0
  214. package/Tests/App/Dashboard/MonitorOverviewPage.test.tsx +2136 -0
  215. package/Tests/App/Dashboard/MonitorOverviewSelectReadable.test.ts +368 -0
  216. package/Tests/App/Dashboard/MonitorOverviewStatBar.test.tsx +526 -0
  217. package/Tests/App/Dashboard/MonitorOverviewSupportingCards.test.tsx +542 -0
  218. package/Tests/App/Dashboard/MonitorOwnersCombine.test.ts +299 -0
  219. package/Tests/App/Dashboard/MonitorProbesCard.test.tsx +602 -0
  220. package/Tests/App/Dashboard/MonitorResponseTimeCard.test.tsx +386 -0
  221. package/Tests/App/Dashboard/MonitorSetupAndConnectionCards.test.tsx +612 -0
  222. package/Tests/App/Dashboard/MonitorStatusChangesCard.test.tsx +257 -0
  223. package/Tests/App/Dashboard/MonitorSummaryTestMonitorButton.test.tsx +360 -1
  224. package/Tests/App/Dashboard/MonitorUptimeHistoryCard.test.tsx +835 -0
  225. package/Tests/App/Dashboard/MonitorUptimeSummaryHook.test.tsx +971 -0
  226. package/Tests/App/Dashboard/MonitorViewLayout.test.tsx +580 -0
  227. package/Tests/App/Dashboard/OnCallLayerRestrictionTimesPreserved.test.tsx +441 -0
  228. package/Tests/App/Dashboard/OnCallNavbar.test.tsx +214 -0
  229. package/Tests/App/Dashboard/OnCallSchedulePreviewCalendarDisplay.test.tsx +1389 -0
  230. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +205 -1
  231. package/Tests/App/Dashboard/OnCallScheduleTimeInputsSettingsZone.test.tsx +198 -0
  232. package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
  233. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +524 -0
  234. package/Tests/App/Dashboard/RemediationSuggestionCardApproval.test.tsx +702 -0
  235. package/Tests/App/Dashboard/RemoveUserFromProject.test.tsx +427 -0
  236. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +4 -0
  237. package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
  238. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
  239. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
  240. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +3 -1
  241. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +166 -0
  242. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +622 -0
  243. package/Tests/App/Dashboard/ScheduledMaintenanceChangeStateNotifyDefault.test.tsx +1491 -0
  244. package/Tests/App/Dashboard/ScheduledMaintenanceFeedPublicNoteNotifyDefault.test.tsx +489 -0
  245. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
  246. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewNotifyDefault.test.tsx +909 -0
  247. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +14 -0
  248. package/Tests/App/Dashboard/ScheduledMaintenanceWindowStatsTranslation.test.tsx +307 -0
  249. package/Tests/App/Dashboard/SecurityEventAttributeColumnPicker.test.tsx +542 -0
  250. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +174 -0
  251. package/Tests/App/Dashboard/SecurityEventConnectionsEmptyState.test.tsx +973 -212
  252. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +491 -52
  253. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +301 -0
  254. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +158 -1
  255. package/Tests/App/Dashboard/SecurityEventProviderTile.test.tsx +869 -0
  256. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +959 -94
  257. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +2 -0
  258. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +274 -1
  259. package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
  260. package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
  261. package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
  262. package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
  263. package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
  264. package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
  265. package/Tests/App/Dashboard/TeamMembershipRemoval.test.ts +900 -0
  266. package/Tests/App/Dashboard/TeamMembershipRemovalPages.test.tsx +460 -0
  267. package/Tests/App/Dashboard/TimezoneElements.test.tsx +321 -0
  268. package/Tests/App/Dashboard/TimezoneSelectButton.test.tsx +634 -0
  269. package/Tests/App/Dashboard/TraceExplorer.test.tsx +260 -0
  270. package/Tests/App/Dashboard/TraceMonitorPreview.test.tsx +182 -0
  271. package/Tests/App/Dashboard/UserTimezoneInit.test.tsx +544 -0
  272. package/Tests/App/Dashboard/WorkflowVariableModals.test.tsx +1630 -0
  273. package/Tests/App/Dashboard/WorkflowVariableRoutes.test.tsx +603 -0
  274. package/Tests/App/Dashboard/WorkflowVariableTokenStatus.test.tsx +589 -0
  275. package/Tests/App/Dashboard/WorkflowVariableUtil.test.ts +1543 -0
  276. package/Tests/App/Dashboard/WorkflowVariableView.test.tsx +3227 -0
  277. package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +1277 -395
  278. package/Tests/App/StatusPage/StatusPageOverviewMonitorGroupUptime.test.tsx +714 -0
  279. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.NewYork.test.tsx +1009 -0
  280. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.test.tsx +1092 -0
  281. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -0
  282. package/Tests/Models/DatabaseModels/KubernetesClusterAiAccessColumns.test.ts +520 -0
  283. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +66 -2
  284. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
  285. package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
  286. package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
  287. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  288. package/Tests/Models/DatabaseModels/WorkspaceProjectAuthTokenAccessControl.test.ts +236 -0
  289. package/Tests/Models/IncidentEpisodePublicNoteNotifyColumnContract.test.ts +103 -0
  290. package/Tests/Models/IncidentPublicNoteNotifyColumnContract.test.ts +96 -0
  291. package/Tests/Models/ScheduledMaintenancePublicNoteNotifyColumnContract.test.ts +111 -0
  292. package/Tests/Server/API/AIInvestigationAPI.test.ts +976 -0
  293. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +836 -0
  294. package/Tests/Server/API/CommonAPIAssertCanCreateTable.test.ts +565 -0
  295. package/Tests/Server/API/CommonAPIAssertCanReadTable.test.ts +282 -0
  296. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +1744 -0
  297. package/Tests/Server/API/LogHistogramAPI.test.ts +371 -0
  298. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +125 -30
  299. package/Tests/Server/API/MicrosoftTeamsOAuthState.test.ts +1121 -0
  300. package/Tests/Server/API/MonitorUptimeSummaryAPI.test.ts +685 -0
  301. package/Tests/Server/API/SessionReplayAPI.test.ts +602 -1
  302. package/Tests/Server/API/SlackChannelCacheAPI.test.ts +358 -0
  303. package/Tests/Server/API/SlackOAuthState.test.ts +688 -0
  304. package/Tests/Server/API/StatusPageOverviewMonitorGroupUptime.test.ts +527 -0
  305. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +2 -0
  306. package/Tests/Server/API/StatusPageUptimeAPI.test.ts +427 -0
  307. package/Tests/Server/API/UserProjectsAPI.test.ts +4 -0
  308. package/Tests/Server/API/WorkflowVariableAPI.test.ts +404 -0
  309. package/Tests/Server/API/WorkspaceOAuthTestHelpers.ts +289 -0
  310. package/Tests/Server/API/WorkspaceSendTestNotificationRoutes.test.ts +2064 -0
  311. package/Tests/Server/Infrastructure/GlobalCache.test.ts +86 -0
  312. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  313. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +589 -0
  314. package/Tests/Server/Infrastructure/Postgres/AddOAuth2WorkflowVariablesMigration.test.ts +254 -0
  315. package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
  316. package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
  317. package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
  318. package/Tests/Server/Middleware/BlockedUserMiddleware.test.ts +403 -0
  319. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +13 -0
  320. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +2 -0
  321. package/Tests/Server/Middleware/RemovedProjectMemberAuthorization.test.ts +469 -0
  322. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +77 -1
  323. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +211 -0
  324. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +358 -0
  325. package/Tests/Server/Middleware/UserAuthorization.test.ts +97 -0
  326. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +2 -0
  327. package/Tests/Server/Services/AccessTokenProjectUserGrant.test.ts +14 -2
  328. package/Tests/Server/Services/AccessTokenRemovedMemberAccess.test.ts +1044 -0
  329. package/Tests/Server/Services/AddOwnersIdempotency.test.ts +5 -0
  330. package/Tests/Server/Services/AlertEpisodeMemberCreatePermission.test.ts +139 -0
  331. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +1361 -0
  332. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +375 -0
  333. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +2384 -0
  334. package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
  335. package/Tests/Server/Services/FindOwnersProjectMembership.test.ts +267 -0
  336. package/Tests/Server/Services/InboundEmail/SendGridInboundProviderAddress.test.ts +90 -0
  337. package/Tests/Server/Services/IncidentEpisodeMemberCreatePermission.test.ts +143 -0
  338. package/Tests/Server/Services/IncidentEpisodePublicNoteNotifyDefault.test.ts +805 -0
  339. package/Tests/Server/Services/IncidentPublicNoteNotifyDefault.test.ts +724 -0
  340. package/Tests/Server/Services/IncidentRoleProjectMembership.test.ts +233 -0
  341. package/Tests/Server/Services/KubernetesClusterAiAccessAgentRunnerAssignments.test.ts +372 -0
  342. package/Tests/Server/Services/KubernetesClusterAiAccessBindingGuard.test.ts +1876 -0
  343. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +620 -0
  344. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +421 -0
  345. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +3662 -0
  346. package/Tests/Server/Services/KubernetesClusterAiAccessSubjectClusters.test.ts +405 -0
  347. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +975 -0
  348. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +497 -0
  349. package/Tests/Server/Services/KubernetesClusterServiceDeleteSettlesRemediation.test.ts +328 -0
  350. package/Tests/Server/Services/LogAggregationService.test.ts +99 -0
  351. package/Tests/Server/Services/MonitorServiceIncomingEmailCustomAddress.test.ts +447 -0
  352. package/Tests/Server/Services/MonitorServiceProbeFlagSync.test.ts +1334 -0
  353. package/Tests/Server/Services/MonitorServiceProbeStatusGrouping.test.ts +1640 -0
  354. package/Tests/Server/Services/MonitorServiceRefreshCurrentStatus.test.ts +225 -0
  355. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +8 -8
  356. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +264 -1
  357. package/Tests/Server/Services/MonitorStatusTimelineMergedDowntime.test.ts +284 -0
  358. package/Tests/Server/Services/MonitorUptimeSummaryService.test.ts +1043 -0
  359. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +5 -0
  360. package/Tests/Server/Services/OnCallUndeliverablePageOwnerMembership.test.ts +359 -0
  361. package/Tests/Server/Services/ProbeServiceDashboardLink.test.ts +191 -0
  362. package/Tests/Server/Services/ProjectOwnersProjectMembership.test.ts +221 -0
  363. package/Tests/Server/Services/RuleRun/IncidentAlertMaintenanceRuleEnginesRun.test.ts +5 -0
  364. package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
  365. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +28 -0
  366. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +39 -1
  367. package/Tests/Server/Services/RunnerJobEnqueueAiCommandAgentRunner.test.ts +230 -0
  368. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +978 -0
  369. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +866 -0
  370. package/Tests/Server/Services/RunnerJobSubmitResultRedaction.test.ts +322 -0
  371. package/Tests/Server/Services/RunnerJobSubmitResultUnstorableText.test.ts +357 -0
  372. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +227 -0
  373. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +581 -0
  374. package/Tests/Server/Services/ScheduledMaintenancePublicNoteNotifyDefault.test.ts +802 -0
  375. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +53 -0
  376. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1175 -0
  377. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
  378. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
  379. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
  380. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
  381. package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
  382. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +174 -0
  383. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
  384. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +1176 -1
  385. package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +9 -0
  386. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +18 -0
  387. package/Tests/Server/Services/TeamMemberProjectMembership.test.ts +255 -0
  388. package/Tests/Server/Services/TeamMemberRemovalRevokesProjectAccess.test.ts +600 -0
  389. package/Tests/Server/Services/TeamMemberResourceCleanup.test.ts +731 -0
  390. package/Tests/Server/Services/TeamMemberWorkspaceAccountCleanup.test.ts +164 -0
  391. package/Tests/Server/Services/TraceAggregationService.test.ts +64 -0
  392. package/Tests/Server/Services/UserServiceBlockedUser.test.ts +293 -0
  393. package/Tests/Server/Services/WorkflowVariableOAuthHooks.test.ts +1016 -0
  394. package/Tests/Server/Services/WorkflowVariableOAuthPostgres.test.ts +543 -0
  395. package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +16 -5
  396. package/Tests/Server/Services/WorkspaceNotificationRuleNoteDelivery.test.ts +473 -0
  397. package/Tests/Server/Services/WorkspaceNotificationRuleSendTestNotification.test.ts +3690 -0
  398. package/Tests/Server/Services/WorkspaceProjectAuthTokenMiscDataSecrets.test.ts +374 -0
  399. package/Tests/Server/Services/WorkspaceProjectAuthTokenSlackChannelCache.test.ts +143 -0
  400. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
  401. package/Tests/Server/Types/Database/Permissions/WorkflowVariableOAuthColumnPermission.test.ts +250 -0
  402. package/Tests/Server/Types/Database/QueryHelperJsonContains.test.ts +96 -0
  403. package/Tests/Server/Types/Markdown.test.ts +1035 -1
  404. package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
  405. package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
  406. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +285 -0
  407. package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +159 -1
  408. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +752 -0
  409. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +1840 -0
  410. package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +1067 -0
  411. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +355 -0
  412. package/Tests/Server/Utils/AI/PostIncidentStatusUpdateTool.test.ts +496 -0
  413. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +305 -0
  414. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +1365 -0
  415. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +639 -0
  416. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +1585 -0
  417. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +1065 -0
  418. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +2138 -1
  419. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +1035 -0
  420. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +517 -0
  421. package/Tests/Server/Utils/AI/ResourceTools.test.ts +27 -1
  422. package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
  423. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1371 -24
  424. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorRollbackRace.test.ts +962 -0
  425. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +518 -0
  426. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +939 -2
  427. package/Tests/Server/Utils/Database/ProjectScopedRelationReferences.test.ts +371 -0
  428. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +64 -0
  429. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
  430. package/Tests/Server/Utils/Monitor/DatabaseMetricObservationRemediation.test.ts +102 -0
  431. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +362 -0
  432. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +405 -0
  433. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1156 -0
  434. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
  435. package/Tests/Server/Utils/Monitor/MonitorEmailAddressRedaction.test.ts +255 -0
  436. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +347 -0
  437. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +203 -0
  438. package/Tests/Server/Utils/Monitor/ProbeMonitorsNotification.test.ts +1147 -0
  439. package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
  440. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +79 -0
  441. package/Tests/Server/Utils/ResourceFeed/ResourceFeedUtil.test.ts +21 -1
  442. package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +155 -1
  443. package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
  444. package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
  445. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +288 -0
  446. package/Tests/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.test.ts +304 -0
  447. package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
  448. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +40 -3
  449. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
  450. package/Tests/Server/Utils/Workflow/OAuth2TokenClient.test.ts +702 -0
  451. package/Tests/Server/Utils/Workflow/OAuth2TokenClientHttpIntegration.test.ts +507 -0
  452. package/Tests/Server/Utils/Workflow/WorkflowVariableOAuthToken.test.ts +831 -0
  453. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +63 -1
  454. package/Tests/Server/Utils/Workspace/MicrosoftTeamsAppAccessToken.test.ts +256 -0
  455. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +60 -0
  456. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChatActionAuthorization.test.ts +442 -0
  457. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +44 -2
  458. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +55 -0
  459. package/Tests/Server/Utils/Workspace/MicrosoftTeamsReactionNoteSync.test.ts +1522 -0
  460. package/Tests/Server/Utils/Workspace/MicrosoftTeamsServiceUrl.test.ts +130 -0
  461. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +4 -4
  462. package/Tests/Server/Utils/Workspace/Slack/SlackActionAuthorization.test.ts +691 -0
  463. package/Tests/Server/Utils/Workspace/Slack/SlackMessageByTimestamp.test.ts +253 -0
  464. package/Tests/Server/Utils/Workspace/Slack/SlackReactionNote.test.ts +574 -0
  465. package/Tests/Server/Utils/Workspace/WorkspaceActionAuthorization.test.ts +488 -0
  466. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +861 -0
  467. package/Tests/Server/Utils/Workspace/WorkspaceMessageAuthor.test.ts +635 -0
  468. package/Tests/Server/Utils/Workspace/WorkspaceOAuthState.test.ts +548 -0
  469. package/Tests/Server/Utils/Workspace/WorkspaceReactionNote.test.ts +839 -0
  470. package/Tests/Server/Utils/Workspace/WorkspaceSendErrorMessage.test.ts +725 -0
  471. package/Tests/Types/API/Route.test.ts +56 -0
  472. package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
  473. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +283 -9
  474. package/Tests/Types/DateBrowserLocalWallClock.test.ts +280 -0
  475. package/Tests/Types/DateLocalShortMonthNameFallback.test.ts +389 -0
  476. package/Tests/Types/DateUserTimezone.test.ts +177 -4
  477. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +306 -0
  478. package/Tests/Types/Kubernetes/KubernetesClusterAiAccess.test.ts +283 -0
  479. package/Tests/Types/Kubernetes/KubernetesRunnerPostureWriteScope.test.ts +68 -0
  480. package/Tests/Types/Monitor/DatabaseMonitor/SqlServerPlatform.test.ts +144 -0
  481. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +160 -0
  482. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +53 -0
  483. package/Tests/Types/Runbook/RunbookStepType.test.ts +91 -16
  484. package/Tests/Types/Runner/RunnerLiveStatus.test.ts +14 -3
  485. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +15 -0
  486. package/Tests/Types/StatusPage/PublicNoteSubscriberNotificationDefault.test.ts +1868 -0
  487. package/Tests/Types/Timezone.test.ts +5 -0
  488. package/Tests/Types/TimezoneAlias.test.ts +1034 -0
  489. package/Tests/Types/Workflow/JiraTemplateFixtures.ts +833 -0
  490. package/Tests/Types/Workflow/JiraTemplateScripts.test.ts +5414 -0
  491. package/Tests/Types/Workflow/JiraTemplateSimulation.test.ts +4803 -0
  492. package/Tests/Types/Workflow/JiraTemplates.test.ts +5429 -0
  493. package/Tests/Types/Workflow/Templates.test.ts +79 -4
  494. package/Tests/Types/Workflow/WorkflowVariableOAuth.test.ts +520 -0
  495. package/Tests/Types/Workspace/WorkspaceNoteReaction.test.ts +177 -0
  496. package/Tests/UI/Components/AttributesJSON/AttributesJSONView.test.tsx +390 -0
  497. package/Tests/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.test.tsx +566 -0
  498. package/Tests/UI/Components/AttributesJSON/JSONHighlight.test.ts +131 -0
  499. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +276 -1
  500. package/Tests/UI/Components/Calendar/Calendar.test.tsx +1611 -0
  501. package/Tests/UI/Components/Calendar/CalendarDayLayout.test.ts +1486 -0
  502. package/Tests/UI/Components/Calendar/CalendarDayLayoutDst.test.ts +632 -0
  503. package/Tests/UI/Components/Calendar/CalendarDst.test.tsx +388 -0
  504. package/Tests/UI/Components/Charts/ChartGroupPlotFloor.test.tsx +170 -0
  505. package/Tests/UI/Components/Charts/HistogramSelection.test.ts +282 -0
  506. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +6 -0
  507. package/Tests/UI/Components/CustomTimeRangeModalTimezoneTranslation.test.tsx +285 -0
  508. package/Tests/UI/Components/Dropdown.test.tsx +999 -2
  509. package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
  510. package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
  511. package/Tests/UI/Components/Forms/FormFieldCheckboxTranslation.test.tsx +216 -0
  512. package/Tests/UI/Components/Forms/FormFieldTimezoneDescriptionTranslation.test.tsx +461 -0
  513. package/Tests/UI/Components/Forms/IncidentEpisodePublicNoteNotifyPayload.test.tsx +641 -0
  514. package/Tests/UI/Components/Forms/IncidentPublicNoteNotifyPayload.test.tsx +346 -0
  515. package/Tests/UI/Components/Forms/ScheduledMaintenancePublicNoteNotifyPayload.test.tsx +493 -0
  516. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +911 -2
  517. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.NewYork.test.tsx +730 -0
  518. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.Tokyo.test.tsx +506 -0
  519. package/Tests/UI/Components/HistogramClickToZoom.test.tsx +619 -0
  520. package/Tests/UI/Components/Icon.test.tsx +26 -0
  521. package/Tests/UI/Components/LogDetailsPanelAttributesJSON.test.tsx +248 -0
  522. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +47 -1
  523. package/Tests/UI/Components/LogsViewerSmallScreens.test.tsx +620 -0
  524. package/Tests/UI/Components/ModelPageModelChange.test.tsx +242 -3
  525. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteConfirmation.test.tsx +521 -0
  526. package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +406 -2
  527. package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +169 -2
  528. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  529. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +266 -0
  530. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerSmallScreens.test.tsx +439 -0
  531. package/Tests/UI/Components/TimezoneDropdownLegacyValues.test.tsx +704 -0
  532. package/Tests/UI/Components/UseHistogramZoom.test.tsx +89 -1
  533. package/Tests/UI/Components/UseLogsHistogram.test.tsx +175 -0
  534. package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
  535. package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
  536. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +5 -5
  537. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +28 -0
  538. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +51 -0
  539. package/Tests/UI/Utils/Timezone.test.ts +747 -33
  540. package/Tests/UI/Workspace/SendTestNotificationButton.test.tsx +1854 -0
  541. package/Tests/UI/Workspace/WorkspaceCardsSendTestNotification.test.tsx +1864 -0
  542. package/Tests/Utils/AI/InvestigationReport.test.ts +435 -7
  543. package/Tests/Utils/AiRemediation/KubectlAllowlistValidatorParity.test.ts +1084 -0
  544. package/Tests/Utils/AiRemediation/KubectlOutputRedactor.test.ts +2531 -0
  545. package/Tests/Utils/AiRemediation/KubectlPolicy.test.ts +1895 -0
  546. package/Tests/Utils/AiRemediation/KubectlPolicyAllowlist.test.ts +428 -0
  547. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +828 -0
  548. package/Tests/Utils/AiRemediation/KubectlPolicyCustomResourceScope.test.ts +573 -0
  549. package/Tests/Utils/AiRemediation/KubectlPolicyDenials.test.ts +1525 -0
  550. package/Tests/Utils/AiRemediation/KubectlPolicyExposeOverrides.test.ts +275 -0
  551. package/Tests/Utils/AiRemediation/KubectlPolicyNodePatch.test.ts +428 -0
  552. package/Tests/Utils/AiRemediation/KubectlPolicyObjectWords.test.ts +195 -0
  553. package/Tests/Utils/AiRemediation/KubectlPolicyPatchParents.test.ts +637 -0
  554. package/Tests/Utils/AiRemediation/KubectlPolicyPodSecurityBaseline.test.ts +322 -0
  555. package/Tests/Utils/AiRemediation/KubectlPolicySafeWriteScope.test.ts +1026 -0
  556. package/Tests/Utils/AiRemediation/KubectlWaitBudget.test.ts +267 -0
  557. package/Tests/Utils/AiRemediation/KubectlWriteScope.test.ts +390 -0
  558. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +777 -0
  559. package/Tests/Utils/AiRemediation/KubectlWriteScopeClusterScoped.test.ts +613 -0
  560. package/Tests/Utils/AiRemediation/KubectlWriteScopeEntryPoint.test.ts +576 -0
  561. package/Tests/Utils/AiRemediation/KubectlWriteScopeKubectlSpellings.test.ts +752 -0
  562. package/Tests/Utils/AiRemediation/KubectlWriteScopeObjectWords.test.ts +358 -0
  563. package/Tests/Utils/AiRemediation/KubectlWriteScopeOverrides.test.ts +293 -0
  564. package/Tests/Utils/AiRemediation/KubectlWriteScopeParityCases.ts +598 -0
  565. package/Tests/Utils/AiRemediation/KubectlWriteScopePolicyParity.test.ts +403 -0
  566. package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +154 -5
  567. package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
  568. package/Tests/Utils/Monitor/IncomingEmailMonitorAddress.test.ts +478 -0
  569. package/Tests/Utils/Monitor/MonitorCheckScheduleUtil.test.ts +646 -0
  570. package/Tests/Utils/Monitor/MonitorOverviewCriteriaUtil.test.ts +292 -0
  571. package/Tests/Utils/Monitor/MonitorOverviewFamily.test.ts +274 -0
  572. package/Tests/Utils/Monitor/MonitorOverviewPresentationUtil.test.ts +3100 -0
  573. package/Tests/Utils/Monitor/MonitorOverviewProbeUtil.test.ts +1412 -0
  574. package/Tests/Utils/Monitor/MonitorOverviewTargetUtil.test.ts +374 -0
  575. package/Tests/Utils/Monitor/MonitorStatusHistoryUtil.test.ts +332 -0
  576. package/Tests/Utils/Monitor/MonitorUptimeSummaryUtil.test.ts +946 -0
  577. package/Tests/Utils/Rum/SessionIdentity.test.ts +94 -0
  578. package/Tests/Utils/Rum/SessionReplayWafCompatibility.test.ts +840 -0
  579. package/Tests/Utils/Rum/SessionReplayWireEncoding.test.ts +208 -0
  580. package/Tests/Utils/Rum/UserFlow.test.ts +1010 -0
  581. package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
  582. package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
  583. package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
  584. package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
  585. package/Tests/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.test.ts +217 -0
  586. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +1272 -1
  587. package/Tests/Utils/StatusPage/UptimeDailyAggregateTotals.test.ts +298 -0
  588. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +649 -1
  589. package/Tests/Utils/StatusPage/UptimeDailyAggregateWindow.test.ts +314 -0
  590. package/Tests/Utils/Telemetry/AttributesJSON.test.ts +454 -0
  591. package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +418 -2
  592. package/Tests/Utils/Uptime/DayUptimeGraphUtilMidnightDst.test.ts +160 -0
  593. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Auckland.test.ts +45 -0
  594. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Kolkata.test.ts +40 -0
  595. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.London.test.ts +147 -0
  596. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.NewYork.test.ts +48 -0
  597. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Tokyo.test.ts +39 -0
  598. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezoneCases.ts +1015 -0
  599. package/Tests/Utils/Uptime/UptimeUtil.test.ts +75 -0
  600. package/Tests/Utils/Uptime/UptimeUtilChronologicalOrder.test.ts +1279 -0
  601. package/Types/API/Route.ts +19 -2
  602. package/Types/API/URL.ts +26 -2
  603. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +203 -5
  604. package/Types/Date.ts +121 -7
  605. package/Types/Email/EmailMessage.ts +8 -0
  606. package/Types/Email/EmailTemplateType.ts +1 -0
  607. package/Types/Exception/ExceptionMessages.ts +1 -0
  608. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +488 -0
  609. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +54 -0
  610. package/Types/Kubernetes/KubernetesClusterAiAccessToolNames.ts +17 -0
  611. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +9 -0
  612. package/Types/Monitor/DatabaseMonitor/SqlServerPlatform.ts +117 -0
  613. package/Types/Monitor/KubernetesAlertTemplates.ts +6 -5
  614. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
  615. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
  616. package/Types/Monitor/MonitorStepExceptionMonitor.ts +56 -0
  617. package/Types/Monitor/MonitorUptimeSummary.ts +113 -0
  618. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +11 -0
  619. package/Types/Permission.ts +90 -0
  620. package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
  621. package/Types/Rules/RuleRun.ts +4 -0
  622. package/Types/Rum/SessionReplay.ts +55 -2
  623. package/Types/Rum/SessionReplayApi.ts +44 -1
  624. package/Types/Rum/UserFlow.ts +187 -0
  625. package/Types/Runbook/RunbookStepType.ts +10 -0
  626. package/Types/Runbook/RunnerJobOrigin.ts +15 -0
  627. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +6 -2
  628. package/Types/StatusPage/MergedDowntimeTotals.ts +17 -0
  629. package/Types/StatusPage/PublicNoteSubscriberNotificationDefault.ts +148 -0
  630. package/Types/StatusPage/UptimeDailyAggregate.ts +15 -0
  631. package/Types/Telemetry/TelemetryIngestSurface.ts +9 -0
  632. package/Types/Timezone.ts +5 -0
  633. package/Types/TimezoneAlias.ts +385 -0
  634. package/Types/Workflow/Templates.ts +3041 -2
  635. package/Types/Workflow/WorkflowVariableOAuth.ts +476 -0
  636. package/Types/Workspace/WorkspaceNoteReaction.ts +132 -0
  637. package/Typings/Index.d.ts +24 -0
  638. package/UI/Components/AttributesJSON/AttributesJSONPreferences.ts +175 -0
  639. package/UI/Components/AttributesJSON/AttributesJSONView.tsx +138 -0
  640. package/UI/Components/AttributesJSON/AttributesViewToggle.tsx +79 -0
  641. package/UI/Components/AttributesJSON/BracesGlyph.tsx +30 -0
  642. package/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.tsx +491 -0
  643. package/UI/Components/AttributesJSON/JSONHighlight.ts +77 -0
  644. package/UI/Components/Calendar/Calendar.css +83 -0
  645. package/UI/Components/Calendar/Calendar.tsx +136 -7
  646. package/UI/Components/Calendar/CalendarDayLayout.ts +236 -0
  647. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -1
  648. package/UI/Components/Charts/Utils/HistogramSelection.ts +117 -0
  649. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +269 -0
  650. package/UI/Components/Charts/Utils/useHistogramZoom.ts +26 -2
  651. package/UI/Components/Date/CustomTimeRangeModal.tsx +26 -3
  652. package/UI/Components/Dropdown/Dropdown.tsx +58 -9
  653. package/UI/Components/Feed/FeedOptions.ts +427 -0
  654. package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
  655. package/UI/Components/Feed/useFeedItems.ts +28 -9
  656. package/UI/Components/Feed/useFeedOptions.ts +225 -0
  657. package/UI/Components/Forms/Fields/FormField.tsx +49 -14
  658. package/UI/Components/Graphs/DayUptimeGraph.tsx +166 -25
  659. package/UI/Components/Graphs/UptimeBarTooltip.tsx +10 -2
  660. package/UI/Components/Graphs/UptimeDaySummary.tsx +8 -0
  661. package/UI/Components/Icon/Icon.tsx +1 -1
  662. package/UI/Components/LogsViewer/LogsViewer.tsx +52 -5
  663. package/UI/Components/LogsViewer/components/ColumnSelector.tsx +13 -2
  664. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +13 -1
  665. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +91 -76
  666. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +15 -1
  667. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +28 -106
  668. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +54 -6
  669. package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +8 -2
  670. package/UI/Components/LogsViewer/useLogsHistogram.ts +39 -6
  671. package/UI/Components/ModelTable/BaseModelTable.tsx +66 -14
  672. package/UI/Components/MonitorGraphs/Uptime.tsx +12 -0
  673. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +10 -2
  674. package/UI/Components/Page/ModelPage.tsx +59 -3
  675. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +69 -5
  676. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +12 -1
  677. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +15 -1
  678. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +36 -104
  679. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +9 -1
  680. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +19 -1
  681. package/UI/Utils/Timezone.ts +69 -28
  682. package/Utils/AI/InvestigationReport.ts +195 -25
  683. package/Utils/AiRemediation/KubectlOutputRedactor.ts +1724 -0
  684. package/Utils/AiRemediation/KubectlPolicy.ts +4136 -0
  685. package/Utils/AiRemediation/KubectlWaitBudget.ts +156 -0
  686. package/Utils/AiRemediation/KubectlWriteScope.ts +1964 -0
  687. package/Utils/Dashboard/Components/DashboardClockComponent.ts +7 -6
  688. package/Utils/LabelRuleImportExport.ts +27 -9
  689. package/Utils/Monitor/IncomingEmailMonitorAddress.ts +292 -0
  690. package/Utils/Monitor/MetricValueFormatter.ts +2 -2
  691. package/Utils/Monitor/MonitorCheckScheduleUtil.ts +654 -0
  692. package/Utils/Monitor/MonitorOverviewCriteriaUtil.ts +188 -0
  693. package/Utils/Monitor/MonitorOverviewFamily.ts +263 -0
  694. package/Utils/Monitor/MonitorOverviewPresentationUtil.ts +1958 -0
  695. package/Utils/Monitor/MonitorOverviewProbeUtil.ts +979 -0
  696. package/Utils/Monitor/MonitorOverviewTargetUtil.ts +299 -0
  697. package/Utils/Monitor/MonitorStatusHistoryUtil.ts +201 -0
  698. package/Utils/Monitor/MonitorUptimeSummaryUtil.ts +638 -0
  699. package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
  700. package/Utils/Rum/SessionIdentity.ts +44 -0
  701. package/Utils/Rum/SessionReplayWireEncoding.ts +137 -0
  702. package/Utils/Rum/UserFlow.ts +1527 -0
  703. package/Utils/Slo/SloDuration.ts +22 -6
  704. package/Utils/Slo/SloHealth.ts +8 -7
  705. package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
  706. package/Utils/Slo/SloOverviewText.ts +33 -0
  707. package/Utils/Slo/SloProjection.ts +4 -3
  708. package/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.ts +107 -0
  709. package/Utils/StatusPage/ResourceUptime.ts +99 -0
  710. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +197 -0
  711. package/Utils/Telemetry/AttributesJSON.ts +367 -0
  712. package/Utils/Uptime/DayUptimeGraphUtil.ts +165 -0
  713. package/Utils/Uptime/UptimeUtil.ts +122 -38
  714. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +8 -1
  715. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  716. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  717. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  718. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +8 -1
  719. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  720. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +10 -3
  721. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  722. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +10 -3
  723. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  724. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  725. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  726. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +434 -0
  727. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  728. package/build/dist/Models/DatabaseModels/Monitor.js +49 -0
  729. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  730. package/build/dist/Models/DatabaseModels/Runner.js +4 -4
  731. package/build/dist/Models/DatabaseModels/Runner.js.map +1 -1
  732. package/build/dist/Models/DatabaseModels/RunnerJob.js +66 -0
  733. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  734. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +11 -3
  735. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  736. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
  737. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
  738. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
  739. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
  740. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
  741. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
  742. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
  743. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
  744. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +487 -3
  745. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  746. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js +64 -16
  747. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  748. package/build/dist/Server/API/AIInvestigationAPI.js +205 -0
  749. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  750. package/build/dist/Server/API/AutoRemediationAPI.js +115 -2
  751. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  752. package/build/dist/Server/API/CommonAPI.js +49 -0
  753. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  754. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +564 -0
  755. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -0
  756. package/build/dist/Server/API/MicrosoftTeamsAPI.js +465 -176
  757. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  758. package/build/dist/Server/API/MonitorAPI.js +107 -1
  759. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  760. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +5 -16
  761. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -1
  762. package/build/dist/Server/API/SlackAPI.js +322 -78
  763. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  764. package/build/dist/Server/API/StatusPageAPI.js +123 -17
  765. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  766. package/build/dist/Server/API/TelemetryAPI.js +144 -5
  767. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  768. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js +1 -0
  769. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js.map +1 -1
  770. package/build/dist/Server/API/WorkflowVariableAPI.js +109 -0
  771. package/build/dist/Server/API/WorkflowVariableAPI.js.map +1 -0
  772. package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
  773. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  774. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
  775. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
  776. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
  777. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
  778. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js +38 -0
  779. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js.map +1 -0
  780. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js +54 -0
  781. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js.map +1 -0
  782. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js +14 -0
  783. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js.map +1 -0
  784. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js +12 -0
  785. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js.map +1 -0
  786. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js +32 -0
  787. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js.map +1 -0
  788. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
  789. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  790. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +21 -0
  791. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  792. package/build/dist/Server/Middleware/TelemetryIngest.js +32 -2
  793. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  794. package/build/dist/Server/Middleware/UserAuthorization.js +39 -6
  795. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  796. package/build/dist/Server/Services/AIAgentService.js +1 -0
  797. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  798. package/build/dist/Server/Services/AccessTokenService.js +87 -42
  799. package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
  800. package/build/dist/Server/Services/AlertEpisodeMemberService.js +4 -0
  801. package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
  802. package/build/dist/Server/Services/AlertEpisodeService.js +12 -1
  803. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  804. package/build/dist/Server/Services/AlertService.js +110 -23
  805. package/build/dist/Server/Services/AlertService.js.map +1 -1
  806. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +716 -30
  807. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  808. package/build/dist/Server/Services/AutoRemediationRuleService.js +100 -0
  809. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  810. package/build/dist/Server/Services/BillingService.js +1 -0
  811. package/build/dist/Server/Services/BillingService.js.map +1 -1
  812. package/build/dist/Server/Services/DatabaseService.js +7 -0
  813. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  814. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js +11 -16
  815. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js.map +1 -1
  816. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +4 -0
  817. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  818. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +48 -0
  819. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  820. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js +21 -0
  821. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js.map +1 -1
  822. package/build/dist/Server/Services/IncidentEpisodeService.js +12 -1
  823. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  824. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -0
  825. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  826. package/build/dist/Server/Services/IncidentMemberService.js +14 -0
  827. package/build/dist/Server/Services/IncidentMemberService.js.map +1 -1
  828. package/build/dist/Server/Services/IncidentPublicNoteService.js +48 -0
  829. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  830. package/build/dist/Server/Services/IncidentService.js +99 -21
  831. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  832. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  833. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  834. package/build/dist/Server/Services/IncidentTemplateService.js +68 -4
  835. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  836. package/build/dist/Server/Services/Index.js +4 -0
  837. package/build/dist/Server/Services/Index.js.map +1 -1
  838. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +1470 -0
  839. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -0
  840. package/build/dist/Server/Services/KubernetesClusterService.js +1097 -0
  841. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  842. package/build/dist/Server/Services/LogAggregationService.js +14 -6
  843. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  844. package/build/dist/Server/Services/MonitorService.js +490 -50
  845. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  846. package/build/dist/Server/Services/MonitorStatusTimelineService.js +540 -3
  847. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  848. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -0
  849. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  850. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -0
  851. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  852. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -0
  853. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  854. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +3 -0
  855. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  856. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +29 -7
  857. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -1
  858. package/build/dist/Server/Services/OnCallSetupReminderService.js +1 -0
  859. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -1
  860. package/build/dist/Server/Services/ProbeService.js +7 -1
  861. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  862. package/build/dist/Server/Services/ProjectService.js +11 -1
  863. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  864. package/build/dist/Server/Services/RunbookCredentialService.js +24 -0
  865. package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -1
  866. package/build/dist/Server/Services/RunbookSecretService.js +44 -0
  867. package/build/dist/Server/Services/RunbookSecretService.js.map +1 -1
  868. package/build/dist/Server/Services/RunnerJobService.js +641 -21
  869. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  870. package/build/dist/Server/Services/RunnerService.js +344 -5
  871. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  872. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +48 -0
  873. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  874. package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -20
  875. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  876. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +67 -4
  877. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  878. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
  879. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
  880. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
  881. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
  882. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
  883. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
  884. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
  885. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
  886. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
  887. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
  888. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
  889. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
  890. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
  891. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  892. package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -0
  893. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  894. package/build/dist/Server/Services/StatusPageService.js +230 -32
  895. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  896. package/build/dist/Server/Services/StatusPageSubscriberService.js +4 -0
  897. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  898. package/build/dist/Server/Services/TeamMemberService.js +270 -11
  899. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  900. package/build/dist/Server/Services/TraceAggregationService.js +10 -6
  901. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  902. package/build/dist/Server/Services/UserEmailService.js +1 -0
  903. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  904. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1 -0
  905. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  906. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +1 -0
  907. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  908. package/build/dist/Server/Services/UserNotificationRuleService.js +4 -0
  909. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  910. package/build/dist/Server/Services/UserService.js +107 -1
  911. package/build/dist/Server/Services/UserService.js.map +1 -1
  912. package/build/dist/Server/Services/WorkflowVariableService.js +396 -7
  913. package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
  914. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +463 -7
  915. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  916. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +144 -2
  917. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  918. package/build/dist/Server/Types/Database/QueryHelper.js +20 -0
  919. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  920. package/build/dist/Server/Types/Markdown.js +394 -27
  921. package/build/dist/Server/Types/Markdown.js.map +1 -1
  922. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
  923. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
  924. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +123 -0
  925. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -0
  926. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +344 -0
  927. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -0
  928. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +587 -0
  929. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -0
  930. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +18 -2
  931. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -1
  932. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1380 -239
  933. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  934. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +1099 -68
  935. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  936. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +88 -6
  937. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  938. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +45 -11
  939. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  940. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +41 -11
  941. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  942. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +11 -4
  943. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  944. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +4 -2
  945. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  946. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
  947. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
  948. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +997 -141
  949. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  950. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +299 -4
  951. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  952. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +120 -0
  953. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -0
  954. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +160 -1
  955. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  956. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +1 -0
  957. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  958. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +22 -51
  959. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -1
  960. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
  961. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
  962. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +59 -12
  963. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  964. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +139 -109
  965. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  966. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +5 -1
  967. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  968. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +84 -22
  969. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  970. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +70 -5
  971. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -1
  972. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js +249 -0
  973. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js.map +1 -0
  974. package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
  975. package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
  976. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1 -0
  977. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -1
  978. package/build/dist/Server/Utils/PushNotificationUtil.js +27 -0
  979. package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
  980. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +26 -3
  981. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -1
  982. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
  983. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  984. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +38 -3
  985. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -1
  986. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +143 -23
  987. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  988. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js +406 -0
  989. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js.map +1 -0
  990. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
  991. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
  992. package/build/dist/Server/Utils/StartServer.js +4 -3
  993. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  994. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +382 -0
  995. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -0
  996. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js +1 -0
  997. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js.map +1 -1
  998. package/build/dist/Server/Utils/UserPermission/UserPermission.js +10 -0
  999. package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -1
  1000. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +447 -0
  1001. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -0
  1002. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js +202 -0
  1003. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js.map +1 -0
  1004. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +39 -1
  1005. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  1006. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +44 -4
  1007. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  1008. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +150 -191
  1009. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  1010. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +44 -4
  1011. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  1012. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +39 -17
  1013. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -1
  1014. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +151 -145
  1015. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  1016. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +221 -127
  1017. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  1018. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js +91 -0
  1019. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js.map +1 -0
  1020. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js +673 -0
  1021. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js.map +1 -0
  1022. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +3 -9
  1023. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
  1024. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +65 -166
  1025. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
  1026. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +63 -157
  1027. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
  1028. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +23 -3
  1029. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
  1030. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js +92 -0
  1031. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js.map +1 -0
  1032. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +81 -210
  1033. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
  1034. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +68 -138
  1035. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
  1036. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js +240 -0
  1037. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js.map +1 -0
  1038. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +85 -212
  1039. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
  1040. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +60 -17
  1041. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  1042. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  1043. package/build/dist/Server/Utils/Workspace/Workspace.js +134 -41
  1044. package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
  1045. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js +197 -0
  1046. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js.map +1 -0
  1047. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js +32 -0
  1048. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  1049. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js +199 -0
  1050. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js.map +1 -0
  1051. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js +91 -0
  1052. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js.map +1 -0
  1053. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js +450 -0
  1054. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js.map +1 -0
  1055. package/build/dist/Types/API/Route.js +18 -2
  1056. package/build/dist/Types/API/Route.js.map +1 -1
  1057. package/build/dist/Types/API/URL.js +23 -2
  1058. package/build/dist/Types/API/URL.js.map +1 -1
  1059. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +103 -5
  1060. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  1061. package/build/dist/Types/Date.js +97 -7
  1062. package/build/dist/Types/Date.js.map +1 -1
  1063. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  1064. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  1065. package/build/dist/Types/Exception/ExceptionMessages.js +1 -0
  1066. package/build/dist/Types/Exception/ExceptionMessages.js.map +1 -1
  1067. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +245 -0
  1068. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -0
  1069. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +49 -0
  1070. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -0
  1071. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js +16 -0
  1072. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js.map +1 -0
  1073. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js +104 -0
  1074. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js.map +1 -0
  1075. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +6 -5
  1076. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  1077. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js +32 -0
  1078. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js.map +1 -1
  1079. package/build/dist/Types/Monitor/MonitorUptimeSummary.js +52 -0
  1080. package/build/dist/Types/Monitor/MonitorUptimeSummary.js.map +1 -0
  1081. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +9 -1
  1082. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  1083. package/build/dist/Types/Permission.js +84 -0
  1084. package/build/dist/Types/Permission.js.map +1 -1
  1085. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
  1086. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  1087. package/build/dist/Types/Rules/RuleRun.js +4 -0
  1088. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  1089. package/build/dist/Types/Rum/SessionReplay.js +52 -2
  1090. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  1091. package/build/dist/Types/Rum/SessionReplayApi.js +11 -0
  1092. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  1093. package/build/dist/Types/Rum/UserFlow.js +115 -0
  1094. package/build/dist/Types/Rum/UserFlow.js.map +1 -0
  1095. package/build/dist/Types/Runbook/RunbookStepType.js +10 -0
  1096. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  1097. package/build/dist/Types/Runbook/RunnerJobOrigin.js +14 -0
  1098. package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -1
  1099. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +2 -2
  1100. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -1
  1101. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js +2 -0
  1102. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js.map +1 -0
  1103. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js +65 -0
  1104. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js.map +1 -0
  1105. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +8 -0
  1106. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  1107. package/build/dist/Types/Timezone.js +5 -0
  1108. package/build/dist/Types/Timezone.js.map +1 -1
  1109. package/build/dist/Types/TimezoneAlias.js +340 -0
  1110. package/build/dist/Types/TimezoneAlias.js.map +1 -0
  1111. package/build/dist/Types/Workflow/Templates.js +2694 -2
  1112. package/build/dist/Types/Workflow/Templates.js.map +1 -1
  1113. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js +314 -0
  1114. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js.map +1 -0
  1115. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js +98 -0
  1116. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js.map +1 -0
  1117. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js +95 -0
  1118. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js.map +1 -0
  1119. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js +44 -0
  1120. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js.map +1 -0
  1121. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js +27 -0
  1122. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js.map +1 -0
  1123. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js +9 -0
  1124. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js.map +1 -0
  1125. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js +260 -0
  1126. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js.map +1 -0
  1127. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js +56 -0
  1128. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js.map +1 -0
  1129. package/build/dist/UI/Components/Calendar/Calendar.js +94 -7
  1130. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  1131. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js +136 -0
  1132. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js.map +1 -0
  1133. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +1 -1
  1134. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  1135. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js +72 -0
  1136. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js.map +1 -0
  1137. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +178 -0
  1138. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -0
  1139. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js +13 -1
  1140. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js.map +1 -1
  1141. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +20 -4
  1142. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -1
  1143. package/build/dist/UI/Components/Dropdown/Dropdown.js +33 -9
  1144. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  1145. package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
  1146. package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
  1147. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
  1148. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
  1149. package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
  1150. package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
  1151. package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
  1152. package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
  1153. package/build/dist/UI/Components/Forms/Fields/FormField.js +36 -13
  1154. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  1155. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +117 -11
  1156. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  1157. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -1
  1158. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  1159. package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -1
  1160. package/build/dist/UI/Components/Icon/Icon.js +1 -1
  1161. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  1162. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +36 -8
  1163. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  1164. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js +10 -2
  1165. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js.map +1 -1
  1166. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +9 -1
  1167. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  1168. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +16 -15
  1169. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  1170. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  1171. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  1172. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +14 -74
  1173. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  1174. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +22 -5
  1175. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  1176. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +9 -2
  1177. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
  1178. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +16 -5
  1179. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -1
  1180. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +23 -8
  1181. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  1182. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +7 -1
  1183. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  1184. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +5 -1
  1185. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  1186. package/build/dist/UI/Components/Page/ModelPage.js +37 -3
  1187. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  1188. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +27 -6
  1189. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  1190. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +7 -1
  1191. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  1192. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  1193. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  1194. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +17 -77
  1195. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  1196. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +10 -1
  1197. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  1198. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +15 -1
  1199. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -1
  1200. package/build/dist/UI/Utils/Timezone.js +52 -17
  1201. package/build/dist/UI/Utils/Timezone.js.map +1 -1
  1202. package/build/dist/Utils/AI/InvestigationReport.js +116 -21
  1203. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  1204. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js +1209 -0
  1205. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js.map +1 -0
  1206. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +2973 -0
  1207. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -0
  1208. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js +99 -0
  1209. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js.map +1 -0
  1210. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js +1429 -0
  1211. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js.map +1 -0
  1212. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +7 -6
  1213. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -1
  1214. package/build/dist/Utils/LabelRuleImportExport.js +22 -6
  1215. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  1216. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js +214 -0
  1217. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js.map +1 -0
  1218. package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
  1219. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js +471 -0
  1220. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js.map +1 -0
  1221. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js +107 -0
  1222. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js.map +1 -0
  1223. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js +191 -0
  1224. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js.map +1 -0
  1225. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js +1259 -0
  1226. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js.map +1 -0
  1227. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js +593 -0
  1228. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js.map +1 -0
  1229. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js +195 -0
  1230. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js.map +1 -0
  1231. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js +127 -0
  1232. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js.map +1 -0
  1233. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js +464 -0
  1234. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js.map +1 -0
  1235. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
  1236. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
  1237. package/build/dist/Utils/Rum/SessionIdentity.js +31 -1
  1238. package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
  1239. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js +128 -0
  1240. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js.map +1 -0
  1241. package/build/dist/Utils/Rum/UserFlow.js +941 -0
  1242. package/build/dist/Utils/Rum/UserFlow.js.map +1 -0
  1243. package/build/dist/Utils/Slo/SloDuration.js +13 -5
  1244. package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
  1245. package/build/dist/Utils/Slo/SloHealth.js +8 -7
  1246. package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
  1247. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
  1248. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
  1249. package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
  1250. package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
  1251. package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
  1252. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js +82 -0
  1253. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js.map +1 -0
  1254. package/build/dist/Utils/StatusPage/ResourceUptime.js +46 -0
  1255. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  1256. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  1257. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -1
  1258. package/build/dist/Utils/Telemetry/AttributesJSON.js +245 -0
  1259. package/build/dist/Utils/Telemetry/AttributesJSON.js.map +1 -0
  1260. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +103 -0
  1261. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -1
  1262. package/build/dist/Utils/Uptime/UptimeUtil.js +91 -27
  1263. package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
  1264. package/jest.config.json +2 -1
  1265. package/package.json +2 -1
  1266. package/tsconfig.tests.json +36 -0
  1267. package/Tests/UI/Components/EmptyState/EmptyStateGuideLinks.test.tsx +0 -337
  1268. package/UI/Components/EmptyState/EmptyStateGuideLinks.tsx +0 -124
  1269. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js +0 -47
  1270. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js.map +0 -1
@@ -0,0 +1,3100 @@
1
+ import MonitorProbe, {
2
+ MonitorStepProbeResponse,
3
+ } from "../../../Models/DatabaseModels/MonitorProbe";
4
+ import Probe, {
5
+ ProbeConnectionStatus,
6
+ } from "../../../Models/DatabaseModels/Probe";
7
+ import OneUptimeDate from "../../../Types/Date";
8
+ import { JSONObject } from "../../../Types/JSON";
9
+ import { CheckOn, FilterType } from "../../../Types/Monitor/CriteriaFilter";
10
+ import MonitorMetricType from "../../../Types/Monitor/MonitorMetricType";
11
+ import MonitorSteps from "../../../Types/Monitor/MonitorSteps";
12
+ import MonitorType from "../../../Types/Monitor/MonitorType";
13
+ import ObjectID from "../../../Types/ObjectID";
14
+ import Timezone from "../../../Types/Timezone";
15
+ import CronTab from "../../../Utils/CronTab";
16
+ import { MonitorCheckFreshness } from "../../../Utils/Monitor/MonitorCheckScheduleUtil";
17
+ import MonitorOverviewFamilyUtil, {
18
+ MonitorOverviewFamily,
19
+ MonitorOverviewSetupKind,
20
+ } from "../../../Utils/Monitor/MonitorOverviewFamily";
21
+ import MonitorOverviewPresentationUtil, {
22
+ MONITOR_LOG_MINIMUM_RETENTION_SECONDS,
23
+ MonitorOverviewFact,
24
+ MonitorOverviewPresentation,
25
+ MonitorOverviewPresentationInput,
26
+ MonitorOverviewRunState,
27
+ MonitorOverviewStatusRef,
28
+ } from "../../../Utils/Monitor/MonitorOverviewPresentationUtil";
29
+ import MonitorOverviewProbeUtil, {
30
+ MonitorOverviewProbeHealth,
31
+ MonitorOverviewProbeRow,
32
+ MonitorOverviewProbeSummary,
33
+ } from "../../../Utils/Monitor/MonitorOverviewProbeUtil";
34
+ import { afterEach, describe, expect, it, jest } from "@jest/globals";
35
+ import type { MockInstance } from "jest-mock";
36
+
37
+ /*
38
+ * The hero, facts, pulse and section switches of the monitor overview for
39
+ * every family and run state. The invariants at the top are the reason the
40
+ * page was redesigned: a monitor that is paused, not being checked, not set
41
+ * up or still waiting for data must never look healthy, and an unknown must
42
+ * never be presented as a zero or a "none".
43
+ */
44
+
45
+ const NOW: Date = new Date("2026-09-21T12:00:00.000Z");
46
+ const STEP_ID: string = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
47
+ const DEVICE_ID: string = "66666666-6666-4666-8666-666666666666";
48
+ const OTHER_DEVICE_ID: string = "77777777-7777-4777-8777-777777777777";
49
+
50
+ const secondsAgo: (seconds: number) => Date = (seconds: number): Date => {
51
+ return new Date(NOW.getTime() - seconds * 1000);
52
+ };
53
+
54
+ const DAY: number = 86400;
55
+
56
+ const OPERATIONAL: MonitorOverviewStatusRef = {
57
+ id: "22222222-2222-4222-8222-222222222222",
58
+ name: "Operational",
59
+ color: "#10B981",
60
+ isOperationalState: true,
61
+ isOfflineState: false,
62
+ };
63
+
64
+ const DEGRADED: MonitorOverviewStatusRef = {
65
+ id: "33333333-3333-4333-8333-333333333333",
66
+ name: "Degraded",
67
+ color: "#F59E0B",
68
+ isOperationalState: false,
69
+ isOfflineState: false,
70
+ };
71
+
72
+ const OFFLINE: MonitorOverviewStatusRef = {
73
+ id: "44444444-4444-4444-8444-444444444444",
74
+ name: "Offline",
75
+ color: "#EF4444",
76
+ isOperationalState: false,
77
+ isOfflineState: true,
78
+ };
79
+
80
+ const ALL_TYPES: Array<MonitorType> = Object.values(MonitorType);
81
+
82
+ const stepsOf: (...stepData: Array<JSONObject>) => MonitorSteps = (
83
+ ...stepData: Array<JSONObject>
84
+ ): MonitorSteps => {
85
+ return {
86
+ data: {
87
+ monitorStepsInstanceArray: stepData.map((data: JSONObject) => {
88
+ return { data: { id: STEP_ID, ...data } };
89
+ }),
90
+ },
91
+ } as unknown as MonitorSteps;
92
+ };
93
+
94
+ const NO_STEPS: MonitorSteps = stepsOf();
95
+
96
+ const missingWindowSteps: (
97
+ checkOn: CheckOn,
98
+ minutes: number,
99
+ ) => MonitorSteps = (checkOn: CheckOn, minutes: number): MonitorSteps => {
100
+ return stepsOf({
101
+ monitorCriteria: {
102
+ data: {
103
+ monitorCriteriaInstanceArray: [
104
+ {
105
+ data: {
106
+ filters: [
107
+ {
108
+ checkOn: checkOn,
109
+ filterType: FilterType.NotRecievedInMinutes,
110
+ value: minutes,
111
+ },
112
+ ],
113
+ },
114
+ },
115
+ ],
116
+ },
117
+ },
118
+ });
119
+ };
120
+
121
+ const probes: (
122
+ overrides?: Partial<MonitorOverviewProbeSummary>,
123
+ ) => MonitorOverviewProbeSummary = (
124
+ overrides?: Partial<MonitorOverviewProbeSummary>,
125
+ ): MonitorOverviewProbeSummary => {
126
+ return {
127
+ rows: [],
128
+ attachedCount: 2,
129
+ enabledCount: 2,
130
+ reportingCount: 2,
131
+ disabledCount: 0,
132
+ disconnectedCount: 0,
133
+ lastResultAt: secondsAgo(60),
134
+ nextCheckAt: secondsAgo(-240),
135
+ latestResult: {
136
+ probeId: "11111111-1111-4111-8111-111111111111",
137
+ probeName: "London",
138
+ monitoredAt: secondsAgo(60),
139
+ isOnline: true,
140
+ responseTimeInMs: 120,
141
+ responseCode: 200,
142
+ },
143
+ responseTime: {
144
+ medianMs: 120,
145
+ minMs: 100,
146
+ maxMs: 140,
147
+ respondedCount: 2,
148
+ totalCount: 2,
149
+ },
150
+ ...overrides,
151
+ };
152
+ };
153
+
154
+ const NO_RESULTS: Partial<MonitorOverviewProbeSummary> = {
155
+ reportingCount: 0,
156
+ lastResultAt: undefined,
157
+ latestResult: undefined,
158
+ responseTime: null,
159
+ };
160
+
161
+ /*
162
+ * A healthy, running monitor of the given type: the starting point every
163
+ * test changes one thing about.
164
+ */
165
+ const running: (
166
+ monitorType: MonitorType,
167
+ overrides?: Partial<MonitorOverviewPresentationInput>,
168
+ ) => MonitorOverviewPresentationInput = (
169
+ monitorType: MonitorType,
170
+ overrides?: Partial<MonitorOverviewPresentationInput>,
171
+ ): MonitorOverviewPresentationInput => {
172
+ const family: MonitorOverviewFamily =
173
+ MonitorOverviewFamilyUtil.getFamily(monitorType);
174
+
175
+ const base: MonitorOverviewPresentationInput = {
176
+ now: NOW,
177
+ monitorType: monitorType,
178
+ monitorSteps: stepsOf({
179
+ monitorDestination: "https://api.example.com/health",
180
+ networkDeviceMonitor: { networkDeviceId: DEVICE_ID },
181
+ }),
182
+ monitoringInterval: "*/5 * * * *",
183
+ createdAt: secondsAgo(30 * DAY),
184
+ currentStatus: OPERATIONAL,
185
+ statusSince: secondsAgo(3 * DAY + 4 * 3600),
186
+ pause: {
187
+ isDisabled: false,
188
+ byManualIncident: false,
189
+ byScheduledMaintenance: false,
190
+ },
191
+ probeFlags: { isNoProbeEnabled: false, isAllProbesDisconnected: false },
192
+ probes: family === MonitorOverviewFamily.ProbeCheck ? probes() : null,
193
+ heartbeat: {
194
+ lastReceivedAt: secondsAgo(120),
195
+ lastCheckedAt: secondsAgo(30),
196
+ requestMethod: "POST",
197
+ },
198
+ email: { lastReceivedAt: secondsAgo(300), lastCheckedAt: secondsAgo(40) },
199
+ agent: {
200
+ lastReportAt: secondsAgo(45),
201
+ hostname: "web-01.example.com",
202
+ cpuPercent: 42.4,
203
+ memoryPercent: 63.6,
204
+ },
205
+ telemetry: {
206
+ lastScheduledAt: secondsAgo(30),
207
+ nextEvaluationAt: secondsAgo(-270),
208
+ },
209
+ latestEvaluationAt: secondsAgo(90),
210
+ evaluationStatus: "loaded",
211
+ };
212
+
213
+ return { ...base, ...overrides };
214
+ };
215
+
216
+ const build: (
217
+ input: MonitorOverviewPresentationInput,
218
+ ) => MonitorOverviewPresentation = (
219
+ input: MonitorOverviewPresentationInput,
220
+ ): MonitorOverviewPresentation => {
221
+ return MonitorOverviewPresentationUtil.build(input);
222
+ };
223
+
224
+ const factKeys: (presentation: MonitorOverviewPresentation) => Array<string> = (
225
+ presentation: MonitorOverviewPresentation,
226
+ ): Array<string> => {
227
+ return presentation.facts.map((fact: MonitorOverviewFact) => {
228
+ return fact.key;
229
+ });
230
+ };
231
+
232
+ const fact: (
233
+ presentation: MonitorOverviewPresentation,
234
+ key: string,
235
+ ) => MonitorOverviewFact = (
236
+ presentation: MonitorOverviewPresentation,
237
+ key: string,
238
+ ): MonitorOverviewFact => {
239
+ const found: MonitorOverviewFact | undefined = presentation.facts.find(
240
+ (item: MonitorOverviewFact) => {
241
+ return item.key === key;
242
+ },
243
+ );
244
+
245
+ if (!found) {
246
+ throw new Error(`no ${key} fact in ${factKeys(presentation).join(", ")}`);
247
+ }
248
+
249
+ return found;
250
+ };
251
+
252
+ interface Scenario {
253
+ name: string;
254
+ input: MonitorOverviewPresentationInput;
255
+ runState: MonitorOverviewRunState;
256
+ // Awaiting, but already judged by the server, so its history is shown.
257
+ hasPushVerdict?: boolean;
258
+ }
259
+
260
+ /*
261
+ * One monitor per interesting state, across the families. The invariant
262
+ * tests run over all of them.
263
+ */
264
+ const SCENARIOS: Array<Scenario> = [
265
+ {
266
+ name: "manual with a status",
267
+ input: running(MonitorType.Manual),
268
+ runState: MonitorOverviewRunState.Manual,
269
+ },
270
+ {
271
+ name: "API disabled",
272
+ input: running(MonitorType.API, {
273
+ pause: {
274
+ isDisabled: true,
275
+ byManualIncident: false,
276
+ byScheduledMaintenance: false,
277
+ },
278
+ }),
279
+ runState: MonitorOverviewRunState.Paused,
280
+ },
281
+ {
282
+ name: "Logs paused by an incident",
283
+ input: running(MonitorType.Logs, {
284
+ currentStatus: OFFLINE,
285
+ pause: {
286
+ isDisabled: false,
287
+ byManualIncident: true,
288
+ byScheduledMaintenance: false,
289
+ },
290
+ }),
291
+ runState: MonitorOverviewRunState.Paused,
292
+ },
293
+ {
294
+ name: "incoming request in maintenance",
295
+ input: running(MonitorType.IncomingRequest, {
296
+ pause: {
297
+ isDisabled: false,
298
+ byManualIncident: false,
299
+ byScheduledMaintenance: true,
300
+ },
301
+ }),
302
+ runState: MonitorOverviewRunState.Paused,
303
+ },
304
+ {
305
+ name: "API with every probe switched off",
306
+ input: running(MonitorType.API, {
307
+ probeFlags: { isNoProbeEnabled: true, isAllProbesDisconnected: false },
308
+ probes: probes({
309
+ ...NO_RESULTS,
310
+ attachedCount: 2,
311
+ enabledCount: 0,
312
+ disabledCount: 2,
313
+ }),
314
+ }),
315
+ runState: MonitorOverviewRunState.NotChecking,
316
+ },
317
+ {
318
+ name: "Website with every probe disconnected",
319
+ input: running(MonitorType.Website, {
320
+ currentStatus: OPERATIONAL,
321
+ probeFlags: { isNoProbeEnabled: false, isAllProbesDisconnected: true },
322
+ }),
323
+ runState: MonitorOverviewRunState.NotChecking,
324
+ },
325
+ {
326
+ name: "DNS with no criteria",
327
+ input: running(MonitorType.DNS, { monitorSteps: NO_STEPS }),
328
+ runState: MonitorOverviewRunState.NotConfigured,
329
+ },
330
+ {
331
+ name: "network device with no device",
332
+ input: running(MonitorType.NetworkDevice, {
333
+ monitorSteps: stepsOf({ networkDeviceMonitor: {} }),
334
+ }),
335
+ runState: MonitorOverviewRunState.NotConfigured,
336
+ },
337
+ {
338
+ name: "new API monitor",
339
+ input: running(MonitorType.API, {
340
+ createdAt: secondsAgo(120),
341
+ statusSince: undefined,
342
+ probes: probes(NO_RESULTS),
343
+ }),
344
+ runState: MonitorOverviewRunState.AwaitingFirstData,
345
+ },
346
+ {
347
+ name: "heartbeat that never received a request",
348
+ input: running(MonitorType.IncomingRequest, {
349
+ heartbeat: {},
350
+ }),
351
+ runState: MonitorOverviewRunState.AwaitingFirstData,
352
+ },
353
+ {
354
+ name: "inbound email that never received one",
355
+ input: running(MonitorType.IncomingEmail, { email: {} }),
356
+ runState: MonitorOverviewRunState.AwaitingFirstData,
357
+ },
358
+ {
359
+ name: "server whose agent never reported",
360
+ input: running(MonitorType.Server, { agent: {} }),
361
+ runState: MonitorOverviewRunState.AwaitingFirstData,
362
+ },
363
+ {
364
+ name: "kubernetes never evaluated",
365
+ input: running(MonitorType.Kubernetes, {
366
+ createdAt: secondsAgo(120),
367
+ telemetry: {},
368
+ latestEvaluationAt: undefined,
369
+ }),
370
+ runState: MonitorOverviewRunState.AwaitingFirstData,
371
+ },
372
+ {
373
+ name: "API with a stale result",
374
+ input: running(MonitorType.API, {
375
+ probes: probes({ lastResultAt: secondsAgo(2 * 3600) }),
376
+ }),
377
+ runState: MonitorOverviewRunState.Overdue,
378
+ },
379
+ {
380
+ name: "offline API with a stale result",
381
+ input: running(MonitorType.API, {
382
+ currentStatus: OFFLINE,
383
+ probes: probes({ lastResultAt: secondsAgo(2 * 3600) }),
384
+ }),
385
+ runState: MonitorOverviewRunState.Overdue,
386
+ },
387
+ {
388
+ /*
389
+ * The worker keeps stamping "last scheduled" every minute, but nothing
390
+ * has been evaluated for an hour: the queue is stuck, or every
391
+ * evaluation fails.
392
+ */
393
+ name: "metrics monitor with a stale evaluation",
394
+ input: running(MonitorType.Metrics, {
395
+ monitoringInterval: "* * * * *",
396
+ telemetry: {
397
+ lastScheduledAt: secondsAgo(10),
398
+ nextEvaluationAt: secondsAgo(-50),
399
+ },
400
+ latestEvaluationAt: secondsAgo(3600),
401
+ }),
402
+ runState: MonitorOverviewRunState.Overdue,
403
+ },
404
+ {
405
+ name: "running API",
406
+ input: running(MonitorType.API),
407
+ runState: MonitorOverviewRunState.Running,
408
+ },
409
+ {
410
+ name: "running heartbeat",
411
+ input: running(MonitorType.IncomingRequest),
412
+ runState: MonitorOverviewRunState.Running,
413
+ },
414
+ {
415
+ name: "running network device",
416
+ input: running(MonitorType.NetworkDevice),
417
+ runState: MonitorOverviewRunState.Running,
418
+ },
419
+ {
420
+ name: "running server with no status",
421
+ input: running(MonitorType.Server, { currentStatus: undefined }),
422
+ runState: MonitorOverviewRunState.Running,
423
+ },
424
+ {
425
+ name: "heartbeat that never received a request, judged offline",
426
+ input: running(MonitorType.IncomingRequest, {
427
+ currentStatus: OFFLINE,
428
+ heartbeat: {},
429
+ }),
430
+ runState: MonitorOverviewRunState.AwaitingFirstData,
431
+ hasPushVerdict: true,
432
+ },
433
+ {
434
+ name: "API that never reported, created two days ago",
435
+ input: running(MonitorType.API, {
436
+ createdAt: secondsAgo(2 * DAY),
437
+ statusSince: undefined,
438
+ probes: probes({ ...NO_RESULTS, nextCheckAt: undefined }),
439
+ }),
440
+ runState: MonitorOverviewRunState.Overdue,
441
+ },
442
+ {
443
+ name: "logs monitor with nothing in its evaluation log's last day",
444
+ input: running(MonitorType.Logs, {
445
+ createdAt: secondsAgo(10 * DAY),
446
+ latestEvaluationAt: undefined,
447
+ }),
448
+ runState: MonitorOverviewRunState.Overdue,
449
+ },
450
+ {
451
+ name: "new network device whose log is loaded and empty",
452
+ input: running(MonitorType.NetworkDevice, {
453
+ createdAt: secondsAgo(3600),
454
+ latestEvaluationAt: undefined,
455
+ }),
456
+ runState: MonitorOverviewRunState.AwaitingFirstData,
457
+ },
458
+ {
459
+ // Its rows expired: the log only covers the last day or two.
460
+ name: "network device with nothing in its evaluation log's last day",
461
+ input: running(MonitorType.NetworkDevice, {
462
+ currentStatus: OFFLINE,
463
+ latestEvaluationAt: undefined,
464
+ }),
465
+ runState: MonitorOverviewRunState.Running,
466
+ },
467
+ {
468
+ name: "weekly logs monitor whose last evaluation has left the log",
469
+ input: running(MonitorType.Logs, {
470
+ // Fridays at noon; now is Monday noon.
471
+ monitoringInterval: "0 12 * * 5",
472
+ createdAt: secondsAgo(90 * DAY),
473
+ telemetry: {
474
+ lastScheduledAt: secondsAgo(3 * DAY),
475
+ nextEvaluationAt: secondsAgo(-4 * DAY),
476
+ },
477
+ latestEvaluationAt: undefined,
478
+ }),
479
+ runState: MonitorOverviewRunState.Running,
480
+ },
481
+ ];
482
+
483
+ describe("MonitorOverviewPresentationUtil run states", () => {
484
+ it.each(SCENARIOS)("$name is $runState", (scenario: Scenario) => {
485
+ expect(MonitorOverviewPresentationUtil.getRunState(scenario.input)).toBe(
486
+ scenario.runState,
487
+ );
488
+ expect(build(scenario.input).runState).toBe(scenario.runState);
489
+ });
490
+
491
+ it("run-state precedence table", () => {
492
+ const rows: Array<
493
+ [string, MonitorOverviewPresentationInput, MonitorOverviewRunState]
494
+ > = [
495
+ [
496
+ "Manual beats disabled",
497
+ running(MonitorType.Manual, {
498
+ pause: {
499
+ isDisabled: true,
500
+ byManualIncident: true,
501
+ byScheduledMaintenance: true,
502
+ },
503
+ }),
504
+ MonitorOverviewRunState.Manual,
505
+ ],
506
+ [
507
+ "disabled beats not checking",
508
+ running(MonitorType.API, {
509
+ pause: {
510
+ isDisabled: true,
511
+ byManualIncident: false,
512
+ byScheduledMaintenance: false,
513
+ },
514
+ probeFlags: {
515
+ isNoProbeEnabled: true,
516
+ isAllProbesDisconnected: true,
517
+ },
518
+ }),
519
+ MonitorOverviewRunState.Paused,
520
+ ],
521
+ [
522
+ "paused with no probes",
523
+ running(MonitorType.API, {
524
+ pause: {
525
+ isDisabled: false,
526
+ byManualIncident: false,
527
+ byScheduledMaintenance: true,
528
+ },
529
+ probes: probes({
530
+ ...NO_RESULTS,
531
+ attachedCount: 0,
532
+ enabledCount: 0,
533
+ }),
534
+ }),
535
+ MonitorOverviewRunState.Paused,
536
+ ],
537
+ [
538
+ "not checking beats awaiting",
539
+ running(MonitorType.API, {
540
+ createdAt: secondsAgo(60),
541
+ probeFlags: {
542
+ isNoProbeEnabled: true,
543
+ isAllProbesDisconnected: false,
544
+ },
545
+ probes: probes(NO_RESULTS),
546
+ }),
547
+ MonitorOverviewRunState.NotChecking,
548
+ ],
549
+ [
550
+ "not checking beats not configured",
551
+ running(MonitorType.API, {
552
+ monitorSteps: NO_STEPS,
553
+ probeFlags: {
554
+ isNoProbeEnabled: false,
555
+ isAllProbesDisconnected: true,
556
+ },
557
+ }),
558
+ MonitorOverviewRunState.NotChecking,
559
+ ],
560
+ [
561
+ "not configured beats awaiting",
562
+ running(MonitorType.API, {
563
+ monitorSteps: NO_STEPS,
564
+ createdAt: secondsAgo(60),
565
+ probes: probes(NO_RESULTS),
566
+ }),
567
+ MonitorOverviewRunState.NotConfigured,
568
+ ],
569
+ [
570
+ "awaiting beats overdue while only the scheduler's stamp is known",
571
+ running(MonitorType.Logs, {
572
+ createdAt: secondsAgo(10 * DAY),
573
+ telemetry: { nextEvaluationAt: secondsAgo(DAY) },
574
+ latestEvaluationAt: undefined,
575
+ evaluationStatus: "error",
576
+ }),
577
+ MonitorOverviewRunState.AwaitingFirstData,
578
+ ],
579
+ [
580
+ "overdue beats running",
581
+ running(MonitorType.Ping, {
582
+ probes: probes({ lastResultAt: secondsAgo(3600) }),
583
+ }),
584
+ MonitorOverviewRunState.Overdue,
585
+ ],
586
+ [
587
+ "probe flags only matter for probe checks",
588
+ running(MonitorType.IncomingRequest, {
589
+ probeFlags: {
590
+ isNoProbeEnabled: true,
591
+ isAllProbesDisconnected: true,
592
+ },
593
+ }),
594
+ MonitorOverviewRunState.Running,
595
+ ],
596
+ [
597
+ "the probe rows say no probe is enabled before the flag does",
598
+ running(MonitorType.API, {
599
+ probes: probes({
600
+ ...NO_RESULTS,
601
+ enabledCount: 0,
602
+ disabledCount: 2,
603
+ }),
604
+ }),
605
+ MonitorOverviewRunState.NotChecking,
606
+ ],
607
+ [
608
+ "the probe rows say every enabled probe is disconnected",
609
+ running(MonitorType.API, {
610
+ probes: probes({ disconnectedCount: 2, reportingCount: 0 }),
611
+ }),
612
+ MonitorOverviewRunState.NotChecking,
613
+ ],
614
+ [
615
+ "unknown probes are never overdue or awaiting",
616
+ running(MonitorType.API, { probes: null }),
617
+ MonitorOverviewRunState.Running,
618
+ ],
619
+ [
620
+ "a network device is not awaiting while its log is unknown",
621
+ running(MonitorType.NetworkDevice, {
622
+ latestEvaluationAt: undefined,
623
+ evaluationStatus: undefined,
624
+ createdAt: secondsAgo(60),
625
+ }),
626
+ MonitorOverviewRunState.Running,
627
+ ],
628
+ [
629
+ "a network device is not awaiting while its log is unreadable",
630
+ running(MonitorType.NetworkDevice, {
631
+ latestEvaluationAt: undefined,
632
+ evaluationStatus: "forbidden",
633
+ }),
634
+ MonitorOverviewRunState.Running,
635
+ ],
636
+ ];
637
+
638
+ for (const [name, input, expected] of rows) {
639
+ expect({ name: name, runState: build(input).runState }).toEqual({
640
+ name: name,
641
+ runState: expected,
642
+ });
643
+ }
644
+ });
645
+ });
646
+
647
+ describe("MonitorOverviewPresentationUtil invariants", () => {
648
+ it.each(ALL_TYPES)(
649
+ "%s never produces more than 4 facts, minimal and full input",
650
+ (monitorType: MonitorType) => {
651
+ const minimal: MonitorOverviewPresentationInput = {
652
+ now: NOW,
653
+ monitorType: monitorType,
654
+ pause: {
655
+ isDisabled: false,
656
+ byManualIncident: false,
657
+ byScheduledMaintenance: false,
658
+ },
659
+ probeFlags: {
660
+ isNoProbeEnabled: false,
661
+ isAllProbesDisconnected: false,
662
+ },
663
+ probes: null,
664
+ heartbeat: {},
665
+ email: {},
666
+ agent: {},
667
+ telemetry: {},
668
+ };
669
+
670
+ for (const input of [minimal, running(monitorType)]) {
671
+ const presentation: MonitorOverviewPresentation = build(input);
672
+
673
+ expect(presentation.facts.length).toBeGreaterThanOrEqual(1);
674
+ expect(presentation.facts.length).toBeLessThanOrEqual(4);
675
+ expect(presentation.facts[presentation.facts.length - 1]!.key).toBe(
676
+ "owners",
677
+ );
678
+ expect(new Set(factKeys(presentation)).size).toBe(
679
+ presentation.facts.length,
680
+ );
681
+ }
682
+ },
683
+ );
684
+
685
+ it("states Paused/NotChecking/NotConfigured/AwaitingFirstData are never good and never show the status name as the primary badge", () => {
686
+ const guarded: Array<MonitorOverviewRunState> = [
687
+ MonitorOverviewRunState.Paused,
688
+ MonitorOverviewRunState.NotChecking,
689
+ MonitorOverviewRunState.NotConfigured,
690
+ MonitorOverviewRunState.AwaitingFirstData,
691
+ ];
692
+ let checked: number = 0;
693
+
694
+ for (const scenario of SCENARIOS) {
695
+ const presentation: MonitorOverviewPresentation = build(scenario.input);
696
+
697
+ if (!guarded.includes(presentation.runState)) {
698
+ continue;
699
+ }
700
+
701
+ checked++;
702
+ expect({ name: scenario.name, tone: presentation.tone }).not.toEqual({
703
+ name: scenario.name,
704
+ tone: "good",
705
+ });
706
+ expect(presentation.badge.tone).not.toBe("good");
707
+ expect(presentation.badge.text).not.toBe(
708
+ scenario.input.currentStatus?.name,
709
+ );
710
+ expect(presentation.badge.statusColor).toBeUndefined();
711
+ }
712
+
713
+ expect(checked).toBeGreaterThanOrEqual(10);
714
+ });
715
+
716
+ it("Overdue is never good", () => {
717
+ for (const status of [OPERATIONAL, DEGRADED, OFFLINE, undefined]) {
718
+ const presentation: MonitorOverviewPresentation = build(
719
+ running(MonitorType.API, {
720
+ currentStatus: status,
721
+ probes: probes({ lastResultAt: secondsAgo(2 * 3600) }),
722
+ }),
723
+ );
724
+
725
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Overdue);
726
+ expect(presentation.tone).not.toBe("good");
727
+ expect(presentation.secondaryBadges).toEqual([
728
+ { text: "Checks overdue", tone: "warning" },
729
+ ]);
730
+ }
731
+
732
+ expect(
733
+ build(
734
+ running(MonitorType.API, {
735
+ currentStatus: OPERATIONAL,
736
+ probes: probes({ lastResultAt: secondsAgo(2 * 3600) }),
737
+ }),
738
+ ).tone,
739
+ ).toBe("warning");
740
+ expect(
741
+ build(
742
+ running(MonitorType.API, {
743
+ currentStatus: OFFLINE,
744
+ probes: probes({ lastResultAt: secondsAgo(2 * 3600) }),
745
+ }),
746
+ ).tone,
747
+ ).toBe("danger");
748
+ });
749
+
750
+ it("showUptime is false only while awaiting first data that nothing has judged", () => {
751
+ for (const scenario of SCENARIOS) {
752
+ const presentation: MonitorOverviewPresentation = build(scenario.input);
753
+
754
+ expect({
755
+ name: scenario.name,
756
+ showUptime: presentation.sections.showUptime,
757
+ }).toEqual({
758
+ name: scenario.name,
759
+ showUptime:
760
+ presentation.runState !== MonitorOverviewRunState.AwaitingFirstData ||
761
+ scenario.hasPushVerdict === true,
762
+ });
763
+ }
764
+ });
765
+
766
+ it("owners is always the last fact", () => {
767
+ for (const scenario of SCENARIOS) {
768
+ const presentation: MonitorOverviewPresentation = build(scenario.input);
769
+ const last: MonitorOverviewFact =
770
+ presentation.facts[presentation.facts.length - 1]!;
771
+
772
+ expect(last).toEqual({ key: "owners", label: "Owners", value: "" });
773
+ }
774
+ });
775
+ });
776
+
777
+ describe("MonitorOverviewPresentationUtil hero", () => {
778
+ it("running shows the status, its colour and how long it has held", () => {
779
+ const presentation: MonitorOverviewPresentation = build(
780
+ running(MonitorType.API),
781
+ );
782
+
783
+ expect(presentation.tone).toBe("good");
784
+ expect(presentation.badge).toEqual({
785
+ text: "Operational",
786
+ tone: "good",
787
+ statusColor: "#10B981",
788
+ });
789
+ expect(presentation.headline).toEqual({
790
+ text: "Operational",
791
+ since: secondsAgo(3 * DAY + 4 * 3600),
792
+ });
793
+ expect(presentation.secondaryBadges).toEqual([]);
794
+ expect(presentation.explanation).toBeUndefined();
795
+ expect(presentation.callToAction).toBeUndefined();
796
+ expect(presentation.freshness).toBe(MonitorCheckFreshness.Fresh);
797
+ });
798
+
799
+ it('since is used only when given; no status gives "No status recorded yet"', () => {
800
+ expect(
801
+ build(running(MonitorType.API, { statusSince: undefined })).headline,
802
+ ).toEqual({ text: "Operational", since: undefined });
803
+
804
+ const noStatus: MonitorOverviewPresentation = build(
805
+ running(MonitorType.API, {
806
+ currentStatus: undefined,
807
+ statusSince: secondsAgo(60),
808
+ }),
809
+ );
810
+
811
+ expect(noStatus.headline).toEqual({ text: "No status recorded yet" });
812
+ expect(noStatus.badge).toEqual({ text: "Unknown status", tone: "neutral" });
813
+ expect(noStatus.tone).toBe("neutral");
814
+ });
815
+
816
+ it("offline status is danger, operational is good, any other is warning", () => {
817
+ expect(MonitorOverviewPresentationUtil.getStatusTone(OPERATIONAL)).toBe(
818
+ "good",
819
+ );
820
+ expect(MonitorOverviewPresentationUtil.getStatusTone(OFFLINE)).toBe(
821
+ "danger",
822
+ );
823
+ expect(MonitorOverviewPresentationUtil.getStatusTone(DEGRADED)).toBe(
824
+ "warning",
825
+ );
826
+ expect(MonitorOverviewPresentationUtil.getStatusTone(undefined)).toBe(
827
+ "neutral",
828
+ );
829
+ // A status flagged both ways must not read as good.
830
+ expect(
831
+ MonitorOverviewPresentationUtil.getStatusTone({
832
+ ...OFFLINE,
833
+ isOperationalState: true,
834
+ }),
835
+ ).toBe("danger");
836
+
837
+ expect(
838
+ build(running(MonitorType.API, { currentStatus: OFFLINE })).badge,
839
+ ).toEqual({ text: "Offline", tone: "danger", statusColor: "#EF4444" });
840
+ expect(
841
+ build(running(MonitorType.API, { currentStatus: DEGRADED })).tone,
842
+ ).toBe("warning");
843
+ });
844
+
845
+ it("an unreadable status colour is dropped, not passed to a style", () => {
846
+ expect(
847
+ build(
848
+ running(MonitorType.API, {
849
+ currentStatus: { ...OPERATIONAL, color: "url(javascript:x)" },
850
+ }),
851
+ ).badge.statusColor,
852
+ ).toBeUndefined();
853
+ });
854
+
855
+ it("manual monitors say who sets the status", () => {
856
+ const presentation: MonitorOverviewPresentation = build(
857
+ running(MonitorType.Manual, { currentStatus: DEGRADED }),
858
+ );
859
+
860
+ expect(presentation.badge.text).toBe("Degraded");
861
+ expect(presentation.tone).toBe("warning");
862
+ expect(presentation.secondaryBadges).toEqual([
863
+ { text: "Manual", tone: "neutral" },
864
+ ]);
865
+ expect(presentation.explanation).toBe(
866
+ "Manual monitor: OneUptime runs no checks. The status changes when someone sets it, or when an incident or scheduled maintenance event changes it.",
867
+ );
868
+ expect(presentation.callToAction).toEqual({
869
+ text: "Change status",
870
+ linkKey: "statusTimeline",
871
+ });
872
+
873
+ const unknown: MonitorOverviewPresentation = build(
874
+ running(MonitorType.Manual, { currentStatus: undefined }),
875
+ );
876
+
877
+ expect(unknown.badge).toEqual({ text: "Unknown status", tone: "neutral" });
878
+ expect(unknown.headline.text).toBe("No status recorded yet");
879
+ });
880
+
881
+ it("paused copy per reason", () => {
882
+ const disabled: MonitorOverviewPresentation = build(SCENARIOS[1]!.input);
883
+
884
+ expect(disabled.badge).toEqual({ text: "Disabled", tone: "neutral" });
885
+ expect(disabled.headline.text).toBe("Monitoring is turned off");
886
+ expect(disabled.explanation).toBe(
887
+ "No checks run while monitoring is off, so the status stays at the last one recorded.",
888
+ );
889
+ expect(disabled.lastKnownStatus).toBe("Last recorded status: Operational");
890
+ expect(disabled.callToAction).toEqual({
891
+ text: "Open settings",
892
+ linkKey: "settings",
893
+ });
894
+
895
+ const byIncident: MonitorOverviewPresentation = build(SCENARIOS[2]!.input);
896
+
897
+ expect(byIncident.badge).toEqual({ text: "Paused", tone: "neutral" });
898
+ expect(byIncident.headline.text).toBe(
899
+ "Monitoring is paused by an incident",
900
+ );
901
+ expect(byIncident.lastKnownStatus).toBe("Last recorded status: Offline");
902
+ expect(byIncident.callToAction).toEqual({
903
+ text: "View incidents",
904
+ linkKey: "incidents",
905
+ });
906
+
907
+ const maintenance: MonitorOverviewPresentation = build(SCENARIOS[3]!.input);
908
+
909
+ expect(maintenance.badge.text).toBe("Paused");
910
+ expect(maintenance.headline.text).toBe(
911
+ "Monitoring is paused for scheduled maintenance",
912
+ );
913
+ expect(maintenance.explanation).toBe(
914
+ "Checks resume automatically when the maintenance event ends.",
915
+ );
916
+ expect(maintenance.callToAction).toBeUndefined();
917
+ });
918
+
919
+ it('paused with no probe enabled keeps a "Probes Not Enabled" secondary badge', () => {
920
+ const presentation: MonitorOverviewPresentation = build(
921
+ running(MonitorType.API, {
922
+ pause: {
923
+ isDisabled: true,
924
+ byManualIncident: false,
925
+ byScheduledMaintenance: false,
926
+ },
927
+ probeFlags: { isNoProbeEnabled: true, isAllProbesDisconnected: true },
928
+ }),
929
+ );
930
+
931
+ expect(presentation.badge.text).toBe("Disabled");
932
+ expect(presentation.secondaryBadges).toEqual([
933
+ { text: "Probes Not Enabled", tone: "danger" },
934
+ ]);
935
+
936
+ expect(
937
+ build(
938
+ running(MonitorType.API, {
939
+ pause: {
940
+ isDisabled: false,
941
+ byManualIncident: false,
942
+ byScheduledMaintenance: true,
943
+ },
944
+ probeFlags: {
945
+ isNoProbeEnabled: false,
946
+ isAllProbesDisconnected: true,
947
+ },
948
+ }),
949
+ ).secondaryBadges,
950
+ ).toEqual([{ text: "Probes Disconnected", tone: "danger" }]);
951
+
952
+ // Only probe checks have probes to complain about.
953
+ expect(
954
+ build(
955
+ running(MonitorType.Logs, {
956
+ pause: {
957
+ isDisabled: true,
958
+ byManualIncident: false,
959
+ byScheduledMaintenance: false,
960
+ },
961
+ probeFlags: {
962
+ isNoProbeEnabled: true,
963
+ isAllProbesDisconnected: false,
964
+ },
965
+ }),
966
+ ).secondaryBadges,
967
+ ).toEqual([]);
968
+ });
969
+
970
+ it("NotChecking wording for attached > 0, attached = 0, and unknown attachment", () => {
971
+ const switchedOff: (
972
+ attachedCount: number,
973
+ ) => MonitorOverviewPresentation = (
974
+ attachedCount: number,
975
+ ): MonitorOverviewPresentation => {
976
+ return build(
977
+ running(MonitorType.API, {
978
+ probeFlags: {
979
+ isNoProbeEnabled: true,
980
+ isAllProbesDisconnected: false,
981
+ },
982
+ probes: probes({
983
+ ...NO_RESULTS,
984
+ attachedCount: attachedCount,
985
+ enabledCount: 0,
986
+ disabledCount: attachedCount,
987
+ }),
988
+ }),
989
+ );
990
+ };
991
+
992
+ const three: MonitorOverviewPresentation = switchedOff(3);
993
+
994
+ expect(three.badge).toEqual({ text: "Probes Not Enabled", tone: "danger" });
995
+ expect(three.tone).toBe("danger");
996
+ expect(three.headline.text).toBe(
997
+ "Every probe is turned off for this monitor",
998
+ );
999
+ expect(three.explanation).toBe(
1000
+ "3 probes are attached but switched off, so nothing is checking this resource.",
1001
+ );
1002
+ expect(three.callToAction).toEqual({
1003
+ text: "Manage probes",
1004
+ linkKey: "probes",
1005
+ });
1006
+ expect(three.lastKnownStatus).toBe("Last recorded status: Operational");
1007
+
1008
+ expect(switchedOff(1).explanation).toBe(
1009
+ "1 probe is attached but switched off, so nothing is checking this resource.",
1010
+ );
1011
+
1012
+ const none: MonitorOverviewPresentation = switchedOff(0);
1013
+
1014
+ expect(none.headline.text).toBe("No probes are attached to this monitor");
1015
+ expect(none.explanation).toBe(
1016
+ "Add a probe to start checking this resource.",
1017
+ );
1018
+ expect(none.callToAction).toEqual({
1019
+ text: "Add a probe",
1020
+ linkKey: "probes",
1021
+ });
1022
+
1023
+ const unknown: MonitorOverviewPresentation = build(
1024
+ running(MonitorType.API, {
1025
+ probeFlags: { isNoProbeEnabled: true, isAllProbesDisconnected: false },
1026
+ probes: null,
1027
+ }),
1028
+ );
1029
+
1030
+ expect(unknown.headline.text).toBe("Nothing is checking this monitor");
1031
+ expect(unknown.explanation).toBe(
1032
+ "No probe is enabled for this monitor, so no checks run.",
1033
+ );
1034
+ expect(unknown.callToAction?.text).toBe("Manage probes");
1035
+
1036
+ const disconnected: MonitorOverviewPresentation = build(
1037
+ SCENARIOS[5]!.input,
1038
+ );
1039
+
1040
+ expect(disconnected.badge).toEqual({
1041
+ text: "Probes Disconnected",
1042
+ tone: "danger",
1043
+ });
1044
+ expect(disconnected.headline.text).toBe(
1045
+ "Every probe checking this monitor is disconnected",
1046
+ );
1047
+ expect(disconnected.callToAction).toEqual({
1048
+ text: "Check probes",
1049
+ linkKey: "probes",
1050
+ });
1051
+ });
1052
+
1053
+ it('unknown probes never produce "No probes are attached"', () => {
1054
+ for (const flags of [
1055
+ { isNoProbeEnabled: true, isAllProbesDisconnected: false },
1056
+ { isNoProbeEnabled: true, isAllProbesDisconnected: true },
1057
+ { isNoProbeEnabled: false, isAllProbesDisconnected: false },
1058
+ ]) {
1059
+ const presentation: MonitorOverviewPresentation = build(
1060
+ running(MonitorType.API, { probeFlags: flags, probes: null }),
1061
+ );
1062
+
1063
+ expect(JSON.stringify(presentation)).not.toContain(
1064
+ "No probes are attached",
1065
+ );
1066
+ expect(fact(presentation, "probes").value).toBe("Unavailable");
1067
+ expect(fact(presentation, "probes").isMuted).toBe(true);
1068
+ }
1069
+ });
1070
+
1071
+ it("not configured copy", () => {
1072
+ const noCriteria: MonitorOverviewPresentation = build(SCENARIOS[6]!.input);
1073
+
1074
+ expect(noCriteria.badge).toEqual({ text: "Not set up", tone: "neutral" });
1075
+ expect(noCriteria.headline.text).toBe("This monitor has no criteria yet");
1076
+ expect(noCriteria.callToAction).toEqual({
1077
+ text: "Set up criteria",
1078
+ linkKey: "criteria",
1079
+ });
1080
+ expect(noCriteria.lastKnownStatus).toBeUndefined();
1081
+
1082
+ const noDevice: MonitorOverviewPresentation = build(SCENARIOS[7]!.input);
1083
+
1084
+ expect(noDevice.headline.text).toBe("No network device is selected");
1085
+ expect(noDevice.explanation).toBe(
1086
+ "Choose the device this monitor alerts on.",
1087
+ );
1088
+ expect(noDevice.callToAction).toEqual({
1089
+ text: "Choose a device",
1090
+ linkKey: "criteria",
1091
+ });
1092
+ });
1093
+
1094
+ it("awaiting copy per family", () => {
1095
+ const probeCheck: MonitorOverviewPresentation = build(SCENARIOS[8]!.input);
1096
+
1097
+ expect(probeCheck.badge).toEqual({
1098
+ text: "Waiting for data",
1099
+ tone: "info",
1100
+ });
1101
+ expect(probeCheck.tone).toBe("info");
1102
+ expect(probeCheck.headline.text).toBe("Waiting for the first check");
1103
+ expect(probeCheck.explanation).toBe(
1104
+ "2 probes check this every 5 minutes. The first result usually arrives within a few minutes.",
1105
+ );
1106
+ expect(probeCheck.callToAction).toBeUndefined();
1107
+ expect(probeCheck.lastKnownStatus).toBe(
1108
+ "Last recorded status: Operational",
1109
+ );
1110
+
1111
+ expect(
1112
+ build(
1113
+ running(MonitorType.Ping, {
1114
+ createdAt: secondsAgo(60),
1115
+ monitoringInterval: "* * * * *",
1116
+ probes: probes({ ...NO_RESULTS, enabledCount: 1, attachedCount: 1 }),
1117
+ }),
1118
+ ).explanation,
1119
+ ).toBe(
1120
+ "1 probe checks this every minute. The first result usually arrives within a few minutes.",
1121
+ );
1122
+
1123
+ const heartbeat: MonitorOverviewPresentation = build(SCENARIOS[9]!.input);
1124
+
1125
+ expect(heartbeat.headline.text).toBe("Waiting for the first heartbeat");
1126
+ expect(heartbeat.explanation).toBe(
1127
+ "Send a GET or POST request to this monitor's heartbeat URL to start tracking it.",
1128
+ );
1129
+ expect(heartbeat.callToAction).toEqual({
1130
+ text: "Setup instructions",
1131
+ linkKey: "documentation",
1132
+ });
1133
+
1134
+ const email: MonitorOverviewPresentation = build(SCENARIOS[10]!.input);
1135
+
1136
+ expect(email.headline.text).toBe("Waiting for the first email");
1137
+ expect(email.explanation).toBe(
1138
+ "Send an email to this monitor's address to start tracking it.",
1139
+ );
1140
+ expect(email.callToAction?.linkKey).toBe("documentation");
1141
+
1142
+ const agent: MonitorOverviewPresentation = build(SCENARIOS[11]!.input);
1143
+
1144
+ expect(agent.headline.text).toBe("Waiting for the agent to report");
1145
+ expect(agent.explanation).toBe(
1146
+ "Install the server agent to start sending reports.",
1147
+ );
1148
+ expect(agent.callToAction?.linkKey).toBe("documentation");
1149
+
1150
+ const telemetry: MonitorOverviewPresentation = build(
1151
+ running(MonitorType.Traces, {
1152
+ monitoringInterval: "* * * * *",
1153
+ createdAt: secondsAgo(30),
1154
+ telemetry: {},
1155
+ latestEvaluationAt: undefined,
1156
+ currentStatus: undefined,
1157
+ }),
1158
+ );
1159
+
1160
+ expect(telemetry.headline.text).toBe("Waiting for the first evaluation");
1161
+ expect(telemetry.explanation).toBe(
1162
+ "This monitor is evaluated every minute once saved.",
1163
+ );
1164
+ expect(telemetry.callToAction).toBeUndefined();
1165
+ expect(telemetry.lastKnownStatus).toBeUndefined();
1166
+ });
1167
+
1168
+ it("overdue explanation uses compact durations and the interval phrase", () => {
1169
+ const probeCheck: MonitorOverviewPresentation = build(
1170
+ running(MonitorType.API, {
1171
+ probes: probes({ lastResultAt: secondsAgo(2 * 3600 + 5 * 60) }),
1172
+ }),
1173
+ );
1174
+
1175
+ expect(probeCheck.explanation).toBe(
1176
+ "No result for 2h 5m, but this monitor checks every 5 minutes. A probe may be overloaded or offline.",
1177
+ );
1178
+ expect(probeCheck.callToAction).toEqual({
1179
+ text: "Check probes",
1180
+ linkKey: "probes",
1181
+ });
1182
+ // The pulse says how late: the age less one cadence.
1183
+ expect(probeCheck.pulse.overdueSeconds).toBe(2 * 3600);
1184
+
1185
+ const telemetry: MonitorOverviewPresentation = build(SCENARIOS[15]!.input);
1186
+
1187
+ expect(telemetry.explanation).toBe(
1188
+ "No evaluation for 1h, but this monitor is evaluated every minute.",
1189
+ );
1190
+ expect(telemetry.callToAction).toBeUndefined();
1191
+ });
1192
+
1193
+ it("never-reported old monitor reads as overdue with the creation age", () => {
1194
+ const presentation: MonitorOverviewPresentation = build(
1195
+ running(MonitorType.API, {
1196
+ createdAt: secondsAgo(2 * DAY + 3 * 3600),
1197
+ statusSince: undefined,
1198
+ probes: probes(NO_RESULTS),
1199
+ }),
1200
+ );
1201
+
1202
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Overdue);
1203
+ expect(presentation.explanation).toBe(
1204
+ "No check result has arrived since this monitor was created 2d 3h ago.",
1205
+ );
1206
+ expect(presentation.tone).toBe("warning");
1207
+ expect(presentation.isNeverReported).toBe(true);
1208
+ });
1209
+
1210
+ /*
1211
+ * Nothing was ever measured, so the stored status is only the default the
1212
+ * monitor was created with: it must not be the badge, and it has not
1213
+ * "held" for 30 days.
1214
+ */
1215
+ it("never-reported monitors lead with the missing results, not the default status", () => {
1216
+ const presentation: MonitorOverviewPresentation = build(
1217
+ running(MonitorType.API, {
1218
+ createdAt: secondsAgo(30 * DAY),
1219
+ probes: probes({ ...NO_RESULTS, nextCheckAt: undefined }),
1220
+ }),
1221
+ );
1222
+
1223
+ expect(presentation.badge).toEqual({
1224
+ text: "No results yet",
1225
+ tone: "warning",
1226
+ });
1227
+ expect(presentation.headline).toEqual({
1228
+ text: "No check has completed yet",
1229
+ });
1230
+ expect(presentation.lastKnownStatus).toBe(
1231
+ "Last recorded status: Operational",
1232
+ );
1233
+ expect(presentation.secondaryBadges).toEqual([
1234
+ { text: "Checks overdue", tone: "warning" },
1235
+ ]);
1236
+
1237
+ // Danger stays danger.
1238
+ expect(
1239
+ build(
1240
+ running(MonitorType.API, {
1241
+ currentStatus: OFFLINE,
1242
+ createdAt: secondsAgo(30 * DAY),
1243
+ probes: probes({ ...NO_RESULTS, nextCheckAt: undefined }),
1244
+ }),
1245
+ ).tone,
1246
+ ).toBe("danger");
1247
+
1248
+ // Overdue with results keeps leading with the status it measured.
1249
+ const late: MonitorOverviewPresentation = build(
1250
+ running(MonitorType.API, {
1251
+ probes: probes({ lastResultAt: secondsAgo(2 * 3600) }),
1252
+ }),
1253
+ );
1254
+
1255
+ expect(late.badge.text).toBe("Operational");
1256
+ expect(late.headline.since).toEqual(secondsAgo(3 * DAY + 4 * 3600));
1257
+ expect(late.lastKnownStatus).toBeUndefined();
1258
+ expect(late.isNeverReported).toBe(false);
1259
+ });
1260
+ });
1261
+
1262
+ describe("MonitorOverviewPresentationUtil facts", () => {
1263
+ it("probe checks show the latest result, the probes and the owners", () => {
1264
+ const presentation: MonitorOverviewPresentation = build(
1265
+ running(MonitorType.API),
1266
+ );
1267
+
1268
+ expect(factKeys(presentation)).toEqual([
1269
+ "latest-result",
1270
+ "probes",
1271
+ "owners",
1272
+ ]);
1273
+ expect(fact(presentation, "latest-result")).toEqual({
1274
+ key: "latest-result",
1275
+ label: "Latest result",
1276
+ value: "Up · 120 ms · HTTP 200",
1277
+ secondary: "from London",
1278
+ tone: "good",
1279
+ });
1280
+ expect(fact(presentation, "probes")).toEqual({
1281
+ key: "probes",
1282
+ label: "Probes",
1283
+ linkKey: "probes",
1284
+ value: "2 of 2 reporting",
1285
+ secondary: undefined,
1286
+ tone: "neutral",
1287
+ });
1288
+ });
1289
+
1290
+ it("latest result per outcome and type", () => {
1291
+ const latest: (
1292
+ monitorType: MonitorType,
1293
+ result: Partial<NonNullable<MonitorOverviewProbeSummary["latestResult"]>>,
1294
+ ) => MonitorOverviewFact = (
1295
+ monitorType: MonitorType,
1296
+ result: Partial<NonNullable<MonitorOverviewProbeSummary["latestResult"]>>,
1297
+ ): MonitorOverviewFact => {
1298
+ return fact(
1299
+ build(
1300
+ running(monitorType, {
1301
+ probes: probes({
1302
+ latestResult: {
1303
+ probeId: "11111111-1111-4111-8111-111111111111",
1304
+ probeName: "London",
1305
+ monitoredAt: secondsAgo(60),
1306
+ ...result,
1307
+ },
1308
+ }),
1309
+ }),
1310
+ ),
1311
+ "latest-result",
1312
+ );
1313
+ };
1314
+
1315
+ const down: MonitorOverviewFact = latest(MonitorType.Website, {
1316
+ isOnline: false,
1317
+ responseTimeInMs: 30000.4,
1318
+ responseCode: 503,
1319
+ failureCause: "Service Unavailable",
1320
+ });
1321
+
1322
+ expect(down.value).toBe("Down · 30000 ms · HTTP 503");
1323
+ expect(down.secondary).toBe("Service Unavailable");
1324
+ expect(down.tone).toBe("danger");
1325
+
1326
+ // Only HTTP checks quote a status code.
1327
+ expect(
1328
+ latest(MonitorType.Port, {
1329
+ isOnline: true,
1330
+ responseTimeInMs: 12,
1331
+ responseCode: 200,
1332
+ }).value,
1333
+ ).toBe("Up · 12 ms");
1334
+
1335
+ expect(
1336
+ latest(MonitorType.SyntheticMonitor, {
1337
+ isOnline: true,
1338
+ responseTimeInMs: 4200,
1339
+ }).value,
1340
+ ).toBe("Passed · 4200 ms");
1341
+ expect(
1342
+ latest(MonitorType.CustomJavaScriptCode, { isOnline: false }).value,
1343
+ ).toBe("Failed");
1344
+
1345
+ const reported: MonitorOverviewFact = latest(MonitorType.DNS, {});
1346
+
1347
+ expect(reported.value).toBe("Reported");
1348
+ expect(reported.tone).toBe("neutral");
1349
+ expect(reported.secondary).toBe("from London");
1350
+ });
1351
+
1352
+ it("latest result when there is none, and when probes are unknown", () => {
1353
+ expect(
1354
+ fact(
1355
+ build(running(MonitorType.API, { probes: probes(NO_RESULTS) })),
1356
+ "latest-result",
1357
+ ),
1358
+ ).toEqual({
1359
+ key: "latest-result",
1360
+ label: "Latest result",
1361
+ value: "—",
1362
+ secondary: "No results yet",
1363
+ isMuted: true,
1364
+ });
1365
+ expect(
1366
+ fact(build(running(MonitorType.API, { probes: null })), "latest-result"),
1367
+ ).toEqual({
1368
+ key: "latest-result",
1369
+ label: "Latest result",
1370
+ value: "Unavailable",
1371
+ isMuted: true,
1372
+ });
1373
+ });
1374
+
1375
+ it("probes fact tones and secondary line", () => {
1376
+ const probesFact: (
1377
+ overrides: Partial<MonitorOverviewProbeSummary>,
1378
+ ) => MonitorOverviewFact = (
1379
+ overrides: Partial<MonitorOverviewProbeSummary>,
1380
+ ): MonitorOverviewFact => {
1381
+ return fact(
1382
+ build(running(MonitorType.API, { probes: probes(overrides) })),
1383
+ "probes",
1384
+ );
1385
+ };
1386
+
1387
+ const partly: MonitorOverviewFact = probesFact({
1388
+ attachedCount: 5,
1389
+ enabledCount: 3,
1390
+ reportingCount: 2,
1391
+ disabledCount: 2,
1392
+ disconnectedCount: 1,
1393
+ });
1394
+
1395
+ expect(partly.value).toBe("2 of 3 reporting");
1396
+ expect(partly.secondary).toBe("2 disabled · 1 disconnected");
1397
+ expect(partly.tone).toBe("warning");
1398
+
1399
+ expect(
1400
+ probesFact({ reportingCount: 0, lastResultAt: secondsAgo(60) }).tone,
1401
+ ).toBe("danger");
1402
+ expect(probesFact({ disabledCount: 1, attachedCount: 3 }).secondary).toBe(
1403
+ "1 disabled",
1404
+ );
1405
+ });
1406
+
1407
+ it("certificate and domain expiry tones at 31, 30, 8, 7, 0 and −1 days, and an invalid certificate", () => {
1408
+ const expiresIn: (seconds: number) => Date = (seconds: number): Date => {
1409
+ return new Date(NOW.getTime() + seconds * 1000);
1410
+ };
1411
+
1412
+ const cases: Array<[number, string, string]> = [
1413
+ [31 * DAY, "in 31 days", "neutral"],
1414
+ [30 * DAY, "in 30 days", "warning"],
1415
+ [8 * DAY, "in 8 days", "warning"],
1416
+ [7 * DAY, "in 7 days", "danger"],
1417
+ [DAY, "in 1 day", "danger"],
1418
+ [3600, "Expires in 1 hour", "danger"],
1419
+ [-DAY, "Expired 1 day ago", "danger"],
1420
+ [-3 * DAY, "Expired 3 days ago", "danger"],
1421
+ ];
1422
+
1423
+ for (const [seconds, value, tone] of cases) {
1424
+ const ssl: MonitorOverviewFact = fact(
1425
+ build(
1426
+ running(MonitorType.SSLCertificate, {
1427
+ probes: probes({
1428
+ latestResult: {
1429
+ probeId: "11111111-1111-4111-8111-111111111111",
1430
+ probeName: "London",
1431
+ monitoredAt: secondsAgo(60),
1432
+ isOnline: true,
1433
+ sslExpiresAt: expiresIn(seconds),
1434
+ isValidCertificate: true,
1435
+ },
1436
+ }),
1437
+ }),
1438
+ ),
1439
+ "certificate-expiry",
1440
+ );
1441
+
1442
+ expect({ seconds: seconds, value: ssl.value, tone: ssl.tone }).toEqual({
1443
+ seconds: seconds,
1444
+ value: value,
1445
+ tone: tone,
1446
+ });
1447
+ expect(ssl.label).toBe("Certificate expires");
1448
+ expect(ssl.secondary).toBe(
1449
+ OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
1450
+ expiresIn(seconds),
1451
+ true,
1452
+ ),
1453
+ );
1454
+
1455
+ const domain: MonitorOverviewFact = fact(
1456
+ build(
1457
+ running(MonitorType.Domain, {
1458
+ probes: probes({
1459
+ latestResult: {
1460
+ probeId: "11111111-1111-4111-8111-111111111111",
1461
+ probeName: "London",
1462
+ monitoredAt: secondsAgo(60),
1463
+ isOnline: true,
1464
+ domainExpiresAt: expiresIn(seconds),
1465
+ },
1466
+ }),
1467
+ }),
1468
+ ),
1469
+ "domain-expiry",
1470
+ );
1471
+
1472
+ expect({
1473
+ seconds: seconds,
1474
+ value: domain.value,
1475
+ tone: domain.tone,
1476
+ }).toEqual({ seconds: seconds, value: value, tone: tone });
1477
+ expect(domain.label).toBe("Domain expires");
1478
+ }
1479
+
1480
+ const invalid: MonitorOverviewFact =
1481
+ MonitorOverviewPresentationUtil.getExpiryFact({
1482
+ key: "certificate-expiry",
1483
+ expiresAt: expiresIn(200 * DAY),
1484
+ isValidCertificate: false,
1485
+ now: NOW,
1486
+ });
1487
+
1488
+ expect(invalid.value).toBe("Not valid");
1489
+ expect(invalid.tone).toBe("danger");
1490
+
1491
+ expect(
1492
+ MonitorOverviewPresentationUtil.getExpiryFact({
1493
+ key: "domain-expiry",
1494
+ expiresAt: undefined,
1495
+ now: NOW,
1496
+ }),
1497
+ ).toEqual({
1498
+ key: "domain-expiry",
1499
+ label: "Domain expires",
1500
+ value: "—",
1501
+ isMuted: true,
1502
+ });
1503
+
1504
+ // Facts are ordered result, expiry, probes, owners.
1505
+ expect(factKeys(build(running(MonitorType.SSLCertificate)))).toEqual([
1506
+ "latest-result",
1507
+ "certificate-expiry",
1508
+ "probes",
1509
+ "owners",
1510
+ ]);
1511
+ expect(
1512
+ fact(
1513
+ build(running(MonitorType.Domain, { probes: null })),
1514
+ "domain-expiry",
1515
+ ).value,
1516
+ ).toBe("Unavailable");
1517
+ });
1518
+
1519
+ it('heartbeat missing-window falls back to "Set in Criteria"', () => {
1520
+ const withWindow: MonitorOverviewPresentation = build(
1521
+ running(MonitorType.IncomingRequest, {
1522
+ monitorSteps: missingWindowSteps(CheckOn.IncomingRequest, 10),
1523
+ }),
1524
+ );
1525
+
1526
+ expect(factKeys(withWindow)).toEqual([
1527
+ "missing-window",
1528
+ "heartbeat-check",
1529
+ "owners",
1530
+ ]);
1531
+ expect(fact(withWindow, "missing-window")).toEqual({
1532
+ key: "missing-window",
1533
+ label: "Missing-request window",
1534
+ value: "10 minutes",
1535
+ secondary: "From this monitor's criteria",
1536
+ });
1537
+ expect(fact(withWindow, "heartbeat-check")).toEqual({
1538
+ key: "heartbeat-check",
1539
+ label: "Last missing-request check",
1540
+ value: "",
1541
+ valueDate: secondsAgo(30),
1542
+ });
1543
+
1544
+ const withoutWindow: MonitorOverviewPresentation = build(
1545
+ running(MonitorType.IncomingRequest),
1546
+ );
1547
+
1548
+ expect(fact(withoutWindow, "missing-window")).toEqual({
1549
+ key: "missing-window",
1550
+ label: "Missing-request window",
1551
+ value: "Set in Criteria",
1552
+ linkKey: "criteria",
1553
+ });
1554
+
1555
+ const email: MonitorOverviewPresentation = build(
1556
+ running(MonitorType.IncomingEmail, {
1557
+ monitorSteps: missingWindowSteps(CheckOn.EmailReceivedAt, 1),
1558
+ email: { lastReceivedAt: secondsAgo(60) },
1559
+ }),
1560
+ );
1561
+
1562
+ expect(fact(email, "missing-window").label).toBe("Missing-email window");
1563
+ expect(fact(email, "missing-window").value).toBe("1 minute");
1564
+ expect(fact(email, "heartbeat-check")).toEqual({
1565
+ key: "heartbeat-check",
1566
+ label: "Last missing-email check",
1567
+ value: "Not run yet",
1568
+ isMuted: true,
1569
+ });
1570
+ });
1571
+
1572
+ it("agent facts before and after the first report", () => {
1573
+ const before: MonitorOverviewPresentation = build(
1574
+ running(MonitorType.Server, { agent: {} }),
1575
+ );
1576
+
1577
+ expect(factKeys(before)).toEqual(["host", "cpu", "memory", "owners"]);
1578
+ expect(fact(before, "host")).toEqual({
1579
+ key: "host",
1580
+ label: "Host",
1581
+ value: "Not reported yet",
1582
+ isMuted: true,
1583
+ });
1584
+ expect(fact(before, "cpu")).toEqual({
1585
+ key: "cpu",
1586
+ label: "CPU",
1587
+ value: "—",
1588
+ isMuted: true,
1589
+ });
1590
+ expect(fact(before, "memory").value).toBe("—");
1591
+
1592
+ const after: MonitorOverviewPresentation = build(
1593
+ running(MonitorType.Server),
1594
+ );
1595
+
1596
+ expect(fact(after, "host")).toEqual({
1597
+ key: "host",
1598
+ label: "Host",
1599
+ value: "web-01.example.com",
1600
+ isMono: true,
1601
+ });
1602
+ expect(fact(after, "cpu").value).toBe("42%");
1603
+ expect(fact(after, "memory").value).toBe("64%");
1604
+ expect(after.target).toEqual({
1605
+ value: "web-01.example.com",
1606
+ isMono: true,
1607
+ extraStepCount: 0,
1608
+ });
1609
+ });
1610
+
1611
+ it("telemetry and infrastructure show the evaluation cadence and criteria", () => {
1612
+ for (const monitorType of [MonitorType.Logs, MonitorType.Kubernetes]) {
1613
+ const presentation: MonitorOverviewPresentation = build(
1614
+ running(monitorType, {
1615
+ monitoringInterval: "* * * * *",
1616
+ monitorSteps: stepsOf({}, {}),
1617
+ }),
1618
+ );
1619
+
1620
+ expect(factKeys(presentation)).toEqual([
1621
+ "evaluates",
1622
+ "criteria",
1623
+ "owners",
1624
+ ]);
1625
+ expect(fact(presentation, "evaluates").value).toBe("Every minute");
1626
+ expect(fact(presentation, "criteria")).toEqual({
1627
+ key: "criteria",
1628
+ label: "Criteria",
1629
+ value: "2 steps",
1630
+ linkKey: "criteria",
1631
+ });
1632
+ }
1633
+
1634
+ expect(fact(build(running(MonitorType.Metrics)), "criteria").value).toBe(
1635
+ "1 step",
1636
+ );
1637
+ });
1638
+
1639
+ it("network device fact links the step's networkDeviceId, not autoProvisionedNetworkDeviceId", () => {
1640
+ const presentation: MonitorOverviewPresentation = build(
1641
+ running(MonitorType.NetworkDevice, {
1642
+ monitorSteps: stepsOf(
1643
+ {
1644
+ networkDeviceMonitor: { networkDeviceId: DEVICE_ID },
1645
+ autoProvisionedNetworkDeviceId: OTHER_DEVICE_ID,
1646
+ },
1647
+ { networkDeviceMonitor: { networkDeviceId: OTHER_DEVICE_ID } },
1648
+ ),
1649
+ }),
1650
+ );
1651
+
1652
+ expect(factKeys(presentation)).toEqual(["device", "evaluated", "owners"]);
1653
+ expect(fact(presentation, "device")).toEqual({
1654
+ key: "device",
1655
+ label: "Device",
1656
+ value: "View device",
1657
+ linkKey: "networkDevice",
1658
+ linkId: DEVICE_ID,
1659
+ });
1660
+ expect(fact(presentation, "evaluated").value).toBe(
1661
+ "On every poll and matching trap",
1662
+ );
1663
+ expect(presentation.target).toBeNull();
1664
+
1665
+ const unselected: MonitorOverviewPresentation = build(
1666
+ running(MonitorType.NetworkDevice, {
1667
+ monitorSteps: stepsOf({
1668
+ networkDeviceMonitor: {},
1669
+ autoProvisionedNetworkDeviceId: OTHER_DEVICE_ID,
1670
+ }),
1671
+ }),
1672
+ );
1673
+
1674
+ expect(fact(unselected, "device")).toEqual({
1675
+ key: "device",
1676
+ label: "Device",
1677
+ value: "Not selected",
1678
+ isMuted: true,
1679
+ });
1680
+ });
1681
+
1682
+ it("manual monitors have one fact about checks", () => {
1683
+ const presentation: MonitorOverviewPresentation = build(
1684
+ running(MonitorType.Manual),
1685
+ );
1686
+
1687
+ expect(presentation.facts).toEqual([
1688
+ {
1689
+ key: "checks",
1690
+ label: "Checks",
1691
+ value: "None",
1692
+ secondary: "Status is set by hand or by incidents",
1693
+ },
1694
+ { key: "owners", label: "Owners", value: "" },
1695
+ ]);
1696
+ });
1697
+ });
1698
+
1699
+ describe("MonitorOverviewPresentationUtil pulse", () => {
1700
+ it('pulse per family, including "Last checked: unavailable" when probes are unknown', () => {
1701
+ expect(build(running(MonitorType.API)).pulse).toEqual({
1702
+ label: "Last checked",
1703
+ at: secondsAgo(60),
1704
+ emptyText: "Not checked yet",
1705
+ cadenceText: "Every 5 minutes",
1706
+ nextAt: secondsAgo(-240),
1707
+ overdueSeconds: undefined,
1708
+ isUnavailable: false,
1709
+ });
1710
+
1711
+ const unknown: MonitorOverviewPresentation = build(
1712
+ running(MonitorType.API, { probes: null }),
1713
+ );
1714
+
1715
+ expect(unknown.pulse.label).toBe("Last checked");
1716
+ expect(unknown.pulse.isUnavailable).toBe(true);
1717
+ expect(unknown.pulse.at).toBeUndefined();
1718
+
1719
+ expect(build(running(MonitorType.IncomingRequest)).pulse).toEqual({
1720
+ label: "Last request",
1721
+ at: secondsAgo(120),
1722
+ emptyText: "No request yet",
1723
+ isUnavailable: false,
1724
+ });
1725
+ expect(build(running(MonitorType.IncomingEmail)).pulse).toEqual({
1726
+ label: "Last email",
1727
+ at: secondsAgo(300),
1728
+ emptyText: "No email yet",
1729
+ isUnavailable: false,
1730
+ });
1731
+ expect(build(running(MonitorType.Server)).pulse).toEqual({
1732
+ label: "Last report",
1733
+ at: secondsAgo(45),
1734
+ emptyText: "No report yet",
1735
+ isUnavailable: false,
1736
+ });
1737
+ expect(
1738
+ build(running(MonitorType.Logs, { monitoringInterval: "* * * * *" }))
1739
+ .pulse,
1740
+ ).toEqual({
1741
+ label: "Last evaluated",
1742
+ at: secondsAgo(90),
1743
+ emptyText: "Not evaluated yet",
1744
+ cadenceText: "Every minute",
1745
+ nextAt: secondsAgo(-270),
1746
+ overdueSeconds: undefined,
1747
+ isUnavailable: false,
1748
+ });
1749
+ expect(build(running(MonitorType.NetworkDevice)).pulse).toEqual({
1750
+ label: "Last evaluated",
1751
+ at: secondsAgo(90),
1752
+ emptyText: "Not evaluated yet",
1753
+ isUnavailable: false,
1754
+ });
1755
+ expect(build(running(MonitorType.Manual)).pulse).toEqual({
1756
+ label: null,
1757
+ emptyText: "No automated checks",
1758
+ isUnavailable: false,
1759
+ });
1760
+ });
1761
+
1762
+ it("a next check in the past is not promised, and overdue carries its lateness", () => {
1763
+ const overdue: MonitorOverviewPresentation = build(
1764
+ running(MonitorType.API, {
1765
+ probes: probes({
1766
+ lastResultAt: secondsAgo(1000),
1767
+ nextCheckAt: secondsAgo(700),
1768
+ }),
1769
+ }),
1770
+ );
1771
+
1772
+ expect(overdue.pulse.nextAt).toBeUndefined();
1773
+ // The result came at 11:43:20, so the next was due at the 11:45 run.
1774
+ expect(overdue.pulse.overdueSeconds).toBe(900);
1775
+ expect(overdue.freshness).toBe(MonitorCheckFreshness.Stale);
1776
+ });
1777
+
1778
+ it("a paused or unchecked monitor promises no cadence", () => {
1779
+ for (const scenario of [SCENARIOS[1]!, SCENARIOS[4]!, SCENARIOS[6]!]) {
1780
+ const presentation: MonitorOverviewPresentation = build(scenario.input);
1781
+
1782
+ expect(presentation.pulse.cadenceText).toBeUndefined();
1783
+ expect(presentation.pulse.nextAt).toBeUndefined();
1784
+ }
1785
+
1786
+ expect(build(SCENARIOS[1]!.input).freshness).toBe(
1787
+ MonitorCheckFreshness.NotScheduled,
1788
+ );
1789
+ });
1790
+ });
1791
+
1792
+ describe("MonitorOverviewPresentationUtil sections", () => {
1793
+ it("probe checks: uptime, response time, summary and the probes card", () => {
1794
+ expect(build(running(MonitorType.API)).sections).toEqual({
1795
+ showUptime: true,
1796
+ setup: null,
1797
+ connection: null,
1798
+ summary: {
1799
+ isShown: true,
1800
+ description: "What each probe saw on its most recent check.",
1801
+ },
1802
+ sideCard: "probes",
1803
+ responseTimeMetric: MonitorMetricType.ResponseTime,
1804
+ telemetryPreview: null,
1805
+ stepCount: 1,
1806
+ });
1807
+
1808
+ expect(
1809
+ build(running(MonitorType.SyntheticMonitor)).sections.responseTimeMetric,
1810
+ ).toBe(MonitorMetricType.ExecutionTime);
1811
+ });
1812
+
1813
+ it("an awaiting probe check has no uptime and no response time yet", () => {
1814
+ const sections: MonitorOverviewPresentation["sections"] = build(
1815
+ SCENARIOS[8]!.input,
1816
+ ).sections;
1817
+
1818
+ expect(sections.showUptime).toBe(false);
1819
+ expect(sections.responseTimeMetric).toBeNull();
1820
+ expect(sections.summary.isShown).toBe(true);
1821
+ expect(sections.sideCard).toBe("probes");
1822
+ });
1823
+
1824
+ it("push types show setup while awaiting and the connection card after", () => {
1825
+ const awaiting: MonitorOverviewPresentation["sections"] = build(
1826
+ SCENARIOS[9]!.input,
1827
+ ).sections;
1828
+
1829
+ expect(awaiting.setup).toBe(MonitorOverviewSetupKind.HeartbeatUrl);
1830
+ expect(awaiting.connection).toBeNull();
1831
+ expect(awaiting.sideCard).toBeNull();
1832
+ expect(awaiting.summary.isShown).toBe(false);
1833
+
1834
+ const after: MonitorOverviewPresentation["sections"] = build(
1835
+ running(MonitorType.IncomingRequest),
1836
+ ).sections;
1837
+
1838
+ expect(after.setup).toBeNull();
1839
+ expect(after.connection).toBe(MonitorOverviewSetupKind.HeartbeatUrl);
1840
+ expect(after.sideCard).toBe("connection");
1841
+ expect(after.summary).toEqual({
1842
+ isShown: true,
1843
+ description: "The most recent request and how the criteria judged it.",
1844
+ });
1845
+
1846
+ expect(build(SCENARIOS[10]!.input).sections.setup).toBe(
1847
+ MonitorOverviewSetupKind.InboundEmail,
1848
+ );
1849
+ expect(build(SCENARIOS[11]!.input).sections.setup).toBe(
1850
+ MonitorOverviewSetupKind.ServerAgent,
1851
+ );
1852
+ expect(build(running(MonitorType.Server)).sections.connection).toBe(
1853
+ MonitorOverviewSetupKind.ServerAgent,
1854
+ );
1855
+ });
1856
+
1857
+ it("previews need a step", () => {
1858
+ expect(build(running(MonitorType.Logs)).sections.telemetryPreview).toBe(
1859
+ "Logs",
1860
+ );
1861
+ expect(
1862
+ build(running(MonitorType.Logs, { monitorSteps: NO_STEPS })).sections
1863
+ .telemetryPreview,
1864
+ ).toBeNull();
1865
+ expect(
1866
+ build(running(MonitorType.Exceptions)).sections.telemetryPreview,
1867
+ ).toBeNull();
1868
+ });
1869
+
1870
+ it("manual monitors have no summary and the manual guide", () => {
1871
+ const sections: MonitorOverviewPresentation["sections"] = build(
1872
+ running(MonitorType.Manual),
1873
+ ).sections;
1874
+
1875
+ expect(sections.summary).toEqual({ isShown: false, description: "" });
1876
+ expect(sections.sideCard).toBe("manual");
1877
+ expect(sections.showUptime).toBe(true);
1878
+ });
1879
+
1880
+ it("the target is the redacted first step", () => {
1881
+ expect(
1882
+ build(
1883
+ running(MonitorType.API, {
1884
+ monitorSteps: stepsOf(
1885
+ {
1886
+ requestType: "GET",
1887
+ monitorDestination: "https://ops:pw@api.example.com/health?k=1",
1888
+ },
1889
+ {},
1890
+ ),
1891
+ }),
1892
+ ).target,
1893
+ ).toEqual({
1894
+ value: "GET https://api.example.com/health",
1895
+ isMono: true,
1896
+ extraStepCount: 1,
1897
+ });
1898
+ });
1899
+ });
1900
+
1901
+ /*
1902
+ * Probe rows as the server stores them, summarised the way the page does,
1903
+ * so these tests cover the whole path from MonitorProbe to the hero.
1904
+ */
1905
+ const probeRow: (data: {
1906
+ probeId: string;
1907
+ name: string;
1908
+ resultAgeSeconds?: number;
1909
+ isOnline?: boolean;
1910
+ nextPingInSeconds?: number;
1911
+ connectionStatus?: ProbeConnectionStatus;
1912
+ isEnabled?: boolean;
1913
+ }) => MonitorProbe = (data: {
1914
+ probeId: string;
1915
+ name: string;
1916
+ resultAgeSeconds?: number;
1917
+ isOnline?: boolean;
1918
+ nextPingInSeconds?: number;
1919
+ connectionStatus?: ProbeConnectionStatus;
1920
+ isEnabled?: boolean;
1921
+ }): MonitorProbe => {
1922
+ const row: MonitorProbe = new MonitorProbe();
1923
+ const probe: Probe = new Probe();
1924
+
1925
+ probe._id = data.probeId;
1926
+ probe.name = data.name;
1927
+ probe.connectionStatus =
1928
+ data.connectionStatus || ProbeConnectionStatus.Connected;
1929
+
1930
+ row.probeId = new ObjectID(data.probeId);
1931
+ row.probe = probe;
1932
+ row.isEnabled = data.isEnabled ?? true;
1933
+ row.nextPingAt = secondsAgo(-(data.nextPingInSeconds ?? 60));
1934
+
1935
+ if (data.resultAgeSeconds !== undefined) {
1936
+ row.lastPingAt = secondsAgo(data.resultAgeSeconds + 2);
1937
+ row.lastMonitoringLog = {
1938
+ [STEP_ID]: {
1939
+ monitoredAt: secondsAgo(data.resultAgeSeconds).toISOString(),
1940
+ isOnline: data.isOnline ?? true,
1941
+ responseTimeInMs: 120,
1942
+ },
1943
+ } as unknown as MonitorStepProbeResponse;
1944
+ }
1945
+
1946
+ return row;
1947
+ };
1948
+
1949
+ const summaryOf: (
1950
+ rows: Array<MonitorProbe>,
1951
+ isScheduled?: boolean,
1952
+ ) => MonitorOverviewProbeSummary = (
1953
+ rows: Array<MonitorProbe>,
1954
+ isScheduled?: boolean,
1955
+ ): MonitorOverviewProbeSummary => {
1956
+ const monitorSteps: MonitorSteps | undefined = running(
1957
+ MonitorType.API,
1958
+ ).monitorSteps;
1959
+
1960
+ return MonitorOverviewProbeUtil.summarizeProbes({
1961
+ monitorProbes: rows,
1962
+ validStepIds: MonitorOverviewProbeUtil.getValidStepIds(monitorSteps),
1963
+ primaryStepId: MonitorOverviewProbeUtil.getPrimaryStepId(monitorSteps),
1964
+ cadenceSeconds: 300,
1965
+ now: NOW,
1966
+ monitoringInterval: "*/5 * * * *",
1967
+ isScheduled: isScheduled,
1968
+ });
1969
+ };
1970
+
1971
+ const FRANKFURT: string = "11111111-1111-4111-8111-111111111111";
1972
+ const VIRGINIA: string = "22222222-2222-4222-8222-222222222222";
1973
+ const SINGAPORE: string = "33333333-3333-4333-8333-333333333333";
1974
+
1975
+ describe("MonitorOverviewPresentationUtil probes that stop checking", () => {
1976
+ /*
1977
+ * Only a probe's own claim moves its nextPingAt, so a probe that lost its
1978
+ * connection keeps a due time that sinks further into the past. The
1979
+ * monitor is still checked on time by the others.
1980
+ */
1981
+ it("one disconnected probe does not make a monitor others check on time overdue", () => {
1982
+ const presentation: MonitorOverviewPresentation = build(
1983
+ running(MonitorType.Website, {
1984
+ probes: summaryOf([
1985
+ probeRow({
1986
+ probeId: FRANKFURT,
1987
+ name: "Frankfurt",
1988
+ resultAgeSeconds: 60,
1989
+ nextPingInSeconds: 240,
1990
+ }),
1991
+ probeRow({
1992
+ probeId: VIRGINIA,
1993
+ name: "Virginia",
1994
+ resultAgeSeconds: 30,
1995
+ nextPingInSeconds: 270,
1996
+ }),
1997
+ probeRow({
1998
+ probeId: SINGAPORE,
1999
+ name: "Singapore",
2000
+ resultAgeSeconds: 3 * DAY,
2001
+ nextPingInSeconds: -3 * DAY,
2002
+ connectionStatus: ProbeConnectionStatus.Disconnected,
2003
+ }),
2004
+ ]),
2005
+ }),
2006
+ );
2007
+
2008
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Running);
2009
+ expect(presentation.freshness).toBe(MonitorCheckFreshness.Fresh);
2010
+ expect(presentation.secondaryBadges).toEqual([]);
2011
+ expect(presentation.pulse.nextAt).toEqual(secondsAgo(-240));
2012
+ expect(presentation.pulse.overdueSeconds).toBeUndefined();
2013
+
2014
+ // The lost probe is still reported where probes are reported.
2015
+ expect(fact(presentation, "probes")).toMatchObject({
2016
+ value: "2 of 3 reporting",
2017
+ secondary: "1 disconnected",
2018
+ tone: "warning",
2019
+ });
2020
+ });
2021
+
2022
+ it("one probe that stopped claiming does not either", () => {
2023
+ const presentation: MonitorOverviewPresentation = build(
2024
+ running(MonitorType.API, {
2025
+ probes: summaryOf([
2026
+ probeRow({
2027
+ probeId: FRANKFURT,
2028
+ name: "Frankfurt",
2029
+ resultAgeSeconds: 60,
2030
+ nextPingInSeconds: 240,
2031
+ }),
2032
+ probeRow({
2033
+ probeId: VIRGINIA,
2034
+ name: "Virginia",
2035
+ resultAgeSeconds: 2 * 3600,
2036
+ nextPingInSeconds: -2 * 3600,
2037
+ }),
2038
+ ]),
2039
+ }),
2040
+ );
2041
+
2042
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Running);
2043
+ expect(presentation.pulse.nextAt).toEqual(secondsAgo(-240));
2044
+ });
2045
+
2046
+ it("is overdue when every probe that is checking is past due", () => {
2047
+ const presentation: MonitorOverviewPresentation = build(
2048
+ running(MonitorType.API, {
2049
+ probes: summaryOf([
2050
+ probeRow({
2051
+ probeId: FRANKFURT,
2052
+ name: "Frankfurt",
2053
+ resultAgeSeconds: 100,
2054
+ nextPingInSeconds: -1200,
2055
+ }),
2056
+ probeRow({
2057
+ probeId: VIRGINIA,
2058
+ name: "Virginia",
2059
+ resultAgeSeconds: 100,
2060
+ nextPingInSeconds: -1100,
2061
+ }),
2062
+ ]),
2063
+ }),
2064
+ );
2065
+
2066
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Overdue);
2067
+ // Past due since the later of the two.
2068
+ expect(presentation.pulse.overdueSeconds).toBe(1100);
2069
+ });
2070
+ });
2071
+
2072
+ describe("MonitorOverviewPresentationUtil paused probes", () => {
2073
+ const MAINTENANCE: MonitorOverviewPresentationInput["pause"] = {
2074
+ isDisabled: false,
2075
+ byManualIncident: false,
2076
+ byScheduledMaintenance: true,
2077
+ };
2078
+
2079
+ it("a paused monitor's probes are neither late nor a fault", () => {
2080
+ const summary: MonitorOverviewProbeSummary = summaryOf(
2081
+ [
2082
+ probeRow({
2083
+ probeId: FRANKFURT,
2084
+ name: "Frankfurt",
2085
+ resultAgeSeconds: 25 * 60,
2086
+ }),
2087
+ probeRow({
2088
+ probeId: VIRGINIA,
2089
+ name: "Virginia",
2090
+ resultAgeSeconds: 25 * 60,
2091
+ isOnline: false,
2092
+ }),
2093
+ ],
2094
+ false,
2095
+ );
2096
+
2097
+ expect(
2098
+ summary.rows.map((row: MonitorOverviewProbeRow) => {
2099
+ return row.health;
2100
+ }),
2101
+ ).toEqual([MonitorOverviewProbeHealth.Down, MonitorOverviewProbeHealth.Up]);
2102
+
2103
+ const presentation: MonitorOverviewPresentation = build(
2104
+ running(MonitorType.API, { pause: MAINTENANCE, probes: summary }),
2105
+ );
2106
+
2107
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Paused);
2108
+ expect(fact(presentation, "probes")).toEqual({
2109
+ key: "probes",
2110
+ label: "Probes",
2111
+ linkKey: "probes",
2112
+ value: "2 enabled",
2113
+ secondary: "Checks paused",
2114
+ tone: "neutral",
2115
+ });
2116
+ });
2117
+
2118
+ it("keeps saying which probes are switched off or disconnected", () => {
2119
+ const presentation: MonitorOverviewPresentation = build(
2120
+ running(MonitorType.API, {
2121
+ pause: {
2122
+ isDisabled: true,
2123
+ byManualIncident: false,
2124
+ byScheduledMaintenance: false,
2125
+ },
2126
+ probes: probes({
2127
+ attachedCount: 4,
2128
+ enabledCount: 3,
2129
+ reportingCount: 0,
2130
+ disabledCount: 1,
2131
+ disconnectedCount: 1,
2132
+ }),
2133
+ }),
2134
+ );
2135
+
2136
+ expect(fact(presentation, "probes")).toMatchObject({
2137
+ value: "3 enabled",
2138
+ secondary: "Checks paused · 1 disabled · 1 disconnected",
2139
+ tone: "neutral",
2140
+ });
2141
+
2142
+ // No probe enabled is still a problem when monitoring resumes.
2143
+ expect(
2144
+ fact(
2145
+ build(
2146
+ running(MonitorType.API, {
2147
+ pause: MAINTENANCE,
2148
+ probes: probes({
2149
+ ...NO_RESULTS,
2150
+ enabledCount: 0,
2151
+ disabledCount: 2,
2152
+ }),
2153
+ }),
2154
+ ),
2155
+ "probes",
2156
+ ),
2157
+ ).toMatchObject({ value: "None enabled", tone: "danger" });
2158
+ });
2159
+ });
2160
+
2161
+ describe("MonitorOverviewPresentationUtil probes that disagree", () => {
2162
+ it("down probes are never neutral in the probes fact", () => {
2163
+ const probesFact: (
2164
+ overrides: Partial<MonitorOverviewProbeSummary>,
2165
+ monitorType?: MonitorType,
2166
+ ) => MonitorOverviewFact = (
2167
+ overrides: Partial<MonitorOverviewProbeSummary>,
2168
+ monitorType?: MonitorType,
2169
+ ): MonitorOverviewFact => {
2170
+ return fact(
2171
+ build(
2172
+ running(monitorType || MonitorType.API, {
2173
+ probes: probes(overrides),
2174
+ }),
2175
+ ),
2176
+ "probes",
2177
+ );
2178
+ };
2179
+
2180
+ expect(
2181
+ probesFact({
2182
+ attachedCount: 3,
2183
+ enabledCount: 3,
2184
+ reportingCount: 3,
2185
+ upCount: 1,
2186
+ downCount: 2,
2187
+ }),
2188
+ ).toMatchObject({
2189
+ value: "3 of 3 reporting",
2190
+ secondary: "2 down",
2191
+ tone: "warning",
2192
+ });
2193
+
2194
+ // Every reporting probe sees it down.
2195
+ expect(
2196
+ probesFact({
2197
+ attachedCount: 3,
2198
+ enabledCount: 3,
2199
+ reportingCount: 2,
2200
+ upCount: 0,
2201
+ downCount: 2,
2202
+ disconnectedCount: 1,
2203
+ }),
2204
+ ).toMatchObject({
2205
+ secondary: "2 down · 1 disconnected",
2206
+ tone: "danger",
2207
+ });
2208
+
2209
+ expect(
2210
+ probesFact(
2211
+ { reportingCount: 2, upCount: 1, downCount: 1 },
2212
+ MonitorType.SyntheticMonitor,
2213
+ ).secondary,
2214
+ ).toBe("1 failing");
2215
+ });
2216
+
2217
+ it("the latest result says the probes disagree instead of quoting whichever reported last", () => {
2218
+ const latest: (
2219
+ monitorType: MonitorType,
2220
+ overrides: Partial<MonitorOverviewProbeSummary>,
2221
+ ) => MonitorOverviewFact = (
2222
+ monitorType: MonitorType,
2223
+ overrides: Partial<MonitorOverviewProbeSummary>,
2224
+ ): MonitorOverviewFact => {
2225
+ return fact(
2226
+ build(running(monitorType, { probes: probes(overrides) })),
2227
+ "latest-result",
2228
+ );
2229
+ };
2230
+
2231
+ expect(
2232
+ latest(MonitorType.API, {
2233
+ attachedCount: 3,
2234
+ enabledCount: 3,
2235
+ reportingCount: 3,
2236
+ upCount: 1,
2237
+ downCount: 2,
2238
+ }),
2239
+ ).toEqual({
2240
+ key: "latest-result",
2241
+ label: "Latest result",
2242
+ value: "Mixed · 1 up, 2 down",
2243
+ tone: "warning",
2244
+ });
2245
+ expect(
2246
+ latest(MonitorType.SyntheticMonitor, { upCount: 1, downCount: 1 }).value,
2247
+ ).toBe("Mixed · 1 passed, 1 failed");
2248
+
2249
+ // Agreement keeps the newest result.
2250
+ expect(latest(MonitorType.API, { upCount: 2, downCount: 0 }).value).toBe(
2251
+ "Up · 120 ms · HTTP 200",
2252
+ );
2253
+ });
2254
+
2255
+ it("from real probe rows", () => {
2256
+ const presentation: MonitorOverviewPresentation = build(
2257
+ running(MonitorType.API, {
2258
+ probes: summaryOf([
2259
+ probeRow({
2260
+ probeId: FRANKFURT,
2261
+ name: "Frankfurt",
2262
+ resultAgeSeconds: 20,
2263
+ }),
2264
+ probeRow({
2265
+ probeId: VIRGINIA,
2266
+ name: "Virginia",
2267
+ resultAgeSeconds: 40,
2268
+ isOnline: false,
2269
+ }),
2270
+ probeRow({
2271
+ probeId: SINGAPORE,
2272
+ name: "Singapore",
2273
+ resultAgeSeconds: 50,
2274
+ isOnline: false,
2275
+ }),
2276
+ ]),
2277
+ }),
2278
+ );
2279
+
2280
+ expect(fact(presentation, "latest-result").value).toBe(
2281
+ "Mixed · 1 up, 2 down",
2282
+ );
2283
+ expect(fact(presentation, "probes")).toMatchObject({
2284
+ value: "3 of 3 reporting",
2285
+ secondary: "2 down",
2286
+ tone: "warning",
2287
+ });
2288
+ });
2289
+ });
2290
+
2291
+ /*
2292
+ * The worker stamps telemetryMonitorLastMonitorAt when it QUEUES an
2293
+ * evaluation. Only the evaluation log says one ran.
2294
+ */
2295
+ describe("MonitorOverviewPresentationUtil telemetry evaluations", () => {
2296
+ it("a stuck evaluator is overdue even while the scheduler keeps stamping", () => {
2297
+ const presentation: MonitorOverviewPresentation = build(
2298
+ running(MonitorType.Logs, {
2299
+ monitoringInterval: "* * * * *",
2300
+ telemetry: {
2301
+ lastScheduledAt: secondsAgo(5),
2302
+ nextEvaluationAt: secondsAgo(-55),
2303
+ },
2304
+ latestEvaluationAt: secondsAgo(20 * 60),
2305
+ evaluationStatus: "loaded",
2306
+ }),
2307
+ );
2308
+
2309
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Overdue);
2310
+ expect(presentation.pulse).toMatchObject({
2311
+ label: "Last evaluated",
2312
+ at: secondsAgo(20 * 60),
2313
+ });
2314
+ expect(presentation.explanation).toBe(
2315
+ "No evaluation for 20m, but this monitor is evaluated every minute.",
2316
+ );
2317
+ });
2318
+
2319
+ it("without the evaluation log, the scheduler's stamp is called what it is", () => {
2320
+ const statuses: Array<"loading" | "error" | "forbidden"> = [
2321
+ "loading",
2322
+ "error",
2323
+ "forbidden",
2324
+ ];
2325
+
2326
+ for (const evaluationStatus of statuses) {
2327
+ const presentation: MonitorOverviewPresentation = build(
2328
+ running(MonitorType.Kubernetes, {
2329
+ telemetry: {
2330
+ lastScheduledAt: secondsAgo(30),
2331
+ nextEvaluationAt: secondsAgo(-270),
2332
+ },
2333
+ latestEvaluationAt: undefined,
2334
+ evaluationStatus: evaluationStatus,
2335
+ }),
2336
+ );
2337
+
2338
+ expect({
2339
+ evaluationStatus: evaluationStatus,
2340
+ runState: presentation.runState,
2341
+ label: presentation.pulse.label,
2342
+ at: presentation.pulse.at,
2343
+ }).toEqual({
2344
+ evaluationStatus: evaluationStatus,
2345
+ runState: MonitorOverviewRunState.Running,
2346
+ label: "Last scheduled",
2347
+ at: secondsAgo(30),
2348
+ });
2349
+ }
2350
+
2351
+ // The old name for the stamp is still read.
2352
+ expect(
2353
+ build(
2354
+ running(MonitorType.Logs, {
2355
+ telemetry: { lastEvaluatedAt: secondsAgo(40) },
2356
+ latestEvaluationAt: undefined,
2357
+ evaluationStatus: undefined,
2358
+ }),
2359
+ ).pulse,
2360
+ ).toMatchObject({ label: "Last scheduled", at: secondsAgo(40) });
2361
+ });
2362
+
2363
+ /*
2364
+ * The log still holds every evaluation of a monitor younger than a day,
2365
+ * so its emptiness means none has completed: the worker's stamp, which it
2366
+ * writes when it only queues one, must not end the wait.
2367
+ */
2368
+ it("never evaluated: waiting while young, overdue after", () => {
2369
+ const young: MonitorOverviewPresentation = build(
2370
+ running(MonitorType.Metrics, {
2371
+ createdAt: secondsAgo(120),
2372
+ telemetry: {
2373
+ lastScheduledAt: secondsAgo(30),
2374
+ nextEvaluationAt: secondsAgo(-270),
2375
+ },
2376
+ latestEvaluationAt: undefined,
2377
+ }),
2378
+ );
2379
+
2380
+ expect(young.runState).toBe(MonitorOverviewRunState.AwaitingFirstData);
2381
+ expect(young.pulse).toMatchObject({
2382
+ label: "Last evaluated",
2383
+ at: undefined,
2384
+ emptyText: "Not evaluated yet",
2385
+ });
2386
+
2387
+ const stuck: MonitorOverviewPresentation = build(
2388
+ running(MonitorType.Metrics, {
2389
+ createdAt: secondsAgo(5 * 3600),
2390
+ latestEvaluationAt: undefined,
2391
+ }),
2392
+ );
2393
+
2394
+ expect(stuck.runState).toBe(MonitorOverviewRunState.Overdue);
2395
+ expect(stuck.isNeverReported).toBe(true);
2396
+ expect(stuck.badge).toEqual({ text: "No results yet", tone: "warning" });
2397
+ expect(stuck.headline).toEqual({ text: "No evaluation has completed yet" });
2398
+ expect(stuck.explanation).toBe(
2399
+ "No evaluation has run since this monitor was created 5h ago.",
2400
+ );
2401
+
2402
+ // The worker stamps every evaluation it queues, so none was ever queued.
2403
+ const neverQueued: MonitorOverviewPresentation = build(
2404
+ running(MonitorType.Metrics, {
2405
+ createdAt: secondsAgo(10 * DAY),
2406
+ telemetry: {},
2407
+ latestEvaluationAt: undefined,
2408
+ }),
2409
+ );
2410
+
2411
+ expect(neverQueued.runState).toBe(MonitorOverviewRunState.Overdue);
2412
+ expect(neverQueued.isNeverReported).toBe(true);
2413
+ expect(neverQueued.explanation).toBe(
2414
+ "No evaluation has run since this monitor was created 10d ago.",
2415
+ );
2416
+ });
2417
+ });
2418
+
2419
+ /*
2420
+ * MonitorLog keeps a row for a day (MonitorLogUtil.DEFAULT_RETENTION_DAYS)
2421
+ * and drops it with its daily partition, so a row is gone within about two
2422
+ * days. For a monitor older than that, an empty log is not "never
2423
+ * evaluated".
2424
+ */
2425
+ describe("MonitorOverviewPresentationUtil evaluation log retention", () => {
2426
+ it("an empty log is judged against the day every row is guaranteed to survive", () => {
2427
+ expect(MONITOR_LOG_MINIMUM_RETENTION_SECONDS).toBe(DAY);
2428
+ });
2429
+
2430
+ it("an old monitor evaluated every minute with nothing in the log's last day is overdue, not 'never evaluated'", () => {
2431
+ const presentation: MonitorOverviewPresentation = build(
2432
+ running(MonitorType.Metrics, {
2433
+ monitoringInterval: "* * * * *",
2434
+ createdAt: secondsAgo(90 * DAY),
2435
+ // The worker keeps queueing; nothing completes.
2436
+ telemetry: {
2437
+ lastScheduledAt: secondsAgo(5),
2438
+ nextEvaluationAt: secondsAgo(-55),
2439
+ },
2440
+ latestEvaluationAt: undefined,
2441
+ evaluationStatus: "loaded",
2442
+ }),
2443
+ );
2444
+
2445
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Overdue);
2446
+ expect(presentation.isNeverReported).toBe(false);
2447
+ // What it last measured leads, as for any overdue monitor with results.
2448
+ expect(presentation.badge.text).toBe("Operational");
2449
+ expect(presentation.headline).toEqual({
2450
+ text: "Operational",
2451
+ since: secondsAgo(3 * DAY + 4 * 3600),
2452
+ });
2453
+ expect(presentation.secondaryBadges).toEqual([
2454
+ { text: "Checks overdue", tone: "warning" },
2455
+ ]);
2456
+ expect(presentation.tone).toBe("warning");
2457
+ expect(presentation.explanation).toBe(
2458
+ "No evaluation recorded in the last day, but this monitor is evaluated every minute.",
2459
+ );
2460
+ expect(presentation.lastKnownStatus).toBeUndefined();
2461
+ expect(MonitorOverviewPresentationUtil.getUptimeCaveat(presentation)).toBe(
2462
+ null,
2463
+ );
2464
+ expect(presentation.sections.showUptime).toBe(true);
2465
+ // How late is unknown: the log only says "not in the last day".
2466
+ expect(presentation.pulse).toEqual({
2467
+ label: "Last evaluated",
2468
+ at: undefined,
2469
+ emptyText: "Not evaluated in the last day",
2470
+ cadenceText: "Every minute",
2471
+ nextAt: secondsAgo(-55),
2472
+ overdueSeconds: undefined,
2473
+ isUnavailable: false,
2474
+ });
2475
+ });
2476
+
2477
+ it("a weekly monitor between evaluations shows its last scheduled run, not an overdue or a wait", () => {
2478
+ const presentation: MonitorOverviewPresentation = build(
2479
+ running(MonitorType.Kubernetes, {
2480
+ // Fridays at noon; now is Monday noon.
2481
+ monitoringInterval: "0 12 * * 5",
2482
+ createdAt: secondsAgo(90 * DAY),
2483
+ telemetry: {
2484
+ lastScheduledAt: secondsAgo(3 * DAY),
2485
+ nextEvaluationAt: secondsAgo(-4 * DAY),
2486
+ },
2487
+ latestEvaluationAt: undefined,
2488
+ evaluationStatus: "loaded",
2489
+ }),
2490
+ );
2491
+
2492
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Running);
2493
+ expect(presentation.isNeverReported).toBe(false);
2494
+ expect(presentation.badge.text).toBe("Operational");
2495
+ expect(presentation.sections.showUptime).toBe(true);
2496
+ expect(presentation.pulse).toMatchObject({
2497
+ label: "Last scheduled",
2498
+ at: secondsAgo(3 * DAY),
2499
+ nextAt: secondsAgo(-4 * DAY),
2500
+ });
2501
+ });
2502
+
2503
+ it("a weekly monitor a day or two old, whose first evaluation has left the log, is not still waiting", () => {
2504
+ // Saturdays at 13:00; created just before Saturday's run, 47 hours ago.
2505
+ const presentation: MonitorOverviewPresentation = build(
2506
+ running(MonitorType.Logs, {
2507
+ monitoringInterval: "0 13 * * 6",
2508
+ createdAt: secondsAgo(47 * 3600 + 300),
2509
+ telemetry: {
2510
+ lastScheduledAt: secondsAgo(47 * 3600),
2511
+ nextEvaluationAt: secondsAgo(-(5 * DAY + 3600)),
2512
+ },
2513
+ latestEvaluationAt: undefined,
2514
+ evaluationStatus: "loaded",
2515
+ }),
2516
+ );
2517
+
2518
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Running);
2519
+ expect(presentation.pulse).toMatchObject({
2520
+ label: "Last scheduled",
2521
+ at: secondsAgo(47 * 3600),
2522
+ });
2523
+ });
2524
+
2525
+ it("a schedule with gaps is not overdue through the weekend, and is once Monday's runs are missing", () => {
2526
+ // Every 5 minutes, 09:00 to 17:55 UTC, Monday to Friday.
2527
+ const businessHours: (now: Date) => MonitorOverviewPresentation = (
2528
+ now: Date,
2529
+ ): MonitorOverviewPresentation => {
2530
+ return build(
2531
+ running(MonitorType.Logs, {
2532
+ now: now,
2533
+ monitoringInterval: "*/5 9-17 * * 1-5",
2534
+ createdAt: secondsAgo(90 * DAY),
2535
+ telemetry: {
2536
+ // Friday's last run; the worker queues nothing until Monday.
2537
+ lastScheduledAt: new Date("2026-09-18T17:55:00.000Z"),
2538
+ nextEvaluationAt: new Date("2026-09-21T09:00:00.000Z"),
2539
+ },
2540
+ latestEvaluationAt: undefined,
2541
+ evaluationStatus: "loaded",
2542
+ }),
2543
+ );
2544
+ };
2545
+
2546
+ /*
2547
+ * Friday's rows expire through the weekend. An empty log then proves
2548
+ * only that nothing ran in the last day, which is true of a weekend, so
2549
+ * it is judged from a day ago and not from the two days a row can
2550
+ * outlive its retention.
2551
+ */
2552
+ for (const weekend of [
2553
+ "2026-09-19T20:00:00.000Z",
2554
+ "2026-09-20T12:00:00.000Z",
2555
+ "2026-09-20T23:00:00.000Z",
2556
+ ]) {
2557
+ const presentation: MonitorOverviewPresentation = businessHours(
2558
+ new Date(weekend),
2559
+ );
2560
+
2561
+ expect({ weekend, runState: presentation.runState }).toEqual({
2562
+ weekend,
2563
+ runState: MonitorOverviewRunState.Running,
2564
+ });
2565
+ expect(presentation.pulse.label).toBe("Last scheduled");
2566
+ }
2567
+
2568
+ const mondayMorning: MonitorOverviewPresentation = businessHours(
2569
+ new Date("2026-09-21T08:00:00.000Z"),
2570
+ );
2571
+
2572
+ expect(mondayMorning.runState).toBe(MonitorOverviewRunState.Running);
2573
+ expect(mondayMorning.pulse.label).toBe("Last scheduled");
2574
+
2575
+ const mondayTen: MonitorOverviewPresentation = businessHours(
2576
+ new Date("2026-09-21T10:00:00.000Z"),
2577
+ );
2578
+
2579
+ expect(mondayTen.runState).toBe(MonitorOverviewRunState.Overdue);
2580
+ expect(mondayTen.isNeverReported).toBe(false);
2581
+ });
2582
+
2583
+ it("a monitor a day or two old with nothing in the log's last day is not called 'never evaluated'", () => {
2584
+ const presentation: MonitorOverviewPresentation = build(
2585
+ running(MonitorType.Metrics, {
2586
+ monitoringInterval: "* * * * *",
2587
+ createdAt: secondsAgo(36 * 3600),
2588
+ latestEvaluationAt: undefined,
2589
+ }),
2590
+ );
2591
+
2592
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Overdue);
2593
+ expect(presentation.isNeverReported).toBe(false);
2594
+ expect(presentation.explanation).toBe(
2595
+ "No evaluation recorded in the last day, but this monitor is evaluated every minute.",
2596
+ );
2597
+ });
2598
+
2599
+ it("an impossible schedule is judged as the every-minute one it gets, without CronTab's search", () => {
2600
+ // The 30th of February: CronTab takes over a second to find no run.
2601
+ const search: MockInstance<typeof CronTab.getNextExecutionTimes> =
2602
+ jest.spyOn(CronTab, "getNextExecutionTimes");
2603
+
2604
+ try {
2605
+ const probeCheck: MonitorOverviewPresentation = build(
2606
+ running(MonitorType.API, { monitoringInterval: "0 0 30 2 *" }),
2607
+ );
2608
+ const telemetry: MonitorOverviewPresentation = build(
2609
+ running(MonitorType.Logs, {
2610
+ monitoringInterval: "0 0 30 2 *",
2611
+ createdAt: secondsAgo(90 * DAY),
2612
+ latestEvaluationAt: undefined,
2613
+ }),
2614
+ );
2615
+
2616
+ expect(probeCheck.runState).toBe(MonitorOverviewRunState.Running);
2617
+ expect(probeCheck.pulse.cadenceText).toBe("Every minute");
2618
+ // Nothing in the last day, on a schedule the scheduler runs every minute.
2619
+ expect(telemetry.runState).toBe(MonitorOverviewRunState.Overdue);
2620
+ expect(telemetry.explanation).toBe(
2621
+ "No evaluation recorded in the last day, but this monitor is evaluated every minute.",
2622
+ );
2623
+ expect(search).not.toHaveBeenCalled();
2624
+ } finally {
2625
+ search.mockRestore();
2626
+ }
2627
+ });
2628
+
2629
+ it("paused, it names the last scheduled run instead of 'not evaluated yet'", () => {
2630
+ const presentation: MonitorOverviewPresentation = build(
2631
+ running(MonitorType.Logs, {
2632
+ createdAt: secondsAgo(90 * DAY),
2633
+ pause: {
2634
+ isDisabled: true,
2635
+ byManualIncident: false,
2636
+ byScheduledMaintenance: false,
2637
+ },
2638
+ telemetry: { lastScheduledAt: secondsAgo(5 * DAY) },
2639
+ latestEvaluationAt: undefined,
2640
+ }),
2641
+ );
2642
+
2643
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Paused);
2644
+ expect(presentation.pulse).toMatchObject({
2645
+ label: "Last scheduled",
2646
+ at: secondsAgo(5 * DAY),
2647
+ });
2648
+ });
2649
+ });
2650
+
2651
+ describe("MonitorOverviewPresentationUtil network device evaluations", () => {
2652
+ const pulseFor: (
2653
+ overrides: Partial<MonitorOverviewPresentationInput>,
2654
+ ) => MonitorOverviewPresentation["pulse"] = (
2655
+ overrides: Partial<MonitorOverviewPresentationInput>,
2656
+ ): MonitorOverviewPresentation["pulse"] => {
2657
+ return build(running(MonitorType.NetworkDevice, overrides)).pulse;
2658
+ };
2659
+
2660
+ it("an unreadable log is unavailable, never 'not evaluated yet'", () => {
2661
+ const statuses: Array<"error" | "forbidden"> = ["error", "forbidden"];
2662
+
2663
+ for (const evaluationStatus of statuses) {
2664
+ expect(
2665
+ pulseFor({
2666
+ latestEvaluationAt: undefined,
2667
+ evaluationStatus: evaluationStatus,
2668
+ }),
2669
+ ).toEqual({
2670
+ label: "Last evaluated",
2671
+ at: undefined,
2672
+ emptyText: "Not evaluated yet",
2673
+ isUnavailable: true,
2674
+ });
2675
+ }
2676
+ });
2677
+
2678
+ it("a log still loading says so", () => {
2679
+ expect(
2680
+ pulseFor({ latestEvaluationAt: undefined, evaluationStatus: "loading" }),
2681
+ ).toEqual({
2682
+ label: "Last evaluated",
2683
+ at: undefined,
2684
+ emptyText: "Loading…",
2685
+ isUnavailable: false,
2686
+ });
2687
+ });
2688
+
2689
+ it("a loaded, empty log is waiting for the first poll, not a measured status", () => {
2690
+ const presentation: MonitorOverviewPresentation = build(
2691
+ running(MonitorType.NetworkDevice, {
2692
+ createdAt: secondsAgo(2 * 3600),
2693
+ latestEvaluationAt: undefined,
2694
+ evaluationStatus: "loaded",
2695
+ }),
2696
+ );
2697
+
2698
+ expect(presentation.runState).toBe(
2699
+ MonitorOverviewRunState.AwaitingFirstData,
2700
+ );
2701
+ expect(presentation.badge).toEqual({
2702
+ text: "Waiting for data",
2703
+ tone: "info",
2704
+ });
2705
+ expect(presentation.headline.text).toBe("Waiting for the first poll");
2706
+ expect(presentation.lastKnownStatus).toBe(
2707
+ "Last recorded status: Operational",
2708
+ );
2709
+ expect(presentation.sections.showUptime).toBe(false);
2710
+ expect(presentation.pulse.isUnavailable).toBe(false);
2711
+ });
2712
+
2713
+ /*
2714
+ * Its polling stopped (turned off, or its probe went offline) more than
2715
+ * a day ago, and the log's rows have expired since. It has been judged
2716
+ * for months: that history, and its status, stay.
2717
+ */
2718
+ it("an older device with nothing in the log's last day keeps its status and history", () => {
2719
+ const presentation: MonitorOverviewPresentation = build(
2720
+ running(MonitorType.NetworkDevice, {
2721
+ createdAt: secondsAgo(90 * DAY),
2722
+ currentStatus: OFFLINE,
2723
+ latestEvaluationAt: undefined,
2724
+ evaluationStatus: "loaded",
2725
+ }),
2726
+ );
2727
+
2728
+ expect(presentation.runState).toBe(MonitorOverviewRunState.Running);
2729
+ expect(presentation.badge).toMatchObject({
2730
+ text: "Offline",
2731
+ tone: "danger",
2732
+ });
2733
+ expect(presentation.headline).toEqual({
2734
+ text: "Offline",
2735
+ since: secondsAgo(3 * DAY + 4 * 3600),
2736
+ });
2737
+ expect(presentation.explanation).toBe(
2738
+ "No poll or trap from this device has been evaluated in the last day, so the status shown is the last one recorded.",
2739
+ );
2740
+ expect(presentation.lastKnownStatus).toBeUndefined();
2741
+ expect(presentation.sections.showUptime).toBe(true);
2742
+ expect(presentation.pulse).toEqual({
2743
+ label: "Last evaluated",
2744
+ at: undefined,
2745
+ emptyText: "Not evaluated in the last day",
2746
+ isUnavailable: false,
2747
+ });
2748
+
2749
+ // A device that is being evaluated needs no such note.
2750
+ expect(build(running(MonitorType.NetworkDevice)).explanation).toBe(
2751
+ undefined,
2752
+ );
2753
+ });
2754
+
2755
+ it("a device just under a day old with an empty log is still waiting", () => {
2756
+ expect(
2757
+ build(
2758
+ running(MonitorType.NetworkDevice, {
2759
+ createdAt: secondsAgo(DAY - 60),
2760
+ latestEvaluationAt: undefined,
2761
+ evaluationStatus: "loaded",
2762
+ }),
2763
+ ).runState,
2764
+ ).toBe(MonitorOverviewRunState.AwaitingFirstData);
2765
+ expect(
2766
+ build(
2767
+ running(MonitorType.NetworkDevice, {
2768
+ createdAt: secondsAgo(DAY + 60),
2769
+ latestEvaluationAt: undefined,
2770
+ evaluationStatus: "loaded",
2771
+ }),
2772
+ ).runState,
2773
+ ).toBe(MonitorOverviewRunState.Running);
2774
+ });
2775
+ });
2776
+
2777
+ /*
2778
+ * The heartbeat and agent checks judge a monitor that never reported as if
2779
+ * its last signal came at creation, so it can be Offline with an incident
2780
+ * open while the page still waits for the first signal.
2781
+ */
2782
+ describe("MonitorOverviewPresentationUtil push monitors the server has judged", () => {
2783
+ it("a heartbeat judged offline leads with that verdict and keeps the setup", () => {
2784
+ const presentation: MonitorOverviewPresentation = build(
2785
+ running(MonitorType.IncomingRequest, {
2786
+ currentStatus: OFFLINE,
2787
+ createdAt: secondsAgo(3 * DAY),
2788
+ heartbeat: {},
2789
+ }),
2790
+ );
2791
+
2792
+ expect(presentation.runState).toBe(
2793
+ MonitorOverviewRunState.AwaitingFirstData,
2794
+ );
2795
+ expect(presentation.tone).toBe("danger");
2796
+ expect(presentation.badge).toEqual({
2797
+ text: "Waiting for data",
2798
+ tone: "info",
2799
+ });
2800
+ expect(presentation.secondaryBadges).toEqual([
2801
+ { text: "Offline", tone: "danger" },
2802
+ ]);
2803
+ expect(presentation.headline).toEqual({
2804
+ text: "No heartbeat has arrived since this monitor was created 3d ago",
2805
+ });
2806
+ expect(presentation.explanation).toBe(
2807
+ "Send a GET or POST request to this monitor's heartbeat URL to start tracking it.",
2808
+ );
2809
+ expect(presentation.callToAction).toEqual({
2810
+ text: "Setup instructions",
2811
+ linkKey: "documentation",
2812
+ });
2813
+ expect(presentation.lastKnownStatus).toBeUndefined();
2814
+ expect(presentation.sections.showUptime).toBe(true);
2815
+ expect(presentation.sections.setup).toBe(
2816
+ MonitorOverviewSetupKind.HeartbeatUrl,
2817
+ );
2818
+ });
2819
+
2820
+ it("an email or agent monitor judged degraded or offline leads with its tone", () => {
2821
+ const email: MonitorOverviewPresentation = build(
2822
+ running(MonitorType.IncomingEmail, {
2823
+ currentStatus: DEGRADED,
2824
+ createdAt: secondsAgo(2 * 3600),
2825
+ email: {},
2826
+ }),
2827
+ );
2828
+
2829
+ expect(email.tone).toBe("warning");
2830
+ expect(email.secondaryBadges).toEqual([
2831
+ { text: "Degraded", tone: "warning" },
2832
+ ]);
2833
+ expect(email.headline.text).toBe(
2834
+ "No email has arrived since this monitor was created 2h ago",
2835
+ );
2836
+
2837
+ const agent: MonitorOverviewPresentation = build(
2838
+ running(MonitorType.Server, {
2839
+ currentStatus: OFFLINE,
2840
+ createdAt: secondsAgo(DAY),
2841
+ agent: {},
2842
+ }),
2843
+ );
2844
+
2845
+ expect(agent.headline.text).toBe(
2846
+ "The agent has not reported since this monitor was created 1d ago",
2847
+ );
2848
+ expect(agent.sections.showUptime).toBe(true);
2849
+ expect(agent.sections.setup).toBe(MonitorOverviewSetupKind.ServerAgent);
2850
+ });
2851
+
2852
+ it("history shows once the missing-signal window since creation has passed", () => {
2853
+ const showsUptime: (
2854
+ overrides: Partial<MonitorOverviewPresentationInput>,
2855
+ ) => boolean = (
2856
+ overrides: Partial<MonitorOverviewPresentationInput>,
2857
+ ): boolean => {
2858
+ return build(
2859
+ running(MonitorType.IncomingEmail, { email: {}, ...overrides }),
2860
+ ).sections.showUptime;
2861
+ };
2862
+
2863
+ const steps: MonitorSteps = missingWindowSteps(CheckOn.EmailReceivedAt, 30);
2864
+
2865
+ expect(
2866
+ showsUptime({ monitorSteps: steps, createdAt: secondsAgo(31 * 60) }),
2867
+ ).toBe(true);
2868
+ expect(
2869
+ showsUptime({ monitorSteps: steps, createdAt: secondsAgo(29 * 60) }),
2870
+ ).toBe(false);
2871
+ // No missing-email criterion: nothing will judge it.
2872
+ expect(showsUptime({ createdAt: secondsAgo(DAY) })).toBe(false);
2873
+
2874
+ // Operational means nothing has said otherwise: calm, not a verdict.
2875
+ const calm: MonitorOverviewPresentation = build(
2876
+ running(MonitorType.IncomingEmail, {
2877
+ email: {},
2878
+ monitorSteps: steps,
2879
+ createdAt: secondsAgo(31 * 60),
2880
+ }),
2881
+ );
2882
+
2883
+ expect(calm.tone).toBe("info");
2884
+ expect(calm.secondaryBadges).toEqual([]);
2885
+ expect(calm.headline.text).toBe("Waiting for the first email");
2886
+ });
2887
+
2888
+ it("an agent's window is three minutes, and only with an Is Online criterion", () => {
2889
+ const isOnlineSteps: MonitorSteps = stepsOf({
2890
+ monitorCriteria: {
2891
+ data: {
2892
+ monitorCriteriaInstanceArray: [
2893
+ {
2894
+ data: {
2895
+ filters: [
2896
+ {
2897
+ checkOn: CheckOn.IsOnline,
2898
+ filterType: FilterType.False,
2899
+ },
2900
+ ],
2901
+ },
2902
+ },
2903
+ ],
2904
+ },
2905
+ },
2906
+ });
2907
+ const showsUptime: (
2908
+ overrides: Partial<MonitorOverviewPresentationInput>,
2909
+ ) => boolean = (
2910
+ overrides: Partial<MonitorOverviewPresentationInput>,
2911
+ ): boolean => {
2912
+ return build(running(MonitorType.Server, { agent: {}, ...overrides }))
2913
+ .sections.showUptime;
2914
+ };
2915
+
2916
+ expect(
2917
+ showsUptime({ monitorSteps: isOnlineSteps, createdAt: secondsAgo(200) }),
2918
+ ).toBe(true);
2919
+ expect(
2920
+ showsUptime({ monitorSteps: isOnlineSteps, createdAt: secondsAgo(170) }),
2921
+ ).toBe(false);
2922
+ expect(showsUptime({ createdAt: secondsAgo(DAY) })).toBe(false);
2923
+ });
2924
+ });
2925
+
2926
+ describe("MonitorOverviewPresentationUtil.getUptimeCaveat", () => {
2927
+ const caveatOf: (input: MonitorOverviewPresentationInput) => string | null = (
2928
+ input: MonitorOverviewPresentationInput,
2929
+ ): string | null => {
2930
+ return MonitorOverviewPresentationUtil.getUptimeCaveat(build(input));
2931
+ };
2932
+
2933
+ it("names the unmeasured time per run state", () => {
2934
+ const rows: Array<
2935
+ [string, MonitorOverviewPresentationInput, string | null]
2936
+ > = [
2937
+ ["running", running(MonitorType.API), null],
2938
+ ["disabled", SCENARIOS[1]!.input, "paused"],
2939
+ ["paused by an incident", SCENARIOS[2]!.input, "paused"],
2940
+ ["no probe enabled", SCENARIOS[4]!.input, "not-checking"],
2941
+ ["every probe disconnected", SCENARIOS[5]!.input, "not-checking"],
2942
+ ["no criteria", SCENARIOS[6]!.input, "not-checking"],
2943
+ [
2944
+ "overdue with results",
2945
+ running(MonitorType.API, {
2946
+ probes: probes({ lastResultAt: secondsAgo(2 * 3600) }),
2947
+ }),
2948
+ null,
2949
+ ],
2950
+ [
2951
+ "never reported",
2952
+ running(MonitorType.API, {
2953
+ createdAt: secondsAgo(2 * DAY),
2954
+ probes: probes({ ...NO_RESULTS, nextCheckAt: undefined }),
2955
+ }),
2956
+ "no-results",
2957
+ ],
2958
+ [
2959
+ "judged heartbeat",
2960
+ running(MonitorType.IncomingRequest, {
2961
+ currentStatus: OFFLINE,
2962
+ heartbeat: {},
2963
+ }),
2964
+ null,
2965
+ ],
2966
+ /*
2967
+ * Months of evaluations back these windows; the log just no longer
2968
+ * holds them.
2969
+ */
2970
+ [
2971
+ "telemetry with nothing in its evaluation log's last day",
2972
+ running(MonitorType.Metrics, {
2973
+ monitoringInterval: "* * * * *",
2974
+ createdAt: secondsAgo(90 * DAY),
2975
+ latestEvaluationAt: undefined,
2976
+ }),
2977
+ null,
2978
+ ],
2979
+ ];
2980
+
2981
+ for (const [name, input, expected] of rows) {
2982
+ expect({ name: name, caveat: caveatOf(input) }).toEqual({
2983
+ name: name,
2984
+ caveat: expected,
2985
+ });
2986
+ }
2987
+ });
2988
+
2989
+ it("a manual monitor has no checks to pause", () => {
2990
+ expect(
2991
+ caveatOf(
2992
+ running(MonitorType.Manual, {
2993
+ pause: {
2994
+ isDisabled: false,
2995
+ byManualIncident: true,
2996
+ byScheduledMaintenance: true,
2997
+ },
2998
+ }),
2999
+ ),
3000
+ ).toBeNull();
3001
+ });
3002
+ });
3003
+
3004
+ describe("MonitorOverviewPresentationUtil expiry wording", () => {
3005
+ afterEach(() => {
3006
+ // setUserTimezone is process-wide static state; never leak it.
3007
+ OneUptimeDate.setUserTimezone(null);
3008
+ });
3009
+
3010
+ const expiry: (seconds: number) => MonitorOverviewFact = (
3011
+ seconds: number,
3012
+ ): MonitorOverviewFact => {
3013
+ return MonitorOverviewPresentationUtil.getExpiryFact({
3014
+ key: "certificate-expiry",
3015
+ expiresAt: new Date(NOW.getTime() + seconds * 1000),
3016
+ isValidCertificate: true,
3017
+ now: NOW,
3018
+ });
3019
+ };
3020
+
3021
+ it("hours within a day, either way", () => {
3022
+ OneUptimeDate.setUserTimezone(Timezone.UTC);
3023
+
3024
+ const cases: Array<[number, string]> = [
3025
+ [20 * 3600, "Expires in 20 hours"],
3026
+ [3600, "Expires in 1 hour"],
3027
+ [20 * 60, "Expires in 20 minutes"],
3028
+ [5, "Expires in 1 minute"],
3029
+ [-5, "Expired 1 minute ago"],
3030
+ [-3600, "Expired 1 hour ago"],
3031
+ [-23 * 3600, "Expired 23 hours ago"],
3032
+ ];
3033
+
3034
+ for (const [seconds, value] of cases) {
3035
+ expect({ seconds: seconds, fact: expiry(seconds) }).toMatchObject({
3036
+ seconds: seconds,
3037
+ fact: { value: value, tone: "danger" },
3038
+ });
3039
+ }
3040
+ });
3041
+
3042
+ it("calendar days beyond that, in the zone the date is printed in", () => {
3043
+ OneUptimeDate.setUserTimezone(Timezone.UTC);
3044
+
3045
+ // Noon on the 21st to midnight on the 23rd: two dates away.
3046
+ expect(expiry(36 * 3600)).toMatchObject({
3047
+ value: "in 2 days",
3048
+ tone: "danger",
3049
+ });
3050
+ // Midnight on the 19th, seen at noon on the 21st: two dates back.
3051
+ expect(expiry(-60 * 3600).value).toBe("Expired 2 days ago");
3052
+ // The tone still counts whole days left: 7 days and 20 hours is danger.
3053
+ expect(expiry(7 * DAY + 20 * 3600)).toMatchObject({
3054
+ value: "in 8 days",
3055
+ tone: "danger",
3056
+ });
3057
+
3058
+ // 08:00 on the 21st to 20:00 on the 22nd in New York: one date away.
3059
+ OneUptimeDate.setUserTimezone(Timezone.AmericaNew_York);
3060
+
3061
+ const newYork: MonitorOverviewFact = expiry(36 * 3600);
3062
+
3063
+ expect(newYork.value).toBe("in 1 day");
3064
+ expect(newYork.secondary).toBe("Sep 22, 2026");
3065
+ });
3066
+ });
3067
+
3068
+ /*
3069
+ * `now` is the page's single clock, corrected to the server's by the
3070
+ * caller. Nothing may read the machine clock behind its back, or a fast
3071
+ * browser clock would make every check look late.
3072
+ */
3073
+ describe("MonitorOverviewPresentationUtil clock", () => {
3074
+ afterEach(() => {
3075
+ jest.useRealTimers();
3076
+ });
3077
+
3078
+ it("reads no clock but input.now", () => {
3079
+ const inputs: Array<MonitorOverviewPresentationInput> = SCENARIOS.map(
3080
+ (scenario: Scenario) => {
3081
+ return scenario.input;
3082
+ },
3083
+ );
3084
+ const before: Array<MonitorOverviewPresentation> = inputs.map(
3085
+ (input: MonitorOverviewPresentationInput) => {
3086
+ return build(input);
3087
+ },
3088
+ );
3089
+
3090
+ jest.useFakeTimers();
3091
+ // Seven minutes fast, and three days ahead besides.
3092
+ jest.setSystemTime(new Date(NOW.getTime() + (7 * 60 + 3 * DAY) * 1000));
3093
+
3094
+ expect(
3095
+ inputs.map((input: MonitorOverviewPresentationInput) => {
3096
+ return build(input);
3097
+ }),
3098
+ ).toEqual(before);
3099
+ });
3100
+ });