@oneuptime/common 14.0.2 → 14.0.5

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 (765) hide show
  1. package/Models/DatabaseModels/Alert.ts +55 -0
  2. package/Models/DatabaseModels/AlertFeed.ts +2 -0
  3. package/Models/DatabaseModels/DatabaseServer.ts +1650 -0
  4. package/Models/DatabaseModels/DatabaseServerEndpoint.ts +540 -0
  5. package/Models/DatabaseModels/DatabaseServerFeed.ts +625 -0
  6. package/Models/DatabaseModels/DatabaseServerLabelRule.ts +514 -0
  7. package/Models/DatabaseModels/DatabaseServerOwnerRule.ts +596 -0
  8. package/Models/DatabaseModels/DatabaseServerOwnerTeam.ts +491 -0
  9. package/Models/DatabaseModels/DatabaseServerOwnerUser.ts +490 -0
  10. package/Models/DatabaseModels/Incident.ts +55 -0
  11. package/Models/DatabaseModels/IncidentAlert.ts +540 -0
  12. package/Models/DatabaseModels/IncidentFeed.ts +2 -0
  13. package/Models/DatabaseModels/Index.ts +18 -0
  14. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +26 -0
  15. package/Models/DatabaseModels/Project.ts +60 -0
  16. package/Models/DatabaseModels/RumApplication.ts +52 -1
  17. package/Models/DatabaseModels/ScheduledMaintenance.ts +55 -0
  18. package/Models/DatabaseModels/TelemetryException.ts +2 -0
  19. package/Models/DatabaseModels/UserProjectSsoConsent.ts +146 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.ts +28 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.ts +89 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.ts +673 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.ts +51 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  25. package/Server/Middleware/MultipartFormData.ts +1 -1
  26. package/Server/Middleware/TelemetryIngest.ts +15 -8
  27. package/Server/Services/AlertReminderRuleService.ts +100 -24
  28. package/Server/Services/DatabaseServerEndpointService.ts +895 -0
  29. package/Server/Services/DatabaseServerFeedService.ts +128 -0
  30. package/Server/Services/DatabaseServerLabelRuleEngineService.ts +340 -0
  31. package/Server/Services/DatabaseServerLabelRuleService.ts +14 -0
  32. package/Server/Services/DatabaseServerOwnerRuleEngineService.ts +425 -0
  33. package/Server/Services/DatabaseServerOwnerRuleService.ts +14 -0
  34. package/Server/Services/DatabaseServerOwnerTeamService.ts +180 -0
  35. package/Server/Services/DatabaseServerOwnerUserService.ts +173 -0
  36. package/Server/Services/DatabaseServerService.ts +4546 -0
  37. package/Server/Services/DatabaseService.ts +14 -8
  38. package/Server/Services/DockerResourceService.ts +43 -2
  39. package/Server/Services/IncidentAlertService.ts +1811 -0
  40. package/Server/Services/IncidentReminderRuleService.ts +103 -23
  41. package/Server/Services/IncidentService.ts +210 -10
  42. package/Server/Services/IncidentStateTimelineService.ts +24 -0
  43. package/Server/Services/Index.ts +18 -0
  44. package/Server/Services/InventoryItemService.ts +113 -5
  45. package/Server/Services/IoTDeviceService.ts +145 -37
  46. package/Server/Services/OpenTelemetryIngestService.ts +19 -0
  47. package/Server/Services/PodmanResourceService.ts +43 -2
  48. package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +107 -23
  49. package/Server/Services/ServiceService.ts +1 -1
  50. package/Server/Services/TelemetryUsageBillingService.ts +29 -2
  51. package/Server/Services/UserProjectSsoConsentService.ts +74 -0
  52. package/Server/Services/UserService.ts +14 -1
  53. package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +18 -0
  54. package/Server/Types/Database/QueryHelper.ts +30 -1
  55. package/Server/Utils/AI/Toolbox/ResourceTools.ts +69 -1
  56. package/Server/Utils/Database/AffectedResourceRelations.ts +6 -0
  57. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +188 -6
  58. package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
  59. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +2262 -755
  60. package/Server/Utils/Monitor/MonitorIncident.ts +28 -1
  61. package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +268 -6
  62. package/Server/Utils/Monitor/MonitorResource.ts +47 -11
  63. package/Server/Utils/Monitor/MonitorResourceContext.ts +1 -0
  64. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +239 -21
  65. package/Server/Utils/Monitor/PerSeriesResolutionRootCause.ts +43 -0
  66. package/Server/Utils/Monitor/SeriesResourceLabels.ts +291 -0
  67. package/Server/Utils/Monitor/SeriesResourceLinker.ts +111 -12
  68. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
  69. package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +24 -2
  70. package/Server/Utils/StartServer.ts +26 -6
  71. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
  72. package/Server/Utils/Telemetry/DatabaseEndpointDiscovery.ts +688 -0
  73. package/Server/Utils/Telemetry/EntityRegistry.ts +5 -1
  74. package/Server/Utils/Telemetry/LogExceptionExtractor.ts +18 -1
  75. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +175 -12
  76. package/Server/Utils/Telemetry/ResourceFacetResolver.ts +12 -0
  77. package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +606 -36
  78. package/Server/Utils/Telemetry/Telemetry.ts +16 -0
  79. package/Server/Utils/Telemetry/TelemetryEntity.ts +22 -22
  80. package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +16 -10
  81. package/Tests/App/Accounts/SignedInRedirect.test.tsx +241 -0
  82. package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +221 -4
  83. package/Tests/App/Dashboard/AIInsightsResourcePageContext.test.ts +42 -0
  84. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +100 -0
  85. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +86 -0
  86. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +2 -0
  87. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +19 -0
  88. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +14 -0
  89. package/Tests/App/Dashboard/BulkIncidentLinkActions.test.tsx +869 -0
  90. package/Tests/App/Dashboard/CephMetricTooltips.test.tsx +1777 -0
  91. package/Tests/App/Dashboard/CloudFleetSummary.test.ts +8 -0
  92. package/Tests/App/Dashboard/CloudMetricTooltips.test.tsx +887 -0
  93. package/Tests/App/Dashboard/ContainerHostMetricTooltips.test.tsx +1309 -0
  94. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +26 -1
  95. package/Tests/App/Dashboard/DashboardAppSsoError.test.tsx +501 -0
  96. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +64 -1
  97. package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +178 -18
  98. package/Tests/App/Dashboard/DashboardMasterPageSsoRedirect.test.tsx +444 -0
  99. package/Tests/App/Dashboard/DashboardNavBarForeignHiddenRule.test.tsx +573 -0
  100. package/Tests/App/Dashboard/DatabaseChartAxes.test.tsx +431 -0
  101. package/Tests/App/Dashboard/DatabaseContainerDetailBadge.test.tsx +249 -0
  102. package/Tests/App/Dashboard/DatabaseDashboardE2EFixes.test.ts +681 -0
  103. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +1801 -0
  104. package/Tests/App/Dashboard/DatabaseFleetSummaryRefresh.test.tsx +280 -0
  105. package/Tests/App/Dashboard/DatabaseFormHelpers.test.tsx +321 -0
  106. package/Tests/App/Dashboard/DatabaseMetricMonitorLink.test.tsx +795 -0
  107. package/Tests/App/Dashboard/DatabaseOverviewHeader.test.tsx +580 -0
  108. package/Tests/App/Dashboard/DatabaseRecommendationsEngineMetrics.test.ts +270 -0
  109. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +1486 -0
  110. package/Tests/App/Dashboard/DatabaseServerEngineMetricsFilter.test.ts +223 -0
  111. package/Tests/App/Dashboard/DatabaseServerPresentation.test.ts +435 -0
  112. package/Tests/App/Dashboard/DatabaseServerSummary.test.ts +186 -0
  113. package/Tests/App/Dashboard/DatabaseServerTelemetryQueries.test.ts +1553 -0
  114. package/Tests/App/Dashboard/DatabaseTableTruncation.test.tsx +326 -0
  115. package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +512 -0
  116. package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +774 -0
  117. package/Tests/App/Dashboard/DatabaseViewLayout.test.tsx +233 -0
  118. package/Tests/App/Dashboard/DatabaseWorkloadBadge.test.tsx +972 -0
  119. package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +5 -0
  120. package/Tests/App/Dashboard/DiscoveryReviewUnnamedHostHint.test.tsx +1061 -0
  121. package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +39 -0
  122. package/Tests/App/Dashboard/DockerSwarmClustersListTooltips.test.tsx +414 -0
  123. package/Tests/App/Dashboard/DockerSwarmMetricTooltips.test.ts +796 -0
  124. package/Tests/App/Dashboard/DockerSwarmOverviewTooltips.test.tsx +747 -0
  125. package/Tests/App/Dashboard/EventOverlayScope.test.ts +147 -0
  126. package/Tests/App/Dashboard/EventOverlayScopeHook.test.tsx +1 -0
  127. package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +38 -18
  128. package/Tests/App/Dashboard/HostDetailViewTooltips.test.tsx +725 -0
  129. package/Tests/App/Dashboard/HostListColumnTooltips.test.tsx +472 -0
  130. package/Tests/App/Dashboard/HostMetricTooltips.test.ts +709 -0
  131. package/Tests/App/Dashboard/HostOverviewTooltips.test.tsx +698 -0
  132. package/Tests/App/Dashboard/HostTooltipHarness.ts +124 -0
  133. package/Tests/App/Dashboard/HostsListTooltips.test.tsx +395 -0
  134. package/Tests/App/Dashboard/IncidentAlertLinkDialog.test.tsx +691 -0
  135. package/Tests/App/Dashboard/IncidentAlertLinkHelpers.test.ts +458 -0
  136. package/Tests/App/Dashboard/IncidentAlertLinkPages.test.tsx +828 -0
  137. package/Tests/App/Dashboard/IncidentAlertLinkSideMenus.test.tsx +231 -0
  138. package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +689 -0
  139. package/Tests/App/Dashboard/IoTMetricTooltips.test.tsx +1123 -0
  140. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +837 -0
  141. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +699 -0
  142. package/Tests/App/Dashboard/KubernetesContainersTabTooltips.test.tsx +320 -0
  143. package/Tests/App/Dashboard/KubernetesCostTooltips.test.tsx +502 -0
  144. package/Tests/App/Dashboard/KubernetesInsightsTooltips.test.tsx +222 -0
  145. package/Tests/App/Dashboard/KubernetesResourceMetricTooltips.test.ts +561 -0
  146. package/Tests/App/Dashboard/KubernetesResourcePagesTooltips.test.tsx +1168 -0
  147. package/Tests/App/Dashboard/LinkIncidentAlertModal.test.tsx +154 -0
  148. package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +1 -0
  149. package/Tests/App/Dashboard/MetricDescriptionRules.ts +170 -0
  150. package/Tests/App/Dashboard/MetricDescriptionsCatalog.test.ts +206 -0
  151. package/Tests/App/Dashboard/MetricDescriptionsConsistency.test.ts +275 -0
  152. package/Tests/App/Dashboard/MetricsViewerDrillDown.test.tsx +310 -0
  153. package/Tests/App/Dashboard/MetricsViewerEntityScopedRows.test.tsx +495 -0
  154. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +132 -1
  155. package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +5 -0
  156. package/Tests/App/Dashboard/NetworkDeviceTooltips.test.tsx +852 -0
  157. package/Tests/App/Dashboard/NetworkDiagnosticsTooltips.test.tsx +556 -0
  158. package/Tests/App/Dashboard/NetworkMetricTooltips.test.ts +1348 -0
  159. package/Tests/App/Dashboard/NetworkSiteTooltips.test.tsx +679 -0
  160. package/Tests/App/Dashboard/ProductOrientation.test.tsx +4 -3
  161. package/Tests/App/Dashboard/ProxmoxMetricTooltips.test.ts +592 -0
  162. package/Tests/App/Dashboard/ProxmoxOverviewTooltips.test.tsx +1002 -0
  163. package/Tests/App/Dashboard/ProxmoxResourcePageTooltips.test.tsx +923 -0
  164. package/Tests/App/Dashboard/RecommendationsLoadedContext.test.tsx +349 -0
  165. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +62 -0
  166. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
  167. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +14 -0
  168. package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +1066 -0
  169. package/Tests/App/Dashboard/RumOverviewPage.test.tsx +1195 -0
  170. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +2 -0
  171. package/Tests/App/Dashboard/ServerlessMetricTooltips.test.tsx +474 -0
  172. package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +1085 -0
  173. package/Tests/App/Dashboard/ServiceMetricTooltips.test.tsx +772 -0
  174. package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +56 -1
  175. package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +1207 -0
  176. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +10 -0
  177. package/Tests/App/Dashboard/TelemetryResourceTabTooltips.test.tsx +437 -0
  178. package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +2 -1
  179. package/Tests/App/Dashboard/VMwareMetricTooltips.test.ts +801 -0
  180. package/Tests/App/Dashboard/VMwareOverviewTooltips.test.tsx +601 -0
  181. package/Tests/App/Dashboard/VMwareResourcePagesTooltips.test.tsx +646 -0
  182. package/Tests/App/Dashboard/VMwareTooltipHarness.ts +145 -0
  183. package/Tests/App/StatusPage/StatusPageHeaderForeignHiddenRule.test.tsx +284 -0
  184. package/Tests/ClassStrings.ts +178 -0
  185. package/Tests/Fixtures/MobilePasskeyUser.json +1 -2
  186. package/Tests/ForeignHiddenRuleGuard.ts +2152 -0
  187. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +2250 -0
  188. package/Tests/Models/DatabaseModels/IncidentAlertModel.test.ts +554 -0
  189. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +24 -5
  190. package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +117 -0
  191. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  192. package/Tests/RenderedMarkup.ts +518 -0
  193. package/Tests/ResponsiveVisibility.test.ts +490 -4
  194. package/Tests/ResponsiveVisibility.ts +505 -52
  195. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  196. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +541 -0
  197. package/Tests/Server/Infrastructure/Postgres/AddIncidentAlertMigration.test.ts +341 -0
  198. package/Tests/Server/Infrastructure/Postgres/AddSessionReplaySameOriginTracePropagationMigration.test.ts +247 -0
  199. package/Tests/Server/Infrastructure/Postgres/AddUserProjectSsoConsentMigration.test.ts +234 -0
  200. package/Tests/Server/Services/ContainerSnapshotUpsertPostgres.test.ts +319 -0
  201. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +13 -0
  202. package/Tests/Server/Services/DatabaseServerEndpointService.test.ts +2033 -0
  203. package/Tests/Server/Services/DatabaseServerFeedAndOwnerServices.test.ts +529 -0
  204. package/Tests/Server/Services/DatabaseServerLifecycle.test.ts +3302 -0
  205. package/Tests/Server/Services/DatabaseServerRegistries.test.ts +603 -0
  206. package/Tests/Server/Services/DatabaseServerRuleEngines.test.ts +754 -0
  207. package/Tests/Server/Services/DatabaseServerService.test.ts +3558 -0
  208. package/Tests/Server/Services/DatabaseServerSqlPostgres.test.ts +2045 -0
  209. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedPostgres.test.ts +362 -0
  210. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedReply.test.ts +210 -0
  211. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +18 -0
  212. package/Tests/Server/Services/IncidentAlertPostgres.test.ts +903 -0
  213. package/Tests/Server/Services/IncidentAlertRegistration.test.ts +96 -0
  214. package/Tests/Server/Services/IncidentAlertService.test.ts +2225 -0
  215. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +2 -0
  216. package/Tests/Server/Services/IncidentAlertStateCascade.test.ts +970 -0
  217. package/Tests/Server/Services/IncidentCreateFromAlerts.test.ts +1284 -0
  218. package/Tests/Server/Services/IncomingCallPolicyEscalationRuleTarget.test.ts +225 -0
  219. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +280 -2
  220. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +8 -2
  221. package/Tests/Server/Services/IoTDeviceLatestMetricsPostgres.test.ts +689 -0
  222. package/Tests/Server/Services/IoTDeviceService.test.ts +379 -0
  223. package/Tests/Server/Services/LatestMetricsValuesShape.test.ts +360 -0
  224. package/Tests/Server/Services/LlmModelPriceService.test.ts +402 -0
  225. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +7 -1
  226. package/Tests/Server/Services/OwnerRowUniqueness.test.ts +61 -9
  227. package/Tests/Server/Services/ReminderRuleLookupPostgres.test.ts +2250 -0
  228. package/Tests/Server/Services/ReminderRuleRefreshOpenSubjectsPaging.test.ts +653 -0
  229. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +68 -20
  230. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
  231. package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +9 -0
  232. package/Tests/Server/Services/UserProjectSsoConsentPostgres.test.ts +153 -0
  233. package/Tests/Server/Services/UserProjectSsoConsentService.test.ts +192 -0
  234. package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +71 -0
  235. package/Tests/Server/Services/UserSignupIdTakeoverPostgres.test.ts +238 -0
  236. package/Tests/Server/Services/WorkspaceNotificationSummaryHelpers.test.ts +592 -0
  237. package/Tests/Server/Types/Billing/PayAsYouGoMeteredAuthorization.test.ts +3 -0
  238. package/Tests/Server/Types/Database/EventOverlayScopePostgres.test.ts +1 -0
  239. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +11 -0
  240. package/Tests/Server/Types/Database/Permissions/InfrastructureOwnershipScope.test.ts +8 -0
  241. package/Tests/Server/Types/Database/QueryHelperBooleanForCriteriaBackedRule.test.ts +1094 -0
  242. package/Tests/Server/Utils/AI/ResourceTools.test.ts +134 -0
  243. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +41 -0
  244. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +279 -9
  245. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaDisplayContext.test.ts +933 -0
  246. package/Tests/Server/Utils/Monitor/DatabaseMaintenanceSuppression.test.ts +582 -0
  247. package/Tests/Server/Utils/Monitor/DatabaseServerMonitorLinking.test.ts +753 -0
  248. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +1 -0
  249. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +1 -0
  250. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +119 -0
  251. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +60 -25
  252. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +1719 -0
  253. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesAffectedResources.test.ts +1447 -0
  254. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesRatioEmail.test.ts +352 -0
  255. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformMetricUnits.test.ts +717 -0
  256. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformRawScan.test.ts +2125 -0
  257. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformSeriesAffectedResources.test.ts +1288 -0
  258. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +1 -0
  259. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +1 -0
  260. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +1 -0
  261. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +88 -0
  262. package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +205 -0
  263. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +62 -0
  264. package/Tests/Server/Utils/Monitor/MonitorResourceDatabaseMaintenance.test.ts +803 -0
  265. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +133 -3
  266. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +1 -0
  267. package/Tests/Server/Utils/Monitor/PerSeriesRecoveryResolution.test.ts +472 -1
  268. package/Tests/Server/Utils/Monitor/PerSeriesSecondEntityAlerting.test.ts +1 -0
  269. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +236 -2
  270. package/Tests/Server/Utils/SeriesResourceLabels.test.ts +70 -0
  271. package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +103 -0
  272. package/Tests/Server/Utils/StartServerProtobufContentType.test.ts +54 -0
  273. package/Tests/Server/Utils/Telemetry/DatabaseEndpointDiscovery.test.ts +1895 -0
  274. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +7 -0
  275. package/Tests/Server/Utils/Telemetry/LogExceptionExtractorSeverityGate.test.ts +90 -0
  276. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +570 -0
  277. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +97 -2
  278. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +1337 -0
  279. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +295 -0
  280. package/Tests/Types/AI/AIResourceContext.test.ts +30 -0
  281. package/Tests/Types/DatabaseServer/DatabaseContainerClassifier.test.ts +3644 -0
  282. package/Tests/Types/DatabaseServer/DatabaseContainerCommand.test.ts +340 -0
  283. package/Tests/Types/DatabaseServer/DatabaseContainerCommandCorpus.ts +826 -0
  284. package/Tests/Types/DatabaseServer/DatabaseContainerCommandPostgres.test.ts +150 -0
  285. package/Tests/Types/DatabaseServer/DatabaseEndpoint.test.ts +2657 -0
  286. package/Tests/Types/DatabaseServer/DatabaseServerDiscoverySource.test.ts +64 -0
  287. package/Tests/Types/DatabaseServer/DatabaseServerEntityKeys.test.ts +952 -0
  288. package/Tests/Types/DatabaseServer/DatabaseServerMetricCatalog.test.ts +1161 -0
  289. package/Tests/Types/DatabaseServer/DatabaseSystem.test.ts +1514 -0
  290. package/Tests/Types/DatabaseServer/DatabaseTelemetryResolver.test.ts +1558 -0
  291. package/Tests/Types/Monitor/CephAlertTemplates.test.ts +255 -6
  292. package/Tests/Types/Monitor/DatabaseAlertTemplates.test.ts +2254 -0
  293. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +298 -0
  294. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +271 -0
  295. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +253 -9
  296. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +13 -5
  297. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +4 -2
  298. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +111 -2
  299. package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +61 -0
  300. package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +114 -0
  301. package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +181 -0
  302. package/Tests/Types/NetworkDevice/DiscoveredHostNamingStatus.test.ts +401 -0
  303. package/Tests/Types/Rum/WebVitals.test.ts +173 -0
  304. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +4 -1
  305. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +24 -2
  306. package/Tests/UI/Components/Card.test.tsx +93 -2
  307. package/Tests/UI/Components/FeedOptions.test.ts +6 -0
  308. package/Tests/UI/Components/FeedOptionsButton.test.tsx +8 -1
  309. package/Tests/UI/Components/FieldLabelInfoCardTooltip.test.tsx +413 -0
  310. package/Tests/UI/Components/ForeignHiddenRuleResilience.test.tsx +1147 -0
  311. package/Tests/UI/Components/HeaderRightRail.test.tsx +41 -1
  312. package/Tests/UI/Components/InfoTooltip.test.tsx +242 -0
  313. package/Tests/UI/Components/LogsEntityNames.test.ts +16 -1
  314. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +7 -3
  315. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +207 -3
  316. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDeleteVerb.test.tsx +367 -0
  317. package/Tests/UI/Components/ModelTable/BaseModelTableCreateButtonTitle.test.tsx +275 -0
  318. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +2 -1
  319. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  320. package/Tests/UI/Components/SrOnlyForeignRule.test.tsx +994 -0
  321. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +34 -1
  322. package/Tests/UI/Components/TableHeaderTooltip.test.tsx +662 -0
  323. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +4 -1
  324. package/Tests/UI/ForeignHiddenRuleGuard.test.ts +1951 -0
  325. package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +166 -9
  326. package/Tests/UI/Rum/RecordingHealthTooltips.test.tsx +447 -0
  327. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +164 -0
  328. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +6750 -0
  329. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +19 -1
  330. package/Tests/UI/Rum/ReplayRail.test.tsx +723 -12
  331. package/Tests/UI/Rum/ReplayRailDetail.test.tsx +86 -0
  332. package/Tests/UI/Rum/ReplayScreenshot.test.ts +2124 -0
  333. package/Tests/UI/Rum/ReplayScreenshotActions.test.tsx +3288 -0
  334. package/Tests/UI/Rum/ReplayStage.test.tsx +62 -0
  335. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +983 -2
  336. package/Tests/UI/Rum/ReplayTabSwitcher.test.tsx +19 -1
  337. package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +149 -0
  338. package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +107 -6
  339. package/Tests/UI/Rum/SessionReplayUsersTableTooltips.test.tsx +330 -0
  340. package/Tests/UI/Rum/UserFlowTooltips.test.tsx +886 -0
  341. package/Tests/UI/TailwindPlayCdnForeignHiddenRule.test.ts +1005 -0
  342. package/Tests/UI/ThemeGroupHoverReveal.test.ts +847 -0
  343. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +80 -0
  344. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +23 -0
  345. package/Tests/UI/Utils/ModelAPISsoRedirect.test.ts +127 -0
  346. package/Tests/UI/Utils/Navigation.test.ts +102 -0
  347. package/Tests/UI/Utils/PermissionGate.test.ts +76 -0
  348. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +23 -0
  349. package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +696 -0
  350. package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
  351. package/Tests/Utils/MetricUnitUtil.test.ts +227 -2
  352. package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +288 -2
  353. package/Tests/Utils/Monitor/PlatformResourceIdentityAndUnits.test.ts +1559 -0
  354. package/Tests/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.test.ts +2546 -0
  355. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +315 -0
  356. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +336 -1
  357. package/Tests/Utils/NetworkDiscovery/NetbiosNameUtil.test.ts +244 -0
  358. package/Tests/Utils/Rum/SessionTraceState.test.ts +239 -0
  359. package/Tests/Utils/ValueFormatter.test.ts +50 -0
  360. package/Tests/tsconfig.json +16 -0
  361. package/Types/AI/AIResourceContext.ts +7 -0
  362. package/Types/Dashboard/DashboardTemplates.ts +2 -1
  363. package/Types/DatabaseServer/DatabaseConnectionSpan.ts +77 -0
  364. package/Types/DatabaseServer/DatabaseContainerClassifier.ts +2068 -0
  365. package/Types/DatabaseServer/DatabaseContainerCommand.ts +801 -0
  366. package/Types/DatabaseServer/DatabaseEndpoint.ts +1943 -0
  367. package/Types/DatabaseServer/DatabaseServerDiscoverySource.ts +68 -0
  368. package/Types/DatabaseServer/DatabaseServerMetricCatalog.ts +966 -0
  369. package/Types/DatabaseServer/DatabaseSystem.ts +2124 -0
  370. package/Types/DatabaseServer/DatabaseTelemetryResolver.ts +555 -0
  371. package/Types/Email/EmailTemplateType.ts +1 -0
  372. package/Types/Incident/IncidentAlertLink.ts +38 -0
  373. package/Types/Monitor/CephAlertTemplates.ts +18 -0
  374. package/Types/Monitor/DatabaseAlertTemplates.ts +2020 -0
  375. package/Types/Monitor/KubernetesAlertTemplates.ts +64 -24
  376. package/Types/Monitor/KubernetesMetricCatalog.ts +118 -19
  377. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +14 -0
  378. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +93 -10
  379. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +109 -8
  380. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +36 -7
  381. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +43 -2
  382. package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +41 -6
  383. package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +26 -0
  384. package/Types/NetworkDevice/DiscoveredHostNamingStatus.ts +147 -0
  385. package/Types/Permission.ts +315 -0
  386. package/Types/Probe/ProbeApiIngestResponse.ts +8 -0
  387. package/Types/Rules/RuleCriteriaFieldRegistry.ts +10 -0
  388. package/Types/Rules/RuleRun.ts +12 -0
  389. package/Types/Rum/SessionReplay.ts +25 -6
  390. package/Types/Rum/WebVitals.ts +52 -0
  391. package/Types/Telemetry/EntityType.ts +23 -0
  392. package/Types/Telemetry/ResourceFacetCatalog.ts +14 -0
  393. package/Types/Telemetry/ServiceType.ts +8 -0
  394. package/UI/Components/Banner/Banner.tsx +1 -1
  395. package/UI/Components/Card/Card.tsx +1 -1
  396. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +2 -2
  397. package/UI/Components/Charts/Line/LineChart.tsx +7 -0
  398. package/UI/Components/CommandPalette/CommandPalette.tsx +1 -1
  399. package/UI/Components/Date/StartAndEndDate.tsx +5 -5
  400. package/UI/Components/Detail/FieldLabel.tsx +8 -0
  401. package/UI/Components/Diagram/HorizontalStepChain.tsx +1 -1
  402. package/UI/Components/Feed/FeedOptionsButton.tsx +1 -1
  403. package/UI/Components/Forms/BasicForm.tsx +1 -1
  404. package/UI/Components/Header/Header.tsx +1 -1
  405. package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -1
  406. package/UI/Components/HeaderAlert/HeaderAlert.tsx +1 -1
  407. package/UI/Components/InfoCard/InfoCard.tsx +47 -1
  408. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +1 -1
  409. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +1 -1
  410. package/UI/Components/Modal/ModalFooter.tsx +1 -1
  411. package/UI/Components/ModelDetail/CardModelDetail.tsx +2 -2
  412. package/UI/Components/ModelTable/BaseModelTable.tsx +99 -21
  413. package/UI/Components/ModelTable/Column.ts +5 -0
  414. package/UI/Components/Navbar/NavBar.tsx +9 -2
  415. package/UI/Components/Navbar/NavBarMenuModal.tsx +2 -2
  416. package/UI/Components/Page/Page.tsx +1 -1
  417. package/UI/Components/Pagination/Pagination.tsx +3 -3
  418. package/UI/Components/ProgressButtons/ProgressButtonItem.tsx +1 -1
  419. package/UI/Components/SideMenu/SideMenu.tsx +4 -1
  420. package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +4 -2
  421. package/UI/Components/StatusPage/ResourceList.tsx +6 -6
  422. package/UI/Components/Table/TableHeader.tsx +47 -2
  423. package/UI/Components/Table/Types/Column.ts +7 -0
  424. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +1 -1
  425. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +1 -1
  426. package/UI/Components/Tooltip/InfoTooltip.tsx +78 -0
  427. package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +1 -1
  428. package/UI/Components/Workflow/ComponentsModal.tsx +2 -2
  429. package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
  430. package/UI/Styles/Theme.css +8 -1
  431. package/UI/Utils/Navigation.ts +89 -42
  432. package/UI/Utils/PermissionGate.ts +11 -2
  433. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +7 -0
  434. package/UI/Utils/TelemetryService.ts +4 -3
  435. package/Utils/Incident/IncidentFromAlerts.ts +516 -0
  436. package/Utils/LabelRuleImportExport.ts +2 -0
  437. package/Utils/MetricUnitUtil.ts +103 -11
  438. package/Utils/Monitor/MetricValueFormatter.ts +66 -13
  439. package/Utils/Monitor/PlatformMetricUnitUtil.ts +215 -0
  440. package/Utils/Monitor/PlatformResourceIdentity.ts +514 -0
  441. package/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.ts +425 -0
  442. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +32 -0
  443. package/Utils/NetworkDiscovery/NetbiosNameUtil.ts +73 -0
  444. package/Utils/Rum/SessionTraceState.ts +212 -0
  445. package/Utils/Telemetry/DatabaseServerEntityKeys.ts +275 -0
  446. package/Utils/Telemetry/EntityKey.ts +176 -0
  447. package/Utils/ValueFormatter.ts +16 -0
  448. package/build/dist/Models/DatabaseModels/Alert.js +54 -0
  449. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  450. package/build/dist/Models/DatabaseModels/AlertFeed.js +2 -0
  451. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  452. package/build/dist/Models/DatabaseModels/DatabaseServer.js +1683 -0
  453. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -0
  454. package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js +559 -0
  455. package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js.map +1 -0
  456. package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js +648 -0
  457. package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js.map +1 -0
  458. package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js +522 -0
  459. package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js.map +1 -0
  460. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js +603 -0
  461. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js.map +1 -0
  462. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js +506 -0
  463. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js.map +1 -0
  464. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js +505 -0
  465. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js.map +1 -0
  466. package/build/dist/Models/DatabaseModels/Incident.js +54 -0
  467. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  468. package/build/dist/Models/DatabaseModels/IncidentAlert.js +553 -0
  469. package/build/dist/Models/DatabaseModels/IncidentAlert.js.map +1 -0
  470. package/build/dist/Models/DatabaseModels/IncidentFeed.js +2 -0
  471. package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
  472. package/build/dist/Models/DatabaseModels/Index.js +18 -0
  473. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  474. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  475. package/build/dist/Models/DatabaseModels/Project.js +62 -0
  476. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  477. package/build/dist/Models/DatabaseModels/RumApplication.js +53 -1
  478. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  479. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +54 -0
  480. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  481. package/build/dist/Models/DatabaseModels/TelemetryException.js +2 -0
  482. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  483. package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js +162 -0
  484. package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js.map +1 -0
  485. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js +20 -0
  486. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js.map +1 -0
  487. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js +42 -0
  488. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js.map +1 -0
  489. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js +240 -0
  490. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js.map +1 -0
  491. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js +26 -0
  492. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js.map +1 -0
  493. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  494. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  495. package/build/dist/Server/Middleware/MultipartFormData.js +1 -1
  496. package/build/dist/Server/Middleware/TelemetryIngest.js +15 -8
  497. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  498. package/build/dist/Server/Services/AlertReminderRuleService.js +72 -23
  499. package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
  500. package/build/dist/Server/Services/DatabaseServerEndpointService.js +694 -0
  501. package/build/dist/Server/Services/DatabaseServerEndpointService.js.map +1 -0
  502. package/build/dist/Server/Services/DatabaseServerFeedService.js +110 -0
  503. package/build/dist/Server/Services/DatabaseServerFeedService.js.map +1 -0
  504. package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js +272 -0
  505. package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js.map +1 -0
  506. package/build/dist/Server/Services/DatabaseServerLabelRuleService.js +13 -0
  507. package/build/dist/Server/Services/DatabaseServerLabelRuleService.js.map +1 -0
  508. package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js +340 -0
  509. package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js.map +1 -0
  510. package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js +13 -0
  511. package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js.map +1 -0
  512. package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js +160 -0
  513. package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js.map +1 -0
  514. package/build/dist/Server/Services/DatabaseServerOwnerUserService.js +153 -0
  515. package/build/dist/Server/Services/DatabaseServerOwnerUserService.js.map +1 -0
  516. package/build/dist/Server/Services/DatabaseServerService.js +3272 -0
  517. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -0
  518. package/build/dist/Server/Services/DatabaseService.js +8 -4
  519. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  520. package/build/dist/Server/Services/DockerResourceService.js +30 -3
  521. package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
  522. package/build/dist/Server/Services/IncidentAlertService.js +1370 -0
  523. package/build/dist/Server/Services/IncidentAlertService.js.map +1 -0
  524. package/build/dist/Server/Services/IncidentReminderRuleService.js +73 -23
  525. package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
  526. package/build/dist/Server/Services/IncidentService.js +164 -12
  527. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  528. package/build/dist/Server/Services/IncidentStateTimelineService.js +21 -0
  529. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  530. package/build/dist/Server/Services/Index.js +18 -0
  531. package/build/dist/Server/Services/Index.js.map +1 -1
  532. package/build/dist/Server/Services/InventoryItemService.js +88 -5
  533. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  534. package/build/dist/Server/Services/IoTDeviceService.js +111 -24
  535. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  536. package/build/dist/Server/Services/OpenTelemetryIngestService.js +24 -9
  537. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  538. package/build/dist/Server/Services/PodmanResourceService.js +30 -3
  539. package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
  540. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +75 -23
  541. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
  542. package/build/dist/Server/Services/ServiceService.js +1 -1
  543. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  544. package/build/dist/Server/Services/TelemetryUsageBillingService.js +23 -2
  545. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  546. package/build/dist/Server/Services/UserProjectSsoConsentService.js +79 -0
  547. package/build/dist/Server/Services/UserProjectSsoConsentService.js.map +1 -0
  548. package/build/dist/Server/Services/UserService.js +13 -1
  549. package/build/dist/Server/Services/UserService.js.map +1 -1
  550. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +18 -0
  551. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
  552. package/build/dist/Server/Types/Database/QueryHelper.js +26 -1
  553. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  554. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +46 -0
  555. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  556. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +6 -0
  557. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -1
  558. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +134 -13
  559. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  560. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -6
  561. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  562. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1489 -524
  563. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  564. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +22 -6
  565. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  566. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +196 -6
  567. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
  568. package/build/dist/Server/Utils/Monitor/MonitorResource.js +36 -9
  569. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  570. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +1 -0
  571. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -1
  572. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +175 -17
  573. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
  574. package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js +29 -0
  575. package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js.map +1 -0
  576. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +211 -0
  577. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  578. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +82 -10
  579. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  580. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
  581. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  582. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +8 -0
  583. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
  584. package/build/dist/Server/Utils/StartServer.js +20 -4
  585. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  586. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
  587. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
  588. package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js +442 -0
  589. package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js.map +1 -0
  590. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +5 -1
  591. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  592. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +15 -1
  593. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
  594. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +121 -5
  595. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  596. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +12 -0
  597. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  598. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +438 -36
  599. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -1
  600. package/build/dist/Server/Utils/Telemetry/Telemetry.js +12 -0
  601. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  602. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +17 -18
  603. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  604. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +16 -10
  605. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -1
  606. package/build/dist/Types/AI/AIResourceContext.js +7 -0
  607. package/build/dist/Types/AI/AIResourceContext.js.map +1 -1
  608. package/build/dist/Types/Dashboard/DashboardTemplates.js +2 -1
  609. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  610. package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js +71 -0
  611. package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js.map +1 -0
  612. package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js +1462 -0
  613. package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js.map +1 -0
  614. package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js +691 -0
  615. package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js.map +1 -0
  616. package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js +1407 -0
  617. package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js.map +1 -0
  618. package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js +60 -0
  619. package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js.map +1 -0
  620. package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js +804 -0
  621. package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js.map +1 -0
  622. package/build/dist/Types/DatabaseServer/DatabaseSystem.js +1860 -0
  623. package/build/dist/Types/DatabaseServer/DatabaseSystem.js.map +1 -0
  624. package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js +406 -0
  625. package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js.map +1 -0
  626. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  627. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  628. package/build/dist/Types/Incident/IncidentAlertLink.js +34 -0
  629. package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -0
  630. package/build/dist/Types/Monitor/CephAlertTemplates.js +18 -0
  631. package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
  632. package/build/dist/Types/Monitor/DatabaseAlertTemplates.js +1518 -0
  633. package/build/dist/Types/Monitor/DatabaseAlertTemplates.js.map +1 -0
  634. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +57 -24
  635. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  636. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +104 -18
  637. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
  638. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +80 -3
  639. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  640. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +10 -3
  641. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  642. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +29 -0
  643. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  644. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +21 -6
  645. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -1
  646. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +25 -0
  647. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -1
  648. package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js +136 -0
  649. package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js.map +1 -0
  650. package/build/dist/Types/Permission.js +276 -0
  651. package/build/dist/Types/Permission.js.map +1 -1
  652. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +10 -0
  653. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  654. package/build/dist/Types/Rules/RuleRun.js +4 -0
  655. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  656. package/build/dist/Types/Rum/WebVitals.js +25 -0
  657. package/build/dist/Types/Rum/WebVitals.js.map +1 -1
  658. package/build/dist/Types/Telemetry/EntityType.js +23 -0
  659. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  660. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +14 -0
  661. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -1
  662. package/build/dist/Types/Telemetry/ServiceType.js +8 -0
  663. package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
  664. package/build/dist/UI/Components/Banner/Banner.js +1 -1
  665. package/build/dist/UI/Components/Banner/Banner.js.map +1 -1
  666. package/build/dist/UI/Components/Card/Card.js +1 -1
  667. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  668. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
  669. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  670. package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
  671. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  672. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +1 -1
  673. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
  674. package/build/dist/UI/Components/Date/StartAndEndDate.js +5 -5
  675. package/build/dist/UI/Components/Date/StartAndEndDate.js.map +1 -1
  676. package/build/dist/UI/Components/Detail/FieldLabel.js +2 -0
  677. package/build/dist/UI/Components/Detail/FieldLabel.js.map +1 -1
  678. package/build/dist/UI/Components/Diagram/HorizontalStepChain.js +1 -1
  679. package/build/dist/UI/Components/Diagram/HorizontalStepChain.js.map +1 -1
  680. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +1 -1
  681. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -1
  682. package/build/dist/UI/Components/Forms/BasicForm.js +1 -1
  683. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  684. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  685. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  686. package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js +1 -1
  687. package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js.map +1 -1
  688. package/build/dist/UI/Components/InfoCard/InfoCard.js +22 -1
  689. package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
  690. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +1 -1
  691. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  692. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  693. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  694. package/build/dist/UI/Components/Modal/ModalFooter.js +1 -1
  695. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  696. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
  697. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  698. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +70 -18
  699. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  700. package/build/dist/UI/Components/Navbar/NavBar.js +9 -2
  701. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  702. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
  703. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  704. package/build/dist/UI/Components/Page/Page.js +1 -1
  705. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  706. package/build/dist/UI/Components/Pagination/Pagination.js +3 -3
  707. package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
  708. package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js +1 -1
  709. package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js.map +1 -1
  710. package/build/dist/UI/Components/SideMenu/SideMenu.js +4 -1
  711. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  712. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +3 -1
  713. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -1
  714. package/build/dist/UI/Components/StatusPage/ResourceList.js +6 -6
  715. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  716. package/build/dist/UI/Components/Table/TableHeader.js +18 -2
  717. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  718. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  719. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  720. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +1 -1
  721. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  722. package/build/dist/UI/Components/Tooltip/InfoTooltip.js +28 -0
  723. package/build/dist/UI/Components/Tooltip/InfoTooltip.js.map +1 -0
  724. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +1 -1
  725. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -1
  726. package/build/dist/UI/Components/Workflow/ComponentsModal.js +2 -2
  727. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  728. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
  729. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
  730. package/build/dist/UI/Utils/Navigation.js +66 -33
  731. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  732. package/build/dist/UI/Utils/PermissionGate.js +4 -2
  733. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  734. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +7 -0
  735. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -1
  736. package/build/dist/UI/Utils/TelemetryService.js +3 -2
  737. package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
  738. package/build/dist/Utils/Incident/IncidentFromAlerts.js +306 -0
  739. package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -0
  740. package/build/dist/Utils/LabelRuleImportExport.js +2 -0
  741. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  742. package/build/dist/Utils/MetricUnitUtil.js +77 -13
  743. package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
  744. package/build/dist/Utils/Monitor/MetricValueFormatter.js +58 -13
  745. package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -1
  746. package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js +161 -0
  747. package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js.map +1 -0
  748. package/build/dist/Utils/Monitor/PlatformResourceIdentity.js +323 -0
  749. package/build/dist/Utils/Monitor/PlatformResourceIdentity.js.map +1 -0
  750. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js +270 -0
  751. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js.map +1 -0
  752. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +23 -0
  753. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
  754. package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js +61 -0
  755. package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js.map +1 -1
  756. package/build/dist/Utils/Rum/SessionTraceState.js +154 -0
  757. package/build/dist/Utils/Rum/SessionTraceState.js.map +1 -0
  758. package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js +178 -0
  759. package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js.map +1 -0
  760. package/build/dist/Utils/Telemetry/EntityKey.js +133 -0
  761. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  762. package/build/dist/Utils/ValueFormatter.js +15 -0
  763. package/build/dist/Utils/ValueFormatter.js.map +1 -1
  764. package/package.json +1 -1
  765. package/tsconfig.tests.json +7 -5
@@ -0,0 +1,1309 @@
1
+ /** @timezone UTC */
2
+
3
+ import "@testing-library/jest-dom";
4
+ import {
5
+ act,
6
+ cleanup,
7
+ fireEvent,
8
+ render,
9
+ screen,
10
+ within,
11
+ } from "@testing-library/react";
12
+ import React, { ReactElement } from "react";
13
+
14
+ /*
15
+ * The (i) beside every number on a Docker or Podman host's Overview and
16
+ * Containers pages, and whether its words are true.
17
+ *
18
+ * The pages are rendered for real over a fake analytics server, with the
19
+ * chart, the activity cards, the refresh control and the details card
20
+ * stubbed. What is under test:
21
+ *
22
+ * - every tile, chart card, consumer list, hero count and metric column
23
+ * shows an (i), and hovering it shows the text written for that number;
24
+ * - the chart cards carry their (i) while the first load is still in
25
+ * flight, not only once the data lands;
26
+ * - the numbers are what the words say they are - the tiles average only
27
+ * the last 5 minutes of the selected range (and fall back to the whole
28
+ * range), CPU is per core and runs past 100%, Processes adds containers
29
+ * up, a lone missed minute does not count as downtime, and the Containers
30
+ * table's network columns are one interface's running total.
31
+ *
32
+ * Source-level wiring (which description goes with which title) is pinned
33
+ * by App/Tests/Dashboard/ContainerHostMetricTooltipsWiring.test.ts.
34
+ */
35
+
36
+ interface ChartProps {
37
+ data: Array<{ seriesName: string; data: Array<{ x: Date; y: number }> }>;
38
+ yAxis: { legend: string; options: { max: number | "auto" } };
39
+ }
40
+
41
+ const chartRenders: Array<ChartProps> = [];
42
+
43
+ /*
44
+ * Function declarations, so they are hoisted along with the jest.mock calls
45
+ * that use them. They are only called at render time.
46
+ */
47
+ function recordChart(props: ChartProps): void {
48
+ chartRenders.push(props);
49
+ }
50
+
51
+ function stubModule(testId: string): {
52
+ __esModule: boolean;
53
+ default: () => ReactElement;
54
+ } {
55
+ return {
56
+ __esModule: true,
57
+ default: (): ReactElement => {
58
+ return React.createElement("div", { "data-testid": testId });
59
+ },
60
+ };
61
+ }
62
+
63
+ jest.mock("../../../UI/Components/Charts/Line/LineChart", () => {
64
+ return {
65
+ __esModule: true,
66
+ default: (props: ChartProps): ReactElement => {
67
+ recordChart(props);
68
+ return React.createElement("div", { "data-testid": "line-chart" });
69
+ },
70
+ };
71
+ });
72
+ jest.mock(
73
+ "../../../../App/FeatureSet/Dashboard/src/Components/ResourceActivity/ResourceActivityCards",
74
+ () => {
75
+ return stubModule("stub-activity-cards");
76
+ },
77
+ );
78
+ jest.mock(
79
+ "../../../../App/FeatureSet/Dashboard/src/Components/TelemetryResource/AutoRefreshControl",
80
+ () => {
81
+ return stubModule("stub-refresh-control");
82
+ },
83
+ );
84
+ jest.mock("../../../UI/Components/ModelDetail/CardModelDetail", () => {
85
+ return stubModule("stub-host-details");
86
+ });
87
+
88
+ import DockerHostOverview from "../../../../App/FeatureSet/Dashboard/src/Pages/Docker/View/Overview";
89
+ import PodmanHostOverview from "../../../../App/FeatureSet/Dashboard/src/Pages/Podman/View/Overview";
90
+ import DockerHostContainers from "../../../../App/FeatureSet/Dashboard/src/Pages/Docker/View/Containers";
91
+ import PodmanHostContainers from "../../../../App/FeatureSet/Dashboard/src/Pages/Podman/View/Containers";
92
+ import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
93
+ import {
94
+ CONTAINER_HOST_METRIC_DESCRIPTIONS,
95
+ ContainerHostMetric,
96
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/MetricDescriptions/ContainerHostMetricDescriptions";
97
+ import {
98
+ expectReadableDescriptionRecord,
99
+ expectTitleExplained,
100
+ } from "./MetricDescriptionRules";
101
+ import DockerHost from "../../../Models/DatabaseModels/DockerHost";
102
+ import PodmanHost from "../../../Models/DatabaseModels/PodmanHost";
103
+ import Route from "../../../Types/API/Route";
104
+ import AggregatedModel from "../../../Types/BaseDatabase/AggregatedModel";
105
+ import InBetween from "../../../Types/BaseDatabase/InBetween";
106
+ import ObjectID from "../../../Types/ObjectID";
107
+ import AnalyticsModelAPI from "../../../UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI";
108
+ import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
109
+ import Navigation from "../../../UI/Utils/Navigation";
110
+ import ProjectUtil from "../../../UI/Utils/Project";
111
+
112
+ const D: Record<ContainerHostMetric, string> =
113
+ CONTAINER_HOST_METRIC_DESCRIPTIONS;
114
+
115
+ const SENTENCE_END_PATTERN: RegExp = /[.!?](?:\s|$)/;
116
+ const PAST_100_PATTERN: RegExp = /above 100%|more than 100%/;
117
+ const HOST_MEMORY_PATTERN: RegExp = /host's total memory|host memory/;
118
+ const RUNTIME_NAME_PATTERN: RegExp = /docker|podman/i;
119
+ const SLOT_WORD_PATTERN: RegExp = /\bslots?\b/;
120
+
121
+ /*
122
+ * ---------------------------------------------------------------------------
123
+ * The words
124
+ * ---------------------------------------------------------------------------
125
+ */
126
+
127
+ /*
128
+ * Every title a customer sees, and the text its (i) shows. Tiles and charts
129
+ * share titles; a tile is one number, the chart is the whole range.
130
+ */
131
+ const TILE_TITLES: Array<[string, ContainerHostMetric]> = [
132
+ ["Containers", "containers"],
133
+ ["Avg CPU", "avgCpu"],
134
+ ["Peak CPU", "peakCpu"],
135
+ ["Avg Memory", "avgMemory"],
136
+ ["Peak Memory", "peakMemory"],
137
+ ["Processes", "processes"],
138
+ ];
139
+
140
+ const CHART_TITLES: Array<[string, ContainerHostMetric]> = [
141
+ ["Availability", "availabilityChart"],
142
+ ["Avg CPU", "avgCpuChart"],
143
+ ["Peak CPU", "peakCpuChart"],
144
+ ["Avg Memory", "avgMemoryChart"],
145
+ ["Peak Memory", "peakMemoryChart"],
146
+ ["Network", "networkChart"],
147
+ ];
148
+
149
+ const LIST_TITLES: Array<[string, ContainerHostMetric]> = [
150
+ ["Top CPU Consumers", "topCpuConsumers"],
151
+ ["Top Memory Consumers", "topMemoryConsumers"],
152
+ ];
153
+
154
+ const COLUMN_TITLES: Array<[string, ContainerHostMetric]> = [
155
+ ["CPU", "containerCpu"],
156
+ ["Memory", "containerMemory"],
157
+ ["Memory %", "containerMemoryPercent"],
158
+ ["Network RX (total)", "containerNetworkRx"],
159
+ ["Network TX (total)", "containerNetworkTx"],
160
+ ];
161
+
162
+ const ALL_TITLES: Array<[string, ContainerHostMetric]> = [
163
+ ...TILE_TITLES,
164
+ ...CHART_TITLES,
165
+ ...LIST_TITLES,
166
+ ...COLUMN_TITLES,
167
+ ];
168
+
169
+ const TILE_KEYS: Array<ContainerHostMetric> = [
170
+ "avgCpu",
171
+ "peakCpu",
172
+ "avgMemory",
173
+ "peakMemory",
174
+ "processes",
175
+ ];
176
+
177
+ const CPU_KEYS: Array<ContainerHostMetric> = [
178
+ "avgCpu",
179
+ "peakCpu",
180
+ "avgCpuChart",
181
+ "peakCpuChart",
182
+ "topCpuConsumers",
183
+ "containerCpu",
184
+ ];
185
+
186
+ const MEMORY_PERCENT_KEYS: Array<ContainerHostMetric> = [
187
+ "avgMemory",
188
+ "peakMemory",
189
+ "avgMemoryChart",
190
+ "peakMemoryChart",
191
+ "topMemoryConsumers",
192
+ "containerMemoryPercent",
193
+ ];
194
+
195
+ describe("container host metric descriptions", () => {
196
+ test("every text is a short, finished, jargon-explaining sentence, and none repeats another", () => {
197
+ expectReadableDescriptionRecord(
198
+ CONTAINER_HOST_METRIC_DESCRIPTIONS,
199
+ "CONTAINER_HOST_METRIC_DESCRIPTIONS",
200
+ );
201
+ });
202
+
203
+ test.each(ALL_TITLES)(
204
+ "the %s title is explained by its own text",
205
+ (title: string, key: ContainerHostMetric) => {
206
+ expectTitleExplained(title, D[key]);
207
+ expect(D[key].length).toBeLessThanOrEqual(260);
208
+ },
209
+ );
210
+
211
+ test("every text is one or two sentences", () => {
212
+ for (const [key, text] of Object.entries(D)) {
213
+ const sentences: number = text.split(SENTENCE_END_PATTERN).length - 1;
214
+
215
+ expect({ key, sentences: sentences >= 1 && sentences <= 2 }).toEqual({
216
+ key,
217
+ sentences: true,
218
+ });
219
+ }
220
+ });
221
+
222
+ test("every description is used by at least one title on these pages", () => {
223
+ const used: Set<string> = new Set<string>(
224
+ ALL_TITLES.map((pair: [string, ContainerHostMetric]): string => {
225
+ return pair[1];
226
+ }),
227
+ );
228
+
229
+ expect(Array.from(used).sort()).toEqual(Object.keys(D).sort());
230
+ });
231
+
232
+ describe("accuracy anchors", () => {
233
+ test.each(TILE_KEYS)(
234
+ "the %s tile says it averages only the last 5 minutes, and can fall back to the whole range",
235
+ (key: ContainerHostMetric) => {
236
+ expect(D[key]).toContain("last 5 minutes of the selected range");
237
+ /*
238
+ * The same words as the Host, Kubernetes, Proxmox and VMware tiles,
239
+ * which fall back the same way: past 12 hours a bucket is 15 minutes
240
+ * or wider, so one seldom starts in the 5-minute window.
241
+ */
242
+ expect(D[key]).toContain(
243
+ "(often the whole range on ranges over 12 hours or without recent data)",
244
+ );
245
+ expect(D[key]).not.toContain("long ranges");
246
+ },
247
+ );
248
+
249
+ test("the container count says when it counts, and that the fallback keeps stopped containers", () => {
250
+ expect(D.containers).toContain("last 5 minutes of the selected range");
251
+ expect(D.containers).toContain(
252
+ "every container seen anywhere in the range is counted",
253
+ );
254
+ expect(D.containers).toContain("including ones that have since stopped");
255
+ expect(D.containers).toContain("CPU or memory");
256
+ });
257
+
258
+ test.each(CPU_KEYS)(
259
+ "%s says 100%% is one full CPU core, not the whole host",
260
+ (key: ContainerHostMetric) => {
261
+ expect(D[key]).toContain("100% is one full CPU core");
262
+ expect(D[key]).not.toContain("of the host's CPU");
263
+ },
264
+ );
265
+
266
+ test.each(["avgCpu", "avgCpuChart", "containerCpu"] as const)(
267
+ "%s warns that the number can go past 100%%",
268
+ (key: ContainerHostMetric) => {
269
+ expect(D[key]).toMatch(PAST_100_PATTERN);
270
+ },
271
+ );
272
+
273
+ test.each(MEMORY_PERCENT_KEYS)(
274
+ "%s names both denominators: the container's limit, or host memory when there is none",
275
+ (key: ContainerHostMetric) => {
276
+ expect(D[key]).toContain("memory limit");
277
+ expect(D[key]).toMatch(HOST_MEMORY_PATTERN);
278
+ },
279
+ );
280
+
281
+ test("the Peak tiles and charts say the top container is picked per interval", () => {
282
+ expect(D.peakCpu).toContain("busiest container");
283
+ expect(D.peakCpu).toContain("picked per interval");
284
+ expect(D.peakMemory).toContain("picked per interval");
285
+ expect(D.peakCpuChart).toContain(
286
+ "different container from one interval to the next",
287
+ );
288
+ expect(D.peakMemoryChart).toContain("in each interval");
289
+ });
290
+
291
+ test("the texts say interval, like every other resource page, not time slot", () => {
292
+ for (const [key, text] of Object.entries(D)) {
293
+ expect({ key, slot: SLOT_WORD_PATTERN.test(text) }).toEqual({
294
+ key,
295
+ slot: false,
296
+ });
297
+ }
298
+ });
299
+
300
+ test("Processes says threads are counted and containers are added up", () => {
301
+ expect(D.processes).toContain("Processes and threads");
302
+ expect(D.processes).toContain("added together");
303
+ expect(D.processes).toContain("Each thread counts as one");
304
+ });
305
+
306
+ test("Availability explains Up, Down, the bridged minute and the uptime badge", () => {
307
+ expect(D.availabilityChart).toContain("Up");
308
+ expect(D.availabilityChart).toContain("Down if it sent none");
309
+ expect(D.availabilityChart).toContain(
310
+ "a lone missed minute between Up minutes counts as Up",
311
+ );
312
+ expect(D.availabilityChart).toContain("uptime badge");
313
+ expect(D.availabilityChart).toContain("still waiting for data");
314
+ });
315
+
316
+ test("Network is a combined rate in bytes per second, with both directions named", () => {
317
+ expect(D.networkChart).toContain("all added together");
318
+ expect(D.networkChart).toContain("(In)");
319
+ expect(D.networkChart).toContain("(Out)");
320
+ expect(D.networkChart).toContain("bytes per second");
321
+ });
322
+
323
+ test("the consumer lists say how many, and what they are ranked by", () => {
324
+ for (const key of ["topCpuConsumers", "topMemoryConsumers"] as const) {
325
+ /*
326
+ * The list is cut at five but shows fewer on a host with fewer
327
+ * containers, and the ranking value is an interval's average - on a
328
+ * 30-day range that slot is a whole day, not a "latest reading".
329
+ */
330
+ expect(D[key]).toContain("Up to five containers");
331
+ expect(D[key]).not.toContain("The five containers");
332
+ expect(D[key]).toContain("average in its latest interval");
333
+ expect(D[key]).not.toContain("latest value");
334
+ expect(D[key]).toContain("last 5 minutes of the selected range");
335
+ // The fallback is per host, and usual past 12 hours (see the tiles).
336
+ expect(D[key]).toContain(
337
+ "on ranges over 12 hours, often anywhere in it",
338
+ );
339
+ }
340
+ });
341
+
342
+ test("the table's network columns are running totals, not speeds, and may cover one network", () => {
343
+ for (const key of ["containerNetworkRx", "containerNetworkTx"] as const) {
344
+ expect(D[key]).toContain("running total, not a current speed");
345
+ expect(D[key]).toContain("since it last started");
346
+ expect(D[key]).toContain("only one of them");
347
+ }
348
+
349
+ expect(D.containerNetworkRx).toContain("received");
350
+ expect(D.containerNetworkTx).toContain("sent");
351
+ });
352
+
353
+ test("the table's texts speak of the last 5 minutes before now - that page has no time picker", () => {
354
+ for (const [, key] of COLUMN_TITLES) {
355
+ expect(D[key]).not.toContain("selected range");
356
+ }
357
+
358
+ expect(D.containerCpu).toContain("within the last 5 minutes");
359
+ expect(D.containerMemory).toContain("within the last 5 minutes");
360
+ /*
361
+ * Only inactive file cache is subtracted (memory.stat inactive_file),
362
+ * not all cache - so the text must not claim every freeable byte of
363
+ * cache is left out.
364
+ */
365
+ expect(D.containerMemory).toContain(
366
+ "File cache the system has not used recently",
367
+ );
368
+ expect(D.containerMemory).toContain("is not counted");
369
+ expect(D.containerMemory).not.toContain("Cached file data");
370
+ });
371
+
372
+ test("no text names a runtime, so one set serves Docker and Podman", () => {
373
+ for (const text of Object.values(D)) {
374
+ expect(text).not.toMatch(RUNTIME_NAME_PATTERN);
375
+ }
376
+ });
377
+ });
378
+ });
379
+
380
+ /*
381
+ * ---------------------------------------------------------------------------
382
+ * A fake analytics server
383
+ * ---------------------------------------------------------------------------
384
+ */
385
+
386
+ const NOW: Date = new Date("2026-09-24T12:00:30.000Z");
387
+ const HOST_ID: string = "5a1b2c3d-4e5f-4061-8273-94a5b6c7d8e9";
388
+ const PROJECT_ID: ObjectID = new ObjectID(
389
+ "7d3f1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b",
390
+ );
391
+
392
+ const pageProps: PageComponentProps = {
393
+ pageRoute: new Route("/overview"),
394
+ currentProject: null,
395
+ hasPaymentMethod: false,
396
+ };
397
+
398
+ function at(hhmm: string, seconds: number = 0): Date {
399
+ return new Date(
400
+ `2026-09-24T${hhmm}:${String(seconds).padStart(2, "0")}.000Z`,
401
+ );
402
+ }
403
+
404
+ function bucket(
405
+ hhmm: string,
406
+ container: string,
407
+ value: number,
408
+ extra?: Record<string, string>,
409
+ ): AggregatedModel {
410
+ return {
411
+ timestamp: at(hhmm),
412
+ value: value,
413
+ attributes: { "resource.container.name": container, ...(extra || {}) },
414
+ };
415
+ }
416
+
417
+ type Fixture = Record<string, Array<AggregatedModel>>;
418
+
419
+ /*
420
+ * Default range is the past 30 minutes: 11:30:30 to 12:00:30, one-minute
421
+ * buckets. The tile window is buckets STARTING at or after 11:55:30, so
422
+ * 11:56 through 12:00 - the 11:55 bucket starts before it and is out.
423
+ *
424
+ * "batch" only ran early in the range; it has stopped reporting.
425
+ */
426
+ function heartbeatsExcept(missing: Array<string>): Array<AggregatedModel> {
427
+ const rows: Array<AggregatedModel> = [];
428
+
429
+ for (let minute: number = 31; minute <= 60; minute++) {
430
+ const hhmm: string =
431
+ minute === 60 ? "12:00" : `11:${String(minute).padStart(2, "0")}`;
432
+
433
+ if (!missing.includes(hhmm)) {
434
+ rows.push({ timestamp: at(hhmm), value: 2 });
435
+ }
436
+ }
437
+
438
+ return rows;
439
+ }
440
+
441
+ const RECENT_FIXTURE: Fixture = {
442
+ "container.cpu.utilization": [
443
+ bucket("11:40", "api", 10),
444
+ bucket("11:40", "worker", 10),
445
+ bucket("11:40", "batch", 10),
446
+ bucket("11:50", "api", 10),
447
+ bucket("11:50", "worker", 10),
448
+ bucket("11:50", "batch", 10),
449
+ // Starts 30 s before the tile window opens: must not count.
450
+ bucket("11:55", "api", 1000),
451
+ bucket("11:55", "worker", 1000),
452
+ // Inside the window. api is using two and a half cores.
453
+ bucket("11:58", "api", 250),
454
+ bucket("11:58", "worker", 50),
455
+ bucket("12:00", "api", 250),
456
+ bucket("12:00", "worker", 50),
457
+ ],
458
+ "container.memory.percent": [
459
+ bucket("11:40", "api", 90),
460
+ bucket("11:40", "worker", 90),
461
+ bucket("11:40", "batch", 90),
462
+ bucket("11:58", "api", 40),
463
+ bucket("11:58", "worker", 20),
464
+ bucket("12:00", "api", 40),
465
+ bucket("12:00", "worker", 20),
466
+ ],
467
+ "container.pids.count": [
468
+ bucket("11:40", "api", 100),
469
+ bucket("11:40", "worker", 100),
470
+ bucket("11:40", "batch", 100),
471
+ bucket("11:58", "api", 12),
472
+ bucket("11:58", "worker", 30),
473
+ bucket("12:00", "api", 12),
474
+ bucket("12:00", "worker", 30),
475
+ ],
476
+ "container.network.io.usage.rx_bytes": [
477
+ bucket("11:57", "api", 0, { interface: "eth0" }),
478
+ bucket("11:58", "api", 6000, { interface: "eth0" }),
479
+ bucket("11:59", "api", 18000, { interface: "eth0" }),
480
+ // The container restarted: its counter starts again from zero.
481
+ bucket("12:00", "api", 100, { interface: "eth0" }),
482
+ bucket("11:58", "api", 0, { interface: "eth1" }),
483
+ bucket("11:59", "api", 600, { interface: "eth1" }),
484
+ bucket("11:58", "worker", 1000, { interface: "eth0" }),
485
+ bucket("11:59", "worker", 4000, { interface: "eth0" }),
486
+ ],
487
+ "container.network.io.usage.tx_bytes": [
488
+ bucket("11:58", "worker", 0, { interface: "eth0" }),
489
+ bucket("11:59", "worker", 1200, { interface: "eth0" }),
490
+ ],
491
+ // One heartbeat missing at 11:45, between two good minutes.
492
+ "oneuptime.host.heartbeat": heartbeatsExcept(["11:45"]),
493
+ };
494
+
495
+ // Nothing starts in the last 5 minutes: the tiles use the whole range.
496
+ const OLD_ONLY_FIXTURE: Fixture = {
497
+ "container.cpu.utilization": [
498
+ bucket("11:35", "batch", 5),
499
+ bucket("11:40", "api", 10),
500
+ bucket("11:40", "worker", 30),
501
+ bucket("11:50", "api", 20),
502
+ bucket("11:50", "worker", 40),
503
+ ],
504
+ "container.memory.percent": [
505
+ bucket("11:40", "api", 10),
506
+ bucket("11:40", "worker", 30),
507
+ ],
508
+ "container.pids.count": [
509
+ bucket("11:40", "api", 5),
510
+ bucket("11:40", "worker", 7),
511
+ bucket("11:50", "api", 9),
512
+ bucket("11:50", "worker", 11),
513
+ ],
514
+ "oneuptime.host.heartbeat": heartbeatsExcept([]),
515
+ };
516
+
517
+ interface AggregateRequest {
518
+ aggregateBy: {
519
+ query: {
520
+ name: string;
521
+ time: InBetween<Date>;
522
+ attributes: Record<string, string>;
523
+ };
524
+ };
525
+ }
526
+
527
+ interface ListRequest {
528
+ query: {
529
+ name: string;
530
+ time: InBetween<Date>;
531
+ attributes: Record<string, string>;
532
+ };
533
+ }
534
+
535
+ let aggregateRequests: Array<AggregateRequest>;
536
+ let listRequests: Array<ListRequest>;
537
+
538
+ interface RuntimeCase {
539
+ runtime: string;
540
+ Overview: React.FunctionComponent<PageComponentProps>;
541
+ Containers: React.FunctionComponent<PageComponentProps>;
542
+ buildHost: () => DockerHost | PodmanHost;
543
+ }
544
+
545
+ function fillHost<T extends DockerHost | PodmanHost>(host: T): T {
546
+ host._id = HOST_ID;
547
+ host.name = "web-01";
548
+ host.hostIdentifier = "web-01";
549
+ host.otelCollectorStatus = "connected";
550
+ host.osType = "linux";
551
+ host.lastSeenAt = NOW;
552
+ return host;
553
+ }
554
+
555
+ const RUNTIME_CASES: Array<[string, RuntimeCase]> = [
556
+ [
557
+ "Docker",
558
+ {
559
+ runtime: "docker",
560
+ Overview: DockerHostOverview,
561
+ Containers: DockerHostContainers,
562
+ buildHost: (): DockerHost => {
563
+ return fillHost(new DockerHost());
564
+ },
565
+ },
566
+ ],
567
+ [
568
+ "Podman",
569
+ {
570
+ runtime: "podman",
571
+ Overview: PodmanHostOverview,
572
+ Containers: PodmanHostContainers,
573
+ buildHost: (): PodmanHost => {
574
+ return fillHost(new PodmanHost());
575
+ },
576
+ },
577
+ ],
578
+ ];
579
+
580
+ function serveHost(runtimeCase: RuntimeCase): void {
581
+ const getItemSpy: jest.SpyInstance = jest.spyOn(ModelAPI, "getItem");
582
+
583
+ getItemSpy.mockImplementation((): Promise<unknown> => {
584
+ return Promise.resolve(runtimeCase.buildHost());
585
+ });
586
+ }
587
+
588
+ function serveAggregates(fixture: Fixture | "never"): void {
589
+ const aggregateSpy: jest.SpyInstance = jest.spyOn(
590
+ AnalyticsModelAPI,
591
+ "aggregate",
592
+ );
593
+
594
+ aggregateSpy.mockImplementation(
595
+ (...args: Array<unknown>): Promise<unknown> => {
596
+ const request: AggregateRequest = args[0] as AggregateRequest;
597
+ aggregateRequests.push(request);
598
+
599
+ if (fixture === "never") {
600
+ return new Promise<unknown>((): void => {});
601
+ }
602
+
603
+ return Promise.resolve({
604
+ data: fixture[request.aggregateBy.query.name] || [],
605
+ });
606
+ },
607
+ );
608
+ }
609
+
610
+ async function flush(): Promise<void> {
611
+ await act(async () => {
612
+ for (let i: number = 0; i < 20; i++) {
613
+ await Promise.resolve();
614
+ }
615
+ });
616
+ }
617
+
618
+ async function renderPage(
619
+ Page: React.FunctionComponent<PageComponentProps>,
620
+ ): Promise<void> {
621
+ // render() wraps itself in act; the flush lets the fetch chain settle.
622
+ render(<Page {...pageProps} />);
623
+ await flush();
624
+ }
625
+
626
+ /*
627
+ * Hover the (i) and read the text of the tooltip it opens. Tippy links the
628
+ * two with aria-describedby once shown; the popup never finishes animating
629
+ * out under jsdom, so reading by id (rather than "the" tooltip) keeps each
630
+ * read to its own button when several have been opened.
631
+ */
632
+ async function tooltipTextOf(button: HTMLElement): Promise<string> {
633
+ fireEvent.mouseEnter(button);
634
+ await act(async () => {
635
+ jest.advanceTimersByTime(250);
636
+ });
637
+
638
+ const describedBy: string | null = button.getAttribute("aria-describedby");
639
+ const tooltip: HTMLElement | null = describedBy
640
+ ? document.getElementById(describedBy)
641
+ : null;
642
+ const text: string = (
643
+ tooltip?.textContent ||
644
+ screen.getAllByRole("tooltip").pop()?.textContent ||
645
+ ""
646
+ ).trim();
647
+
648
+ fireEvent.mouseLeave(button);
649
+ await act(async () => {
650
+ jest.advanceTimersByTime(250);
651
+ });
652
+
653
+ return text;
654
+ }
655
+
656
+ function aboutButtons(title: string): Array<HTMLElement> {
657
+ return screen.queryAllByRole("button", { name: `About ${title}` });
658
+ }
659
+
660
+ function allAboutButtons(): Array<HTMLElement> {
661
+ return screen
662
+ .queryAllByRole("button")
663
+ .filter((button: HTMLElement): boolean => {
664
+ return (button.getAttribute("aria-label") || "").startsWith("About ");
665
+ });
666
+ }
667
+
668
+ // The tile or chart card an (i) belongs to.
669
+ function cardOf(button: HTMLElement): HTMLElement {
670
+ return button.closest("div.rounded-xl") as HTMLElement;
671
+ }
672
+
673
+ function tileValue(title: string): string {
674
+ const tile: HTMLElement = cardOf(aboutButtons(title)[0]!);
675
+
676
+ return (tile.querySelector("div.text-2xl")?.textContent || "").trim();
677
+ }
678
+
679
+ function latestChart(seriesName: string): ChartProps {
680
+ for (let i: number = chartRenders.length - 1; i >= 0; i--) {
681
+ const props: ChartProps = chartRenders[i]!;
682
+
683
+ if (
684
+ props.data.some((series: { seriesName: string }): boolean => {
685
+ return series.seriesName === seriesName;
686
+ })
687
+ ) {
688
+ return props;
689
+ }
690
+ }
691
+
692
+ throw new Error(`No chart rendered a "${seriesName}" series.`);
693
+ }
694
+
695
+ function seriesPoints(seriesName: string): Array<{ at: string; y: number }> {
696
+ const series: { seriesName: string; data: Array<{ x: Date; y: number }> } =
697
+ latestChart(seriesName).data.find((s: { seriesName: string }): boolean => {
698
+ return s.seriesName === seriesName;
699
+ })!;
700
+
701
+ return series.data.map(
702
+ (point: { x: Date; y: number }): { at: string; y: number } => {
703
+ return { at: point.x.toISOString().slice(11, 16), y: point.y };
704
+ },
705
+ );
706
+ }
707
+
708
+ function consumerRows(listTitle: string): Array<string> {
709
+ const heading: HTMLElement = aboutButtons(listTitle)[0]!.closest(
710
+ "h2",
711
+ ) as HTMLElement;
712
+ let card: HTMLElement | null = heading.parentElement;
713
+
714
+ while (card && !card.querySelector(".divide-y")) {
715
+ card = card.parentElement;
716
+ }
717
+
718
+ const list: HTMLElement = card!.querySelector(".divide-y") as HTMLElement;
719
+
720
+ // "name value" for each row: the container link, then its percentage.
721
+ return Array.from(list.children).map((row: Element): string => {
722
+ return Array.from(row.children)
723
+ .map((part: Element): string => {
724
+ return (part.textContent || "").trim();
725
+ })
726
+ .join(" ");
727
+ });
728
+ }
729
+
730
+ beforeEach(() => {
731
+ // The selected range ends at NOW; Tippy's show delay runs on these timers.
732
+ jest.useFakeTimers();
733
+ jest.setSystemTime(NOW);
734
+ chartRenders.length = 0;
735
+ aggregateRequests = [];
736
+ listRequests = [];
737
+
738
+ jest
739
+ .spyOn(Navigation, "getLastParamAsObjectID")
740
+ .mockImplementation((): ObjectID => {
741
+ return new ObjectID(HOST_ID);
742
+ });
743
+ jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(PROJECT_ID);
744
+ });
745
+
746
+ afterEach(() => {
747
+ cleanup();
748
+ jest.useRealTimers();
749
+ jest.restoreAllMocks();
750
+ window.localStorage.clear();
751
+ });
752
+
753
+ /*
754
+ * ---------------------------------------------------------------------------
755
+ * The overview
756
+ * ---------------------------------------------------------------------------
757
+ */
758
+
759
+ describe.each(RUNTIME_CASES)(
760
+ "%s host overview",
761
+ (_name: string, runtimeCase: RuntimeCase) => {
762
+ test("asks only for its own runtime's containers", async () => {
763
+ serveHost(runtimeCase);
764
+ serveAggregates(RECENT_FIXTURE);
765
+
766
+ await renderPage(runtimeCase.Overview);
767
+
768
+ const containerQueries: Array<AggregateRequest> =
769
+ aggregateRequests.filter((request: AggregateRequest): boolean => {
770
+ return request.aggregateBy.query.name.startsWith("container.");
771
+ });
772
+
773
+ expect(containerQueries.length).toBe(5);
774
+
775
+ for (const request of containerQueries) {
776
+ expect(
777
+ request.aggregateBy.query.attributes["resource.container.runtime"],
778
+ ).toBe(runtimeCase.runtime);
779
+ }
780
+ });
781
+
782
+ test("while the first load is in flight, every chart title already has its (i)", async () => {
783
+ serveHost(runtimeCase);
784
+ serveAggregates("never");
785
+
786
+ await renderPage(runtimeCase.Overview);
787
+
788
+ // The tiles are still a spinner; only the chart skeletons are drawn.
789
+ expect(aboutButtons("Processes")).toHaveLength(0);
790
+ expect(screen.queryAllByTestId("line-chart")).toHaveLength(0);
791
+
792
+ for (const [title, key] of CHART_TITLES) {
793
+ const buttons: Array<HTMLElement> = aboutButtons(title);
794
+
795
+ expect({ title, count: buttons.length }).toEqual({ title, count: 1 });
796
+ expect(await tooltipTextOf(buttons[0]!)).toBe(D[key]);
797
+ }
798
+ });
799
+
800
+ test("every summary tile explains its number", async () => {
801
+ serveHost(runtimeCase);
802
+ serveAggregates(RECENT_FIXTURE);
803
+
804
+ await renderPage(runtimeCase.Overview);
805
+
806
+ for (const [title, key] of TILE_TITLES) {
807
+ // The tile comes before the chart of the same name.
808
+ const tile: HTMLElement = aboutButtons(title)[0]!;
809
+
810
+ expect(cardOf(tile).querySelector("div.text-2xl")).not.toBeNull();
811
+ expect(await tooltipTextOf(tile)).toBe(D[key]);
812
+ }
813
+ });
814
+
815
+ test("every chart card explains its line once the data has landed", async () => {
816
+ serveHost(runtimeCase);
817
+ serveAggregates(RECENT_FIXTURE);
818
+
819
+ await renderPage(runtimeCase.Overview);
820
+
821
+ for (const [title, key] of CHART_TITLES) {
822
+ const buttons: Array<HTMLElement> = aboutButtons(title);
823
+ const chart: HTMLElement = buttons[buttons.length - 1]!;
824
+
825
+ expect(
826
+ within(cardOf(chart)).getByTestId("line-chart"),
827
+ ).toBeInTheDocument();
828
+ expect(await tooltipTextOf(chart)).toBe(D[key]);
829
+ }
830
+ });
831
+
832
+ test("both consumer lists explain how they rank", async () => {
833
+ serveHost(runtimeCase);
834
+ serveAggregates(RECENT_FIXTURE);
835
+
836
+ await renderPage(runtimeCase.Overview);
837
+
838
+ for (const [title, key] of LIST_TITLES) {
839
+ const button: HTMLElement = aboutButtons(title)[0]!;
840
+
841
+ expect(button.closest("h2")).toHaveTextContent(title);
842
+ expect(await tooltipTextOf(button)).toBe(D[key]);
843
+ }
844
+ });
845
+
846
+ test("the hero's container count explains itself; the OS chip is a plain fact", async () => {
847
+ serveHost(runtimeCase);
848
+ serveAggregates(RECENT_FIXTURE);
849
+
850
+ await renderPage(runtimeCase.Overview);
851
+
852
+ const chip: HTMLElement = aboutButtons("2 containers")[0]!;
853
+
854
+ expect(await tooltipTextOf(chip)).toBe(D.containers);
855
+
856
+ const osChip: HTMLElement = screen
857
+ .getByText("linux")
858
+ .closest("span.rounded-md") as HTMLElement;
859
+
860
+ expect(within(osChip).queryByRole("button")).toBeNull();
861
+ });
862
+
863
+ test("every number on the page has exactly one (i), and every overview text is shown", async () => {
864
+ serveHost(runtimeCase);
865
+ serveAggregates(RECENT_FIXTURE);
866
+
867
+ await renderPage(runtimeCase.Overview);
868
+
869
+ const buttons: Array<HTMLElement> = allAboutButtons();
870
+ const texts: Array<string> = [];
871
+
872
+ // 6 tiles + 6 charts + 2 lists + the hero count.
873
+ expect(buttons).toHaveLength(15);
874
+
875
+ for (const button of buttons) {
876
+ texts.push(await tooltipTextOf(button));
877
+ }
878
+
879
+ const expected: Array<string> = [
880
+ ...TILE_TITLES,
881
+ ...CHART_TITLES,
882
+ ...LIST_TITLES,
883
+ ].map((pair: [string, ContainerHostMetric]): string => {
884
+ return D[pair[1]];
885
+ });
886
+
887
+ expect(new Set(texts)).toEqual(new Set(expected));
888
+ });
889
+
890
+ test("no (i) sits inside another button or link", async () => {
891
+ serveHost(runtimeCase);
892
+ serveAggregates(RECENT_FIXTURE);
893
+
894
+ await renderPage(runtimeCase.Overview);
895
+
896
+ for (const button of allAboutButtons()) {
897
+ expect(button.parentElement!.closest("button, a")).toBeNull();
898
+ }
899
+ });
900
+
901
+ describe("the numbers are what the words say", () => {
902
+ test("the CPU tiles average only buckets that start in the last 5 minutes, and pass 100%", async () => {
903
+ serveHost(runtimeCase);
904
+ serveAggregates(RECENT_FIXTURE);
905
+
906
+ await renderPage(runtimeCase.Overview);
907
+
908
+ /*
909
+ * 11:58 and 12:00 average (250 + 50) / 2 = 150. The 11:55 bucket
910
+ * (1000%) starts before the window and the older ones are 10% - if
911
+ * either counted, this would not be 150.
912
+ */
913
+ expect(tileValue("Avg CPU")).toBe("150.0%");
914
+ // The busiest container, api, on two and a half cores.
915
+ expect(tileValue("Peak CPU")).toBe("250.0%");
916
+ });
917
+
918
+ test("the memory tiles average the recent percentages", async () => {
919
+ serveHost(runtimeCase);
920
+ serveAggregates(RECENT_FIXTURE);
921
+
922
+ await renderPage(runtimeCase.Overview);
923
+
924
+ expect(tileValue("Avg Memory")).toBe("30.0%");
925
+ expect(tileValue("Peak Memory")).toBe("40.0%");
926
+
927
+ const memoryTile: HTMLElement = cardOf(aboutButtons("Avg Memory")[0]!);
928
+
929
+ expect(memoryTile).toHaveTextContent("of limit or host memory");
930
+ expect(memoryTile).not.toHaveTextContent("of container limit");
931
+ });
932
+
933
+ test("Processes adds the containers up (12 + 30), ignoring the stopped container's old count", async () => {
934
+ serveHost(runtimeCase);
935
+ serveAggregates(RECENT_FIXTURE);
936
+
937
+ await renderPage(runtimeCase.Overview);
938
+
939
+ expect(tileValue("Processes")).toBe("42");
940
+ expect(cardOf(aboutButtons("Processes")[0]!)).toHaveTextContent(
941
+ "incl. threads",
942
+ );
943
+ });
944
+
945
+ test("Containers counts only containers that reported in the window - the stopped one is gone", async () => {
946
+ serveHost(runtimeCase);
947
+ serveAggregates(RECENT_FIXTURE);
948
+
949
+ await renderPage(runtimeCase.Overview);
950
+
951
+ expect(tileValue("Containers")).toBe("2");
952
+ expect(screen.getByText("2 containers")).toBeInTheDocument();
953
+ });
954
+
955
+ test("the consumer lists rank each container's latest value, top first", async () => {
956
+ serveHost(runtimeCase);
957
+ serveAggregates(RECENT_FIXTURE);
958
+
959
+ await renderPage(runtimeCase.Overview);
960
+
961
+ expect(consumerRows("Top CPU Consumers")).toEqual([
962
+ "api 250.0%",
963
+ "worker 50.0%",
964
+ ]);
965
+ expect(consumerRows("Top Memory Consumers")).toEqual([
966
+ "api 40.0%",
967
+ "worker 20.0%",
968
+ ]);
969
+ });
970
+
971
+ test("the consumer lists rank by each container's latest time slot, not its 5-minute average", async () => {
972
+ serveHost(runtimeCase);
973
+ serveAggregates({
974
+ ...RECENT_FIXTURE,
975
+ "container.cpu.utilization": [
976
+ bucket("11:57", "api", 10),
977
+ bucket("11:57", "worker", 90),
978
+ bucket("11:59", "api", 80),
979
+ bucket("11:59", "worker", 20),
980
+ ],
981
+ });
982
+
983
+ await renderPage(runtimeCase.Overview);
984
+
985
+ /*
986
+ * Over the window worker averages 55% and api 45%, but the list
987
+ * reads the 11:59 slot alone - so api leads, as "its latest time
988
+ * slot" says.
989
+ */
990
+ expect(consumerRows("Top CPU Consumers")).toEqual([
991
+ "api 80.0%",
992
+ "worker 20.0%",
993
+ ]);
994
+ // The tile, by contrast, is the mean of the window's slots.
995
+ expect(tileValue("Avg CPU")).toBe("50.0%");
996
+ });
997
+
998
+ test("the consumer lists stop at five, and show fewer when there are fewer containers", async () => {
999
+ const names: Array<string> = ["a", "b", "c", "d", "e", "f", "g"];
1000
+
1001
+ serveHost(runtimeCase);
1002
+ serveAggregates({
1003
+ ...RECENT_FIXTURE,
1004
+ "container.cpu.utilization": names.map(
1005
+ (name: string, index: number): AggregatedModel => {
1006
+ return bucket("11:59", name, (index + 1) * 10);
1007
+ },
1008
+ ),
1009
+ "container.memory.percent": names.map(
1010
+ (name: string, index: number): AggregatedModel => {
1011
+ return bucket("11:59", name, index + 1);
1012
+ },
1013
+ ),
1014
+ });
1015
+
1016
+ await renderPage(runtimeCase.Overview);
1017
+
1018
+ expect(consumerRows("Top CPU Consumers")).toEqual([
1019
+ "g 70.0%",
1020
+ "f 60.0%",
1021
+ "e 50.0%",
1022
+ "d 40.0%",
1023
+ "c 30.0%",
1024
+ ]);
1025
+ expect(consumerRows("Top Memory Consumers")).toHaveLength(5);
1026
+ expect(tileValue("Containers")).toBe("7");
1027
+ });
1028
+
1029
+ test("the Peak CPU chart keeps values above 100% rather than capping them", async () => {
1030
+ serveHost(runtimeCase);
1031
+ serveAggregates(RECENT_FIXTURE);
1032
+
1033
+ await renderPage(runtimeCase.Overview);
1034
+
1035
+ expect(seriesPoints("Peak CPU %")).toEqual(
1036
+ expect.arrayContaining([
1037
+ { at: "11:58", y: 250 },
1038
+ { at: "12:00", y: 250 },
1039
+ ]),
1040
+ );
1041
+ expect(seriesPoints("Avg CPU %")).toEqual(
1042
+ expect.arrayContaining([{ at: "11:58", y: 150 }]),
1043
+ );
1044
+ });
1045
+
1046
+ test("Network adds every container and interface into one bytes-per-second line per direction", async () => {
1047
+ serveHost(runtimeCase);
1048
+ serveAggregates(RECENT_FIXTURE);
1049
+
1050
+ await renderPage(runtimeCase.Overview);
1051
+
1052
+ /*
1053
+ * 11:58: api eth0 6000 B in 60 s = 100 B/s.
1054
+ * 11:59: api eth0 200 + api eth1 10 + worker 50 = 260 B/s.
1055
+ * 12:00: api's counter reset reads as 0, never negative.
1056
+ */
1057
+ expect(seriesPoints("In")).toEqual([
1058
+ { at: "11:58", y: 100 },
1059
+ { at: "11:59", y: 260 },
1060
+ { at: "12:00", y: 0 },
1061
+ ]);
1062
+ expect(seriesPoints("Out")).toEqual([{ at: "11:59", y: 20 }]);
1063
+ expect(latestChart("In").yAxis.legend).toBe("B/s");
1064
+ });
1065
+
1066
+ test("a lone missed heartbeat minute is not downtime", async () => {
1067
+ serveHost(runtimeCase);
1068
+ serveAggregates(RECENT_FIXTURE);
1069
+
1070
+ await renderPage(runtimeCase.Overview);
1071
+
1072
+ expect(screen.getByText("100.0% uptime")).toBeInTheDocument();
1073
+ });
1074
+
1075
+ test("two missed minutes in a row are downtime", async () => {
1076
+ serveHost(runtimeCase);
1077
+ serveAggregates({
1078
+ ...RECENT_FIXTURE,
1079
+ "oneuptime.host.heartbeat": heartbeatsExcept(["11:40", "11:41"]),
1080
+ });
1081
+
1082
+ await renderPage(runtimeCase.Overview);
1083
+
1084
+ // 28 of the 30 evaluated minutes were up.
1085
+ expect(screen.getByText("93.33% uptime")).toBeInTheDocument();
1086
+ });
1087
+
1088
+ test("when nothing starts in the last 5 minutes, the tiles fall back to the whole range", async () => {
1089
+ serveHost(runtimeCase);
1090
+ serveAggregates(OLD_ONLY_FIXTURE);
1091
+
1092
+ await renderPage(runtimeCase.Overview);
1093
+
1094
+ // CPU buckets average 5, 20 and 30 across the range.
1095
+ expect(tileValue("Avg CPU")).toBe("18.3%");
1096
+ expect(tileValue("Peak CPU")).toBe("25.0%");
1097
+ // (5 + 7) and (9 + 11), averaged.
1098
+ expect(tileValue("Processes")).toBe("16");
1099
+ // Everyone seen anywhere in the range, the stopped container too.
1100
+ expect(tileValue("Containers")).toBe("3");
1101
+ expect(consumerRows("Top CPU Consumers")).toEqual([
1102
+ "worker 40.0%",
1103
+ "api 20.0%",
1104
+ "batch 5.0%",
1105
+ ]);
1106
+ });
1107
+ });
1108
+ },
1109
+ );
1110
+
1111
+ /*
1112
+ * ---------------------------------------------------------------------------
1113
+ * The Containers table
1114
+ * ---------------------------------------------------------------------------
1115
+ */
1116
+
1117
+ function listRow(
1118
+ seconds: number,
1119
+ container: string,
1120
+ value: number,
1121
+ extra?: Record<string, string>,
1122
+ ): Record<string, unknown> {
1123
+ return {
1124
+ time: at("12:00", seconds),
1125
+ value: value,
1126
+ attributes: {
1127
+ "resource.container.name": container,
1128
+ "resource.container.image.name": `${container}:latest`,
1129
+ ...(extra || {}),
1130
+ },
1131
+ };
1132
+ }
1133
+
1134
+ // Newest first, as the page asks for.
1135
+ const LIST_FIXTURE: Record<string, Array<Record<string, unknown>>> = {
1136
+ "container.cpu.utilization": [
1137
+ listRow(10, "api", 250),
1138
+ listRow(5, "worker", 12.3456),
1139
+ listRow(0, "api", 90),
1140
+ ],
1141
+ "container.memory.usage.total": [
1142
+ listRow(10, "api", 1610612736),
1143
+ listRow(5, "worker", 52428800),
1144
+ ],
1145
+ "container.memory.percent": [
1146
+ listRow(10, "api", 37.5),
1147
+ listRow(5, "worker", 2.25),
1148
+ ],
1149
+ "container.network.io.usage.rx_bytes": [
1150
+ listRow(10, "api", 1000, { interface: "eth0" }),
1151
+ listRow(10, "api", 5000, { interface: "eth1" }),
1152
+ listRow(5, "worker", 2048, { interface: "eth0" }),
1153
+ ],
1154
+ "container.network.io.usage.tx_bytes": [
1155
+ listRow(10, "api", 3072, { interface: "eth0" }),
1156
+ listRow(5, "worker", 512, { interface: "eth0" }),
1157
+ ],
1158
+ };
1159
+
1160
+ function serveLists(): void {
1161
+ const getListSpy: jest.SpyInstance = jest.spyOn(AnalyticsModelAPI, "getList");
1162
+
1163
+ getListSpy.mockImplementation((...args: Array<unknown>): Promise<unknown> => {
1164
+ const request: ListRequest = args[0] as ListRequest;
1165
+ listRequests.push(request);
1166
+ const data: Array<Record<string, unknown>> =
1167
+ LIST_FIXTURE[request.query.name] || [];
1168
+
1169
+ return Promise.resolve({
1170
+ data: data,
1171
+ count: data.length,
1172
+ skip: 0,
1173
+ limit: 500,
1174
+ });
1175
+ });
1176
+ }
1177
+
1178
+ function cellsOf(containerName: string): Array<string> {
1179
+ const row: HTMLElement = screen
1180
+ .getByText(containerName)
1181
+ .closest("tr") as HTMLElement;
1182
+
1183
+ return Array.from(row.querySelectorAll("td")).map((cell: Element): string => {
1184
+ return (cell.textContent || "").trim();
1185
+ });
1186
+ }
1187
+
1188
+ describe.each(RUNTIME_CASES)(
1189
+ "%s host containers table",
1190
+ (_name: string, runtimeCase: RuntimeCase) => {
1191
+ test("every metric column header explains its values", async () => {
1192
+ serveHost(runtimeCase);
1193
+ serveLists();
1194
+
1195
+ await renderPage(runtimeCase.Containers);
1196
+
1197
+ const header: HTMLElement = screen.getAllByRole("rowgroup")[0]!;
1198
+
1199
+ expect(
1200
+ within(header)
1201
+ .getAllByRole("button")
1202
+ .filter((button: HTMLElement): boolean => {
1203
+ return (button.getAttribute("aria-label") || "").startsWith(
1204
+ "About ",
1205
+ );
1206
+ }),
1207
+ ).toHaveLength(COLUMN_TITLES.length);
1208
+
1209
+ for (const [title, key] of COLUMN_TITLES) {
1210
+ const button: HTMLElement = within(header).getByRole("button", {
1211
+ name: `About ${title}`,
1212
+ });
1213
+
1214
+ expect(button.closest("th")).toHaveTextContent(title);
1215
+ expect(await tooltipTextOf(button)).toBe(D[key]);
1216
+ }
1217
+ });
1218
+
1219
+ test("names and images are facts and carry no (i)", async () => {
1220
+ serveHost(runtimeCase);
1221
+ serveLists();
1222
+
1223
+ await renderPage(runtimeCase.Containers);
1224
+
1225
+ expect(aboutButtons("Container Name")).toHaveLength(0);
1226
+ expect(aboutButtons("Image")).toHaveLength(0);
1227
+ });
1228
+
1229
+ test("the (i) sits beside the sort button, never inside it", async () => {
1230
+ serveHost(runtimeCase);
1231
+ serveLists();
1232
+
1233
+ await renderPage(runtimeCase.Containers);
1234
+
1235
+ for (const [title] of COLUMN_TITLES) {
1236
+ const button: HTMLElement = aboutButtons(title)[0]!;
1237
+ const sortButton: HTMLElement | null =
1238
+ within(button.closest("th") as HTMLElement)
1239
+ .getAllByRole("button")
1240
+ .find((candidate: HTMLElement): boolean => {
1241
+ return candidate !== button;
1242
+ }) || null;
1243
+
1244
+ expect(button.parentElement!.closest("button, a")).toBeNull();
1245
+ expect(sortButton).toHaveTextContent(title);
1246
+ expect(sortButton!.contains(button)).toBe(false);
1247
+ }
1248
+ });
1249
+
1250
+ test("it reads the last 5 minutes before now, for its own runtime only", async () => {
1251
+ serveHost(runtimeCase);
1252
+ serveLists();
1253
+
1254
+ await renderPage(runtimeCase.Containers);
1255
+
1256
+ expect(listRequests).toHaveLength(5);
1257
+
1258
+ for (const request of listRequests) {
1259
+ expect(request.query.time.startValue).toEqual(
1260
+ new Date(NOW.getTime() - 5 * 60 * 1000),
1261
+ );
1262
+ expect(request.query.time.endValue).toEqual(NOW);
1263
+ expect(request.query.attributes["resource.container.runtime"]).toBe(
1264
+ runtimeCase.runtime,
1265
+ );
1266
+ }
1267
+ });
1268
+
1269
+ test("each cell is the container's latest reading, and CPU can pass 100%", async () => {
1270
+ serveHost(runtimeCase);
1271
+ serveLists();
1272
+
1273
+ await renderPage(runtimeCase.Containers);
1274
+
1275
+ const api: Array<string> = cellsOf("api");
1276
+
1277
+ // Name, image, CPU, memory, memory %, RX, TX, actions.
1278
+ expect(api.slice(1, 5)).toEqual([
1279
+ "api:latest",
1280
+ "250.00%",
1281
+ "1.5 GB",
1282
+ "37.50%",
1283
+ ]);
1284
+ expect(cellsOf("worker").slice(2, 5)).toEqual([
1285
+ "12.35%",
1286
+ "50.0 MB",
1287
+ "2.25%",
1288
+ ]);
1289
+ });
1290
+
1291
+ test("the network columns are one interface's running total, as their text warns", async () => {
1292
+ serveHost(runtimeCase);
1293
+ serveLists();
1294
+
1295
+ await renderPage(runtimeCase.Containers);
1296
+
1297
+ const api: Array<string> = cellsOf("api");
1298
+
1299
+ /*
1300
+ * api has eth0 (1000 B) and eth1 (5000 B). The cell is eth0's counter
1301
+ * alone - not the 5.9 KB the two add up to, and not a per-second rate.
1302
+ */
1303
+ expect(api[5]).toBe("1000 B");
1304
+ expect(api[5]).not.toBe("5.9 KB");
1305
+ expect(api[6]).toBe("3.0 KB");
1306
+ expect(cellsOf("worker")[5]).toBe("2.0 KB");
1307
+ });
1308
+ },
1309
+ );