@oneuptime/common 14.0.3 → 14.0.6

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 (663) hide show
  1. package/Models/DatabaseModels/Alert.ts +55 -0
  2. package/Models/DatabaseModels/DatabaseServer.ts +1650 -0
  3. package/Models/DatabaseModels/DatabaseServerEndpoint.ts +540 -0
  4. package/Models/DatabaseModels/DatabaseServerFeed.ts +625 -0
  5. package/Models/DatabaseModels/DatabaseServerLabelRule.ts +514 -0
  6. package/Models/DatabaseModels/DatabaseServerOwnerRule.ts +596 -0
  7. package/Models/DatabaseModels/DatabaseServerOwnerTeam.ts +491 -0
  8. package/Models/DatabaseModels/DatabaseServerOwnerUser.ts +490 -0
  9. package/Models/DatabaseModels/Incident.ts +55 -0
  10. package/Models/DatabaseModels/Index.ts +16 -0
  11. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +26 -0
  12. package/Models/DatabaseModels/ScheduledMaintenance.ts +55 -0
  13. package/Models/DatabaseModels/TelemetryException.ts +2 -0
  14. package/Models/DatabaseModels/UserProjectSsoConsent.ts +146 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.ts +673 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.ts +51 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  18. package/Server/Middleware/MultipartFormData.ts +1 -1
  19. package/Server/Middleware/TelemetryIngest.ts +15 -8
  20. package/Server/Services/AlertReminderRuleService.ts +100 -24
  21. package/Server/Services/DatabaseServerEndpointService.ts +895 -0
  22. package/Server/Services/DatabaseServerFeedService.ts +128 -0
  23. package/Server/Services/DatabaseServerLabelRuleEngineService.ts +340 -0
  24. package/Server/Services/DatabaseServerLabelRuleService.ts +14 -0
  25. package/Server/Services/DatabaseServerOwnerRuleEngineService.ts +425 -0
  26. package/Server/Services/DatabaseServerOwnerRuleService.ts +14 -0
  27. package/Server/Services/DatabaseServerOwnerTeamService.ts +180 -0
  28. package/Server/Services/DatabaseServerOwnerUserService.ts +173 -0
  29. package/Server/Services/DatabaseServerService.ts +4546 -0
  30. package/Server/Services/DatabaseService.ts +14 -8
  31. package/Server/Services/DockerResourceService.ts +43 -2
  32. package/Server/Services/IncidentReminderRuleService.ts +103 -23
  33. package/Server/Services/Index.ts +16 -0
  34. package/Server/Services/InventoryItemService.ts +113 -5
  35. package/Server/Services/IoTDeviceService.ts +145 -37
  36. package/Server/Services/MetricService.ts +1 -1
  37. package/Server/Services/OpenTelemetryIngestService.ts +19 -0
  38. package/Server/Services/PodmanResourceService.ts +43 -2
  39. package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +107 -23
  40. package/Server/Services/ServiceService.ts +1 -1
  41. package/Server/Services/TelemetryUsageBillingService.ts +29 -2
  42. package/Server/Services/UserProjectSsoConsentService.ts +74 -0
  43. package/Server/Services/UserService.ts +14 -1
  44. package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +18 -0
  45. package/Server/Types/Database/QueryHelper.ts +84 -2
  46. package/Server/Utils/AI/Toolbox/MetricTools.ts +13 -2
  47. package/Server/Utils/AI/Toolbox/ResourceTools.ts +81 -2
  48. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +1 -1
  49. package/Server/Utils/Database/AffectedResourceRelations.ts +6 -0
  50. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +33 -0
  51. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +192 -9
  52. package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
  53. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +2264 -755
  54. package/Server/Utils/Monitor/MonitorIncident.ts +28 -1
  55. package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +268 -6
  56. package/Server/Utils/Monitor/MonitorResource.ts +47 -11
  57. package/Server/Utils/Monitor/MonitorResourceContext.ts +1 -0
  58. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +239 -21
  59. package/Server/Utils/Monitor/PerSeriesResolutionRootCause.ts +43 -0
  60. package/Server/Utils/Monitor/SeriesResourceLabels.ts +291 -0
  61. package/Server/Utils/Monitor/SeriesResourceLinker.ts +111 -12
  62. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
  63. package/Server/Utils/StartServer.ts +26 -6
  64. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
  65. package/Server/Utils/Telemetry/DatabaseEndpointDiscovery.ts +688 -0
  66. package/Server/Utils/Telemetry/EntityRegistry.ts +5 -1
  67. package/Server/Utils/Telemetry/LogExceptionExtractor.ts +18 -1
  68. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +175 -12
  69. package/Server/Utils/Telemetry/ResourceFacetResolver.ts +12 -0
  70. package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +606 -36
  71. package/Server/Utils/Telemetry/Telemetry.ts +16 -0
  72. package/Server/Utils/Telemetry/TelemetryEntity.ts +22 -22
  73. package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +16 -10
  74. package/Tests/App/Accounts/SignedInRedirect.test.tsx +241 -0
  75. package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +221 -4
  76. package/Tests/App/Dashboard/AIInsightsResourcePageContext.test.ts +42 -0
  77. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +100 -0
  78. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +86 -0
  79. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +2 -0
  80. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +19 -0
  81. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +14 -0
  82. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +26 -1
  83. package/Tests/App/Dashboard/DashboardAppSsoError.test.tsx +501 -0
  84. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +64 -1
  85. package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +178 -18
  86. package/Tests/App/Dashboard/DashboardMasterPageSsoRedirect.test.tsx +444 -0
  87. package/Tests/App/Dashboard/DashboardNavBarForeignHiddenRule.test.tsx +573 -0
  88. package/Tests/App/Dashboard/DatabaseChartAxes.test.tsx +431 -0
  89. package/Tests/App/Dashboard/DatabaseContainerDetailBadge.test.tsx +249 -0
  90. package/Tests/App/Dashboard/DatabaseDashboardE2EFixes.test.ts +681 -0
  91. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +1801 -0
  92. package/Tests/App/Dashboard/DatabaseFleetSummaryRefresh.test.tsx +280 -0
  93. package/Tests/App/Dashboard/DatabaseFormHelpers.test.tsx +321 -0
  94. package/Tests/App/Dashboard/DatabaseMetricMonitorLink.test.tsx +795 -0
  95. package/Tests/App/Dashboard/DatabaseOverviewHeader.test.tsx +580 -0
  96. package/Tests/App/Dashboard/DatabaseRecommendationsEngineMetrics.test.ts +270 -0
  97. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +1486 -0
  98. package/Tests/App/Dashboard/DatabaseServerEngineMetricsFilter.test.ts +223 -0
  99. package/Tests/App/Dashboard/DatabaseServerPresentation.test.ts +435 -0
  100. package/Tests/App/Dashboard/DatabaseServerSummary.test.ts +186 -0
  101. package/Tests/App/Dashboard/DatabaseServerTelemetryQueries.test.ts +1553 -0
  102. package/Tests/App/Dashboard/DatabaseTableTruncation.test.tsx +326 -0
  103. package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +512 -0
  104. package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +774 -0
  105. package/Tests/App/Dashboard/DatabaseViewLayout.test.tsx +233 -0
  106. package/Tests/App/Dashboard/DatabaseWorkloadBadge.test.tsx +972 -0
  107. package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +5 -0
  108. package/Tests/App/Dashboard/DiscoveryReviewUnnamedHostHint.test.tsx +1061 -0
  109. package/Tests/App/Dashboard/EventOverlayScope.test.ts +147 -0
  110. package/Tests/App/Dashboard/EventOverlayScopeHook.test.tsx +1 -0
  111. package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +1 -0
  112. package/Tests/App/Dashboard/MetricsViewerDrillDown.test.tsx +310 -0
  113. package/Tests/App/Dashboard/MetricsViewerEntityScopedRows.test.tsx +495 -0
  114. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +132 -1
  115. package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +5 -0
  116. package/Tests/App/Dashboard/NetworkTopologyExplorerSearch.test.tsx +1267 -0
  117. package/Tests/App/Dashboard/ProductOrientation.test.tsx +4 -3
  118. package/Tests/App/Dashboard/RecommendationsLoadedContext.test.tsx +349 -0
  119. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +62 -0
  120. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
  121. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +14 -0
  122. package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +4 -1
  123. package/Tests/App/Dashboard/RumOverviewPage.test.tsx +127 -0
  124. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +2 -0
  125. package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +1085 -0
  126. package/Tests/App/Dashboard/SiteSearchBox.test.tsx +2110 -0
  127. package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +168 -8
  128. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +10 -0
  129. package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +2 -1
  130. package/Tests/App/StatusPage/StatusPageHeaderForeignHiddenRule.test.tsx +284 -0
  131. package/Tests/ClassStrings.ts +178 -0
  132. package/Tests/Fixtures/MobilePasskeyUser.json +1 -2
  133. package/Tests/ForeignHiddenRuleGuard.ts +2152 -0
  134. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +2250 -0
  135. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +24 -5
  136. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  137. package/Tests/RenderedMarkup.ts +518 -0
  138. package/Tests/ResponsiveVisibility.test.ts +490 -4
  139. package/Tests/ResponsiveVisibility.ts +505 -52
  140. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  141. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +541 -0
  142. package/Tests/Server/Infrastructure/Postgres/AddUserProjectSsoConsentMigration.test.ts +234 -0
  143. package/Tests/Server/Services/ContainerSnapshotUpsertPostgres.test.ts +319 -0
  144. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +13 -0
  145. package/Tests/Server/Services/DatabaseServerEndpointService.test.ts +2033 -0
  146. package/Tests/Server/Services/DatabaseServerFeedAndOwnerServices.test.ts +529 -0
  147. package/Tests/Server/Services/DatabaseServerLifecycle.test.ts +3302 -0
  148. package/Tests/Server/Services/DatabaseServerRegistries.test.ts +603 -0
  149. package/Tests/Server/Services/DatabaseServerRuleEngines.test.ts +754 -0
  150. package/Tests/Server/Services/DatabaseServerService.test.ts +3558 -0
  151. package/Tests/Server/Services/DatabaseServerSqlPostgres.test.ts +2045 -0
  152. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedPostgres.test.ts +362 -0
  153. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedReply.test.ts +210 -0
  154. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +18 -0
  155. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +2 -0
  156. package/Tests/Server/Services/IncomingCallPolicyEscalationRuleTarget.test.ts +225 -0
  157. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +280 -2
  158. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +8 -2
  159. package/Tests/Server/Services/IoTDeviceLatestMetricsPostgres.test.ts +689 -0
  160. package/Tests/Server/Services/IoTDeviceService.test.ts +379 -0
  161. package/Tests/Server/Services/LatestMetricsValuesShape.test.ts +360 -0
  162. package/Tests/Server/Services/LlmModelPriceService.test.ts +402 -0
  163. package/Tests/Server/Services/MetricServiceAggregate.test.ts +12 -0
  164. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +7 -1
  165. package/Tests/Server/Services/OwnerRowUniqueness.test.ts +61 -9
  166. package/Tests/Server/Services/ReminderRuleLookupPostgres.test.ts +2250 -0
  167. package/Tests/Server/Services/ReminderRuleRefreshOpenSubjectsPaging.test.ts +653 -0
  168. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +68 -20
  169. package/Tests/Server/Services/SavedViewDefaults.test.ts +325 -0
  170. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
  171. package/Tests/Server/Services/StatusPageCertificateService.test.ts +169 -0
  172. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +191 -0
  173. package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +9 -0
  174. package/Tests/Server/Services/UserProjectSsoConsentPostgres.test.ts +153 -0
  175. package/Tests/Server/Services/UserProjectSsoConsentService.test.ts +192 -0
  176. package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +71 -0
  177. package/Tests/Server/Services/UserSignupIdTakeoverPostgres.test.ts +238 -0
  178. package/Tests/Server/Services/WorkspaceNotificationSummaryHelpers.test.ts +592 -0
  179. package/Tests/Server/Types/Billing/PayAsYouGoMeteredAuthorization.test.ts +3 -0
  180. package/Tests/Server/Types/Database/EventOverlayScopePostgres.test.ts +1 -0
  181. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +11 -0
  182. package/Tests/Server/Types/Database/Permissions/InfrastructureOwnershipScope.test.ts +8 -0
  183. package/Tests/Server/Types/Database/QueryHelperBooleanForCriteriaBackedRule.test.ts +1094 -0
  184. package/Tests/Server/Types/Database/QueryHelperSearchAllWords.test.ts +651 -0
  185. package/Tests/Server/Utils/AI/AlertWriteTools.test.ts +256 -0
  186. package/Tests/Server/Utils/AI/IncidentWriteTools.test.ts +482 -0
  187. package/Tests/Server/Utils/AI/ResourceTools.test.ts +134 -0
  188. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +54 -0
  189. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +41 -0
  190. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +279 -9
  191. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaDisplayContext.test.ts +933 -0
  192. package/Tests/Server/Utils/Monitor/DatabaseMaintenanceSuppression.test.ts +582 -0
  193. package/Tests/Server/Utils/Monitor/DatabaseServerMonitorLinking.test.ts +753 -0
  194. package/Tests/Server/Utils/Monitor/MetricDecimalThreshold.test.ts +417 -0
  195. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +1 -0
  196. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +1 -0
  197. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +119 -0
  198. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +60 -25
  199. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +1815 -0
  200. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesAffectedResources.test.ts +1447 -0
  201. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesRatioEmail.test.ts +352 -0
  202. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformMetricUnits.test.ts +717 -0
  203. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformRawScan.test.ts +2125 -0
  204. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformSeriesAffectedResources.test.ts +1288 -0
  205. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +1 -0
  206. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +1 -0
  207. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +1 -0
  208. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +88 -0
  209. package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +205 -0
  210. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +62 -0
  211. package/Tests/Server/Utils/Monitor/MonitorResourceDatabaseMaintenance.test.ts +803 -0
  212. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +133 -3
  213. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +1 -0
  214. package/Tests/Server/Utils/Monitor/PerSeriesRecoveryResolution.test.ts +472 -1
  215. package/Tests/Server/Utils/Monitor/PerSeriesSecondEntityAlerting.test.ts +1 -0
  216. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +236 -2
  217. package/Tests/Server/Utils/SeriesResourceLabels.test.ts +70 -0
  218. package/Tests/Server/Utils/StartServerProtobufContentType.test.ts +54 -0
  219. package/Tests/Server/Utils/Telemetry/DatabaseEndpointDiscovery.test.ts +1895 -0
  220. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +7 -0
  221. package/Tests/Server/Utils/Telemetry/LogExceptionExtractorSeverityGate.test.ts +90 -0
  222. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +570 -0
  223. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +97 -2
  224. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +1337 -0
  225. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +295 -0
  226. package/Tests/Types/AI/AIResourceContext.test.ts +30 -0
  227. package/Tests/Types/BaseDatabase/AggregationType.test.ts +5 -1
  228. package/Tests/Types/Dashboard/KubernetesDashboardTemplate.test.ts +184 -0
  229. package/Tests/Types/Dashboard/KubernetesDashboardTemplates.test.ts +107 -0
  230. package/Tests/Types/DatabaseServer/DatabaseContainerClassifier.test.ts +3644 -0
  231. package/Tests/Types/DatabaseServer/DatabaseContainerCommand.test.ts +340 -0
  232. package/Tests/Types/DatabaseServer/DatabaseContainerCommandCorpus.ts +826 -0
  233. package/Tests/Types/DatabaseServer/DatabaseContainerCommandPostgres.test.ts +150 -0
  234. package/Tests/Types/DatabaseServer/DatabaseEndpoint.test.ts +2657 -0
  235. package/Tests/Types/DatabaseServer/DatabaseServerDiscoverySource.test.ts +64 -0
  236. package/Tests/Types/DatabaseServer/DatabaseServerEntityKeys.test.ts +952 -0
  237. package/Tests/Types/DatabaseServer/DatabaseServerMetricCatalog.test.ts +1161 -0
  238. package/Tests/Types/DatabaseServer/DatabaseSystem.test.ts +1514 -0
  239. package/Tests/Types/DatabaseServer/DatabaseTelemetryResolver.test.ts +1558 -0
  240. package/Tests/Types/Monitor/CephAlertTemplates.test.ts +255 -6
  241. package/Tests/Types/Monitor/DatabaseAlertTemplates.test.ts +2254 -0
  242. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +292 -0
  243. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +272 -0
  244. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +253 -9
  245. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +13 -5
  246. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +4 -2
  247. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +111 -2
  248. package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +61 -0
  249. package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +114 -0
  250. package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +202 -0
  251. package/Tests/Types/NetworkDevice/DiscoveredHostNamingStatus.test.ts +401 -0
  252. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +4 -1
  253. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +24 -2
  254. package/Tests/UI/Components/Card.test.tsx +93 -2
  255. package/Tests/UI/Components/FeedOptions.test.ts +6 -0
  256. package/Tests/UI/Components/FeedOptionsButton.test.tsx +8 -1
  257. package/Tests/UI/Components/ForeignHiddenRuleResilience.test.tsx +1147 -0
  258. package/Tests/UI/Components/HeaderRightRail.test.tsx +41 -1
  259. package/Tests/UI/Components/LogsEntityNames.test.ts +16 -1
  260. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +7 -3
  261. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +2 -1
  262. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  263. package/Tests/UI/Components/SrOnlyForeignRule.test.tsx +994 -0
  264. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +34 -1
  265. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +4 -1
  266. package/Tests/UI/ForeignHiddenRuleGuard.test.ts +1951 -0
  267. package/Tests/UI/Rum/ChunkLoader.test.ts +76 -0
  268. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +19 -1
  269. package/Tests/UI/Rum/ReplayRailDetail.test.tsx +53 -0
  270. package/Tests/UI/Rum/ReplaySignals.test.ts +54 -0
  271. package/Tests/UI/Rum/ReplayTabSwitcher.test.tsx +19 -1
  272. package/Tests/UI/TailwindPlayCdnForeignHiddenRule.test.ts +1005 -0
  273. package/Tests/UI/ThemeGroupHoverReveal.test.ts +847 -0
  274. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +70 -0
  275. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +21 -0
  276. package/Tests/UI/Utils/ModelAPISsoRedirect.test.ts +127 -0
  277. package/Tests/UI/Utils/Navigation.test.ts +102 -0
  278. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +23 -0
  279. package/Tests/Utils/Dashboard/KubernetesClusterCpuTileRepair.test.ts +370 -0
  280. package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
  281. package/Tests/Utils/MetricUnitUtil.test.ts +227 -2
  282. package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +288 -2
  283. package/Tests/Utils/Monitor/PlatformResourceIdentityAndUnits.test.ts +1559 -0
  284. package/Tests/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.test.ts +2546 -0
  285. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +315 -0
  286. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +336 -1
  287. package/Tests/Utils/NetworkDiscovery/NetbiosNameUtil.test.ts +244 -0
  288. package/Tests/Utils/Rules/MonitorTypeRuleCriteria.test.ts +181 -0
  289. package/Tests/Utils/ValueFormatter.test.ts +50 -0
  290. package/Tests/tsconfig.json +16 -0
  291. package/Types/AI/AIResourceContext.ts +7 -0
  292. package/Types/BaseDatabase/AggregationType.ts +7 -0
  293. package/Types/Dashboard/DashboardTemplates.ts +22 -8
  294. package/Types/DatabaseServer/DatabaseConnectionSpan.ts +77 -0
  295. package/Types/DatabaseServer/DatabaseContainerClassifier.ts +2068 -0
  296. package/Types/DatabaseServer/DatabaseContainerCommand.ts +801 -0
  297. package/Types/DatabaseServer/DatabaseEndpoint.ts +1943 -0
  298. package/Types/DatabaseServer/DatabaseServerDiscoverySource.ts +68 -0
  299. package/Types/DatabaseServer/DatabaseServerMetricCatalog.ts +966 -0
  300. package/Types/DatabaseServer/DatabaseSystem.ts +2124 -0
  301. package/Types/DatabaseServer/DatabaseTelemetryResolver.ts +555 -0
  302. package/Types/Email/EmailTemplateType.ts +1 -0
  303. package/Types/Monitor/CephAlertTemplates.ts +18 -0
  304. package/Types/Monitor/DatabaseAlertTemplates.ts +2020 -0
  305. package/Types/Monitor/KubernetesAlertTemplates.ts +64 -24
  306. package/Types/Monitor/KubernetesMetricCatalog.ts +118 -19
  307. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +14 -0
  308. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +93 -10
  309. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +109 -8
  310. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +36 -7
  311. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +43 -2
  312. package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +41 -6
  313. package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +26 -0
  314. package/Types/NetworkDevice/DiscoveredHostNamingStatus.ts +147 -0
  315. package/Types/Permission.ts +267 -0
  316. package/Types/Probe/ProbeApiIngestResponse.ts +8 -0
  317. package/Types/Rules/RuleCriteriaFieldRegistry.ts +10 -0
  318. package/Types/Rules/RuleRun.ts +12 -0
  319. package/Types/Rum/SessionReplayCustomEvents.ts +29 -3
  320. package/Types/Rum/WebVitals.ts +13 -0
  321. package/Types/Telemetry/EntityType.ts +23 -0
  322. package/Types/Telemetry/ResourceFacetCatalog.ts +14 -0
  323. package/Types/Telemetry/ServiceType.ts +8 -0
  324. package/UI/Components/Banner/Banner.tsx +1 -1
  325. package/UI/Components/Card/Card.tsx +1 -1
  326. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +2 -2
  327. package/UI/Components/Charts/Line/LineChart.tsx +7 -0
  328. package/UI/Components/CommandPalette/CommandPalette.tsx +1 -1
  329. package/UI/Components/Date/StartAndEndDate.tsx +5 -5
  330. package/UI/Components/Diagram/HorizontalStepChain.tsx +1 -1
  331. package/UI/Components/Feed/FeedOptionsButton.tsx +1 -1
  332. package/UI/Components/Forms/BasicForm.tsx +1 -1
  333. package/UI/Components/Header/Header.tsx +1 -1
  334. package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -1
  335. package/UI/Components/HeaderAlert/HeaderAlert.tsx +1 -1
  336. package/UI/Components/Input/Input.tsx +11 -0
  337. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +1 -1
  338. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +1 -1
  339. package/UI/Components/Modal/ModalFooter.tsx +1 -1
  340. package/UI/Components/ModelDetail/CardModelDetail.tsx +2 -2
  341. package/UI/Components/ModelTable/BaseModelTable.tsx +4 -4
  342. package/UI/Components/Navbar/NavBar.tsx +9 -2
  343. package/UI/Components/Navbar/NavBarMenuModal.tsx +2 -2
  344. package/UI/Components/Page/Page.tsx +1 -1
  345. package/UI/Components/Pagination/Pagination.tsx +3 -3
  346. package/UI/Components/ProgressButtons/ProgressButtonItem.tsx +1 -1
  347. package/UI/Components/SideMenu/SideMenu.tsx +4 -1
  348. package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +4 -2
  349. package/UI/Components/StatusPage/ResourceList.tsx +6 -6
  350. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +1 -1
  351. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +1 -1
  352. package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +1 -1
  353. package/UI/Components/Workflow/ComponentsModal.tsx +2 -2
  354. package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
  355. package/UI/Styles/Theme.css +8 -1
  356. package/UI/Utils/Navigation.ts +89 -42
  357. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +7 -0
  358. package/UI/Utils/TelemetryService.ts +4 -3
  359. package/Utils/Dashboard/KubernetesClusterCpuTileRepair.ts +147 -0
  360. package/Utils/LabelRuleImportExport.ts +2 -0
  361. package/Utils/MetricUnitUtil.ts +103 -11
  362. package/Utils/Monitor/MetricValueFormatter.ts +66 -13
  363. package/Utils/Monitor/PlatformMetricUnitUtil.ts +215 -0
  364. package/Utils/Monitor/PlatformResourceIdentity.ts +514 -0
  365. package/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.ts +425 -0
  366. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +32 -0
  367. package/Utils/NetworkDiscovery/NetbiosNameUtil.ts +73 -0
  368. package/Utils/Telemetry/DatabaseServerEntityKeys.ts +275 -0
  369. package/Utils/Telemetry/EntityKey.ts +176 -0
  370. package/Utils/ValueFormatter.ts +16 -0
  371. package/build/dist/Models/DatabaseModels/Alert.js +54 -0
  372. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  373. package/build/dist/Models/DatabaseModels/DatabaseServer.js +1683 -0
  374. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -0
  375. package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js +559 -0
  376. package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js.map +1 -0
  377. package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js +648 -0
  378. package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js.map +1 -0
  379. package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js +522 -0
  380. package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js.map +1 -0
  381. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js +603 -0
  382. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js.map +1 -0
  383. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js +506 -0
  384. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js.map +1 -0
  385. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js +505 -0
  386. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js.map +1 -0
  387. package/build/dist/Models/DatabaseModels/Incident.js +54 -0
  388. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  389. package/build/dist/Models/DatabaseModels/Index.js +16 -0
  390. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  391. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  392. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +54 -0
  393. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  394. package/build/dist/Models/DatabaseModels/TelemetryException.js +2 -0
  395. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  396. package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js +162 -0
  397. package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js.map +1 -0
  398. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js +240 -0
  399. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js.map +1 -0
  400. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js +26 -0
  401. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js.map +1 -0
  402. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  403. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  404. package/build/dist/Server/Middleware/MultipartFormData.js +1 -1
  405. package/build/dist/Server/Middleware/TelemetryIngest.js +15 -8
  406. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  407. package/build/dist/Server/Services/AlertReminderRuleService.js +72 -23
  408. package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
  409. package/build/dist/Server/Services/DatabaseServerEndpointService.js +694 -0
  410. package/build/dist/Server/Services/DatabaseServerEndpointService.js.map +1 -0
  411. package/build/dist/Server/Services/DatabaseServerFeedService.js +110 -0
  412. package/build/dist/Server/Services/DatabaseServerFeedService.js.map +1 -0
  413. package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js +272 -0
  414. package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js.map +1 -0
  415. package/build/dist/Server/Services/DatabaseServerLabelRuleService.js +13 -0
  416. package/build/dist/Server/Services/DatabaseServerLabelRuleService.js.map +1 -0
  417. package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js +340 -0
  418. package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js.map +1 -0
  419. package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js +13 -0
  420. package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js.map +1 -0
  421. package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js +160 -0
  422. package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js.map +1 -0
  423. package/build/dist/Server/Services/DatabaseServerOwnerUserService.js +153 -0
  424. package/build/dist/Server/Services/DatabaseServerOwnerUserService.js.map +1 -0
  425. package/build/dist/Server/Services/DatabaseServerService.js +3272 -0
  426. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -0
  427. package/build/dist/Server/Services/DatabaseService.js +8 -4
  428. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  429. package/build/dist/Server/Services/DockerResourceService.js +30 -3
  430. package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
  431. package/build/dist/Server/Services/IncidentReminderRuleService.js +73 -23
  432. package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
  433. package/build/dist/Server/Services/Index.js +16 -0
  434. package/build/dist/Server/Services/Index.js.map +1 -1
  435. package/build/dist/Server/Services/InventoryItemService.js +88 -5
  436. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  437. package/build/dist/Server/Services/IoTDeviceService.js +111 -24
  438. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  439. package/build/dist/Server/Services/MetricService.js +1 -1
  440. package/build/dist/Server/Services/OpenTelemetryIngestService.js +24 -9
  441. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  442. package/build/dist/Server/Services/PodmanResourceService.js +30 -3
  443. package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
  444. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +75 -23
  445. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
  446. package/build/dist/Server/Services/ServiceService.js +1 -1
  447. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  448. package/build/dist/Server/Services/TelemetryUsageBillingService.js +23 -2
  449. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  450. package/build/dist/Server/Services/UserProjectSsoConsentService.js +79 -0
  451. package/build/dist/Server/Services/UserProjectSsoConsentService.js.map +1 -0
  452. package/build/dist/Server/Services/UserService.js +13 -1
  453. package/build/dist/Server/Services/UserService.js.map +1 -1
  454. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +18 -0
  455. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
  456. package/build/dist/Server/Types/Database/QueryHelper.js +73 -2
  457. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  458. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +13 -2
  459. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -1
  460. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +58 -1
  461. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  462. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +1 -1
  463. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +6 -0
  464. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -1
  465. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +31 -0
  466. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  467. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +135 -14
  468. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  469. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -6
  470. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  471. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1489 -524
  472. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  473. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +22 -6
  474. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  475. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +196 -6
  476. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
  477. package/build/dist/Server/Utils/Monitor/MonitorResource.js +36 -9
  478. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  479. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +1 -0
  480. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -1
  481. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +175 -17
  482. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
  483. package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js +29 -0
  484. package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js.map +1 -0
  485. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +211 -0
  486. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  487. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +82 -10
  488. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  489. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
  490. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  491. package/build/dist/Server/Utils/StartServer.js +20 -4
  492. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  493. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
  494. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
  495. package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js +442 -0
  496. package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js.map +1 -0
  497. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +5 -1
  498. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  499. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +15 -1
  500. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
  501. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +121 -5
  502. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  503. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +12 -0
  504. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  505. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +438 -36
  506. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -1
  507. package/build/dist/Server/Utils/Telemetry/Telemetry.js +12 -0
  508. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  509. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +17 -18
  510. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  511. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +16 -10
  512. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -1
  513. package/build/dist/Types/AI/AIResourceContext.js +7 -0
  514. package/build/dist/Types/AI/AIResourceContext.js.map +1 -1
  515. package/build/dist/Types/BaseDatabase/AggregationType.js +7 -0
  516. package/build/dist/Types/BaseDatabase/AggregationType.js.map +1 -1
  517. package/build/dist/Types/Dashboard/DashboardTemplates.js +22 -8
  518. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  519. package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js +71 -0
  520. package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js.map +1 -0
  521. package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js +1462 -0
  522. package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js.map +1 -0
  523. package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js +691 -0
  524. package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js.map +1 -0
  525. package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js +1407 -0
  526. package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js.map +1 -0
  527. package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js +60 -0
  528. package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js.map +1 -0
  529. package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js +804 -0
  530. package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js.map +1 -0
  531. package/build/dist/Types/DatabaseServer/DatabaseSystem.js +1860 -0
  532. package/build/dist/Types/DatabaseServer/DatabaseSystem.js.map +1 -0
  533. package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js +406 -0
  534. package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js.map +1 -0
  535. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  536. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  537. package/build/dist/Types/Monitor/CephAlertTemplates.js +18 -0
  538. package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
  539. package/build/dist/Types/Monitor/DatabaseAlertTemplates.js +1518 -0
  540. package/build/dist/Types/Monitor/DatabaseAlertTemplates.js.map +1 -0
  541. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +57 -24
  542. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  543. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +104 -18
  544. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
  545. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +80 -3
  546. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  547. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +10 -3
  548. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  549. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +29 -0
  550. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  551. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +21 -6
  552. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -1
  553. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +25 -0
  554. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -1
  555. package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js +136 -0
  556. package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js.map +1 -0
  557. package/build/dist/Types/Permission.js +234 -0
  558. package/build/dist/Types/Permission.js.map +1 -1
  559. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +10 -0
  560. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  561. package/build/dist/Types/Rules/RuleRun.js +4 -0
  562. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  563. package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -1
  564. package/build/dist/Types/Rum/WebVitals.js +12 -0
  565. package/build/dist/Types/Rum/WebVitals.js.map +1 -1
  566. package/build/dist/Types/Telemetry/EntityType.js +23 -0
  567. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  568. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +14 -0
  569. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -1
  570. package/build/dist/Types/Telemetry/ServiceType.js +8 -0
  571. package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
  572. package/build/dist/UI/Components/Banner/Banner.js +1 -1
  573. package/build/dist/UI/Components/Banner/Banner.js.map +1 -1
  574. package/build/dist/UI/Components/Card/Card.js +1 -1
  575. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  576. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
  577. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  578. package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
  579. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  580. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +1 -1
  581. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
  582. package/build/dist/UI/Components/Date/StartAndEndDate.js +5 -5
  583. package/build/dist/UI/Components/Date/StartAndEndDate.js.map +1 -1
  584. package/build/dist/UI/Components/Diagram/HorizontalStepChain.js +1 -1
  585. package/build/dist/UI/Components/Diagram/HorizontalStepChain.js.map +1 -1
  586. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +1 -1
  587. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -1
  588. package/build/dist/UI/Components/Forms/BasicForm.js +1 -1
  589. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  590. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  591. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  592. package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js +1 -1
  593. package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js.map +1 -1
  594. package/build/dist/UI/Components/Input/Input.js +1 -1
  595. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  596. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +1 -1
  597. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  598. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  599. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  600. package/build/dist/UI/Components/Modal/ModalFooter.js +1 -1
  601. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  602. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
  603. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  604. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +4 -4
  605. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  606. package/build/dist/UI/Components/Navbar/NavBar.js +9 -2
  607. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  608. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
  609. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  610. package/build/dist/UI/Components/Page/Page.js +1 -1
  611. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  612. package/build/dist/UI/Components/Pagination/Pagination.js +3 -3
  613. package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
  614. package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js +1 -1
  615. package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js.map +1 -1
  616. package/build/dist/UI/Components/SideMenu/SideMenu.js +4 -1
  617. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  618. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +3 -1
  619. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -1
  620. package/build/dist/UI/Components/StatusPage/ResourceList.js +6 -6
  621. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  622. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  623. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  624. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +1 -1
  625. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  626. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +1 -1
  627. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -1
  628. package/build/dist/UI/Components/Workflow/ComponentsModal.js +2 -2
  629. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  630. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
  631. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
  632. package/build/dist/UI/Utils/Navigation.js +66 -33
  633. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  634. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +7 -0
  635. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -1
  636. package/build/dist/UI/Utils/TelemetryService.js +3 -2
  637. package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
  638. package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js +110 -0
  639. package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js.map +1 -0
  640. package/build/dist/Utils/LabelRuleImportExport.js +2 -0
  641. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  642. package/build/dist/Utils/MetricUnitUtil.js +77 -13
  643. package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
  644. package/build/dist/Utils/Monitor/MetricValueFormatter.js +58 -13
  645. package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -1
  646. package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js +161 -0
  647. package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js.map +1 -0
  648. package/build/dist/Utils/Monitor/PlatformResourceIdentity.js +323 -0
  649. package/build/dist/Utils/Monitor/PlatformResourceIdentity.js.map +1 -0
  650. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js +270 -0
  651. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js.map +1 -0
  652. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +23 -0
  653. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
  654. package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js +61 -0
  655. package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js.map +1 -1
  656. package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js +178 -0
  657. package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js.map +1 -0
  658. package/build/dist/Utils/Telemetry/EntityKey.js +133 -0
  659. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  660. package/build/dist/Utils/ValueFormatter.js +15 -0
  661. package/build/dist/Utils/ValueFormatter.js.map +1 -1
  662. package/package.json +1 -1
  663. package/tsconfig.tests.json +7 -5
@@ -0,0 +1,1486 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import {
11
+ cleanup,
12
+ fireEvent,
13
+ render,
14
+ screen,
15
+ waitFor,
16
+ within,
17
+ } from "@testing-library/react";
18
+ import * as React from "react";
19
+ import { MemoryRouter } from "react-router-dom";
20
+ import getJestMockFunction, { MockFunction } from "../../MockType";
21
+ import { PROJECT_ID, goTo } from "./SideMenuHarness";
22
+
23
+ /*
24
+ * The Databases components other pages render: the chip for the
25
+ * databaseServers relation, the "no telemetry scope" banner, the calling
26
+ * services table and the engine-metrics section of the Overview. Rendered
27
+ * for real (charts replaced by a marker) on a project URL so every link is
28
+ * a concrete, project-scoped href.
29
+ */
30
+
31
+ const chartCardMock: MockFunction = getJestMockFunction();
32
+ const aggregateMock: MockFunction = getJestMockFunction();
33
+ const getListAnalyticsMock: MockFunction = getJestMockFunction();
34
+
35
+ jest.mock(
36
+ "../../../../App/FeatureSet/Dashboard/src/Components/TelemetryResource/ChartCard",
37
+ () => {
38
+ return {
39
+ __esModule: true,
40
+ default: (props: { title: string }) => {
41
+ chartCardMock(props);
42
+ return <div data-testid="chart-card">{props.title}</div>;
43
+ },
44
+ };
45
+ },
46
+ );
47
+
48
+ // The arrow wrapper is load bearing: jest.mock is hoisted above the mock.
49
+ jest.mock("../../../UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI", () => {
50
+ return {
51
+ __esModule: true,
52
+ default: {
53
+ aggregate: (...args: Array<unknown>) => {
54
+ return aggregateMock(...args);
55
+ },
56
+ getList: (...args: Array<unknown>) => {
57
+ return getListAnalyticsMock(...args);
58
+ },
59
+ },
60
+ };
61
+ });
62
+
63
+ jest.mock("../../../UI/Components/Modal/Modal", () => {
64
+ return {
65
+ __esModule: true,
66
+ ModalWidth: { Normal: 0, Medium: 1, Large: 2 },
67
+ default: (props: {
68
+ title: string;
69
+ description?: string;
70
+ children?: React.ReactNode;
71
+ onClose?: () => void;
72
+ }) => {
73
+ return (
74
+ <section role="dialog" aria-label={props.title}>
75
+ <p>{props.description}</p>
76
+ {props.children}
77
+ <button onClick={props.onClose}>Close</button>
78
+ </section>
79
+ );
80
+ },
81
+ };
82
+ });
83
+
84
+ jest.mock(
85
+ "../../../UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker",
86
+ () => {
87
+ return {
88
+ __esModule: true,
89
+ default: (props: { value?: { range?: string } }) => {
90
+ return (
91
+ <div data-testid="time-range-picker">{props.value?.range || ""}</div>
92
+ );
93
+ },
94
+ };
95
+ },
96
+ );
97
+
98
+ jest.mock("../../../UI/Components/ComponentLoader/ComponentLoader", () => {
99
+ return {
100
+ __esModule: true,
101
+ default: () => {
102
+ return <div data-testid="component-loader" />;
103
+ },
104
+ };
105
+ });
106
+
107
+ jest.mock("../../../UI/Components/Card/Card", () => {
108
+ return {
109
+ __esModule: true,
110
+ default: (props: {
111
+ title?: string;
112
+ description?: string;
113
+ children?: React.ReactNode;
114
+ }) => {
115
+ return (
116
+ <section data-testid="card" aria-label={props.title}>
117
+ <p>{props.description}</p>
118
+ {props.children}
119
+ </section>
120
+ );
121
+ },
122
+ };
123
+ });
124
+
125
+ import DatabaseServerElement from "../../../../App/FeatureSet/Dashboard/src/Components/DatabaseServer/DatabaseServerElement";
126
+ import DatabaseServersElement from "../../../../App/FeatureSet/Dashboard/src/Components/DatabaseServer/DatabaseServersElement";
127
+ import DatabaseServerUnscopedBanner, {
128
+ DATABASE_SERVER_ID_ONLY_TITLE,
129
+ getDatabaseServerScopeBannerDescription,
130
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/DatabaseServer/DatabaseServerUnscopedBanner";
131
+ import { isDatabaseServerScopedByIdOnly } from "../../../../App/FeatureSet/Dashboard/src/Pages/Database/Utils/DatabaseTelemetryScope";
132
+ import DatabaseCallingServicesCard, {
133
+ getCallingServicesFooter,
134
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/DatabaseServer/DatabaseCallingServicesCard";
135
+ import DatabaseEngineMetricsSection, {
136
+ ENGINE_METRICS_DISCONNECTED_TITLE,
137
+ ENGINE_METRICS_NO_CURATED_OVERVIEW_TITLE,
138
+ ENGINE_METRICS_NO_DATA_TITLE,
139
+ ENGINE_METRICS_NOT_CONNECTED_TITLE,
140
+ EngineMetricsGuidance,
141
+ getEngineMetricsGuidance,
142
+ getEngineMetricsMissingDescription,
143
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/DatabaseServer/DatabaseEngineMetricsSection";
144
+ import DatabaseMetricChartModal from "../../../../App/FeatureSet/Dashboard/src/Components/DatabaseServer/DatabaseMetricChartModal";
145
+ import DatabaseRuntimeSection from "../../../../App/FeatureSet/Dashboard/src/Components/DatabaseServer/DatabaseRuntimeSection";
146
+ import {
147
+ DatabaseRunsOnLink,
148
+ DatabaseWorkloadLink,
149
+ getDatabaseRunsOnRoute,
150
+ getDatabaseWorkloadRoute,
151
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/DatabaseServer/DatabaseRunsOnLink";
152
+ import {
153
+ DatabaseEngineMetricsStatus,
154
+ DatabaseRuntimePlatform,
155
+ formatDatabaseFraction,
156
+ formatDatabaseMetricValue,
157
+ } from "../../../../App/FeatureSet/Dashboard/src/Pages/Database/Utils/DatabaseServerPresentation";
158
+ import {
159
+ DatabaseEngineMetricResult,
160
+ toEngineMetricResult,
161
+ } from "../../../../App/FeatureSet/Dashboard/src/Pages/Database/Utils/DatabaseServerTelemetryQueries";
162
+ import {
163
+ getDatabaseAgentCollectedMetricsText,
164
+ getDatabaseAgentEngine,
165
+ } from "../../../../App/FeatureSet/Dashboard/src/Pages/Database/Utils/DocumentationMarkdown";
166
+ import DatabaseServer from "../../../Models/DatabaseModels/DatabaseServer";
167
+ import {
168
+ DatabaseServerMetricDefinition,
169
+ getDatabaseServerMetrics,
170
+ } from "../../../Types/DatabaseServer/DatabaseServerMetricCatalog";
171
+ import AggregationType from "../../../Types/BaseDatabase/AggregationType";
172
+ import Includes from "../../../Types/BaseDatabase/Includes";
173
+ import ObjectID from "../../../Types/ObjectID";
174
+ import OneUptimeDate from "../../../Types/Date";
175
+ import RangeStartAndEndDateTime from "../../../Types/Time/RangeStartAndEndDateTime";
176
+ import TimeRange from "../../../Types/Time/TimeRange";
177
+
178
+ const MODEL_ID: ObjectID = new ObjectID("84858d6c-1111-4aaa-8bbb-000000000001");
179
+ const SERVICE_ID: string = "6a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d";
180
+ const START: Date = new Date("2026-09-24T10:00:00.000Z");
181
+
182
+ function at(minute: number): Date {
183
+ return new Date(START.getTime() + minute * 60 * 1000);
184
+ }
185
+
186
+ function inRouter(element: React.ReactElement): React.ReactElement {
187
+ return <MemoryRouter>{element}</MemoryRouter>;
188
+ }
189
+
190
+ function databasePath(suffix: string = ""): string {
191
+ return `/dashboard/${PROJECT_ID}/databases/${MODEL_ID.toString()}${suffix}`;
192
+ }
193
+
194
+ beforeEach(() => {
195
+ chartCardMock.mockReset();
196
+ aggregateMock.mockReset();
197
+ getListAnalyticsMock.mockReset();
198
+ goTo(`/dashboard/${PROJECT_ID}/databases`);
199
+ });
200
+
201
+ afterEach(() => {
202
+ cleanup();
203
+ });
204
+
205
+ describe("DatabaseServerElement", () => {
206
+ test("links to the database's page in the current project", () => {
207
+ const database: DatabaseServer = new DatabaseServer();
208
+ database._id = MODEL_ID.toString();
209
+ database.name = "PostgreSQL db.prod.internal:5432";
210
+
211
+ render(inRouter(<DatabaseServerElement databaseServer={database} />));
212
+
213
+ const link: HTMLElement = screen.getByRole("link", {
214
+ name: /PostgreSQL db\.prod\.internal:5432/,
215
+ });
216
+ expect(link).toHaveAttribute("href", databasePath());
217
+ });
218
+
219
+ test("without an id it renders the plain name", () => {
220
+ const database: DatabaseServer = new DatabaseServer();
221
+ database.name = "orders-db";
222
+
223
+ render(inRouter(<DatabaseServerElement databaseServer={database} />));
224
+
225
+ expect(screen.getByText("orders-db")).toBeInTheDocument();
226
+ expect(screen.queryByRole("link")).not.toBeInTheDocument();
227
+ });
228
+
229
+ test("the list renders one chip per database and an empty message", () => {
230
+ const first: DatabaseServer = new DatabaseServer();
231
+ first._id = MODEL_ID.toString();
232
+ first.name = "orders-db";
233
+
234
+ const { unmount } = render(
235
+ inRouter(<DatabaseServersElement databaseServers={[first]} />),
236
+ );
237
+ expect(screen.getByRole("link", { name: /orders-db/ })).toBeVisible();
238
+ unmount();
239
+
240
+ render(inRouter(<DatabaseServersElement databaseServers={[]} />));
241
+ expect(screen.getByText("No databases.")).toBeInTheDocument();
242
+ });
243
+ });
244
+
245
+ describe("DatabaseServerUnscopedBanner", () => {
246
+ test("explains the missing scope and offers both ways out", () => {
247
+ render(
248
+ inRouter(
249
+ <DatabaseServerUnscopedBanner modelId={MODEL_ID} signal="logs" />,
250
+ ),
251
+ );
252
+
253
+ const card: HTMLElement = screen.getByRole("region", {
254
+ name: "No telemetry scope yet",
255
+ });
256
+ expect(card).toHaveTextContent("no logs can be attributed to it");
257
+ expect(
258
+ within(card).getByRole("link", { name: /Add an endpoint/ }),
259
+ ).toHaveAttribute("href", databasePath("/endpoints"));
260
+ expect(
261
+ within(card).getByRole("link", { name: /Connect the Database Agent/ }),
262
+ ).toHaveAttribute("href", databasePath("/documentation"));
263
+ });
264
+
265
+ test("defaults the signal to telemetry", () => {
266
+ render(inRouter(<DatabaseServerUnscopedBanner modelId={MODEL_ID} />));
267
+
268
+ expect(
269
+ screen.getByRole("region", { name: "No telemetry scope yet" }),
270
+ ).toHaveTextContent("no telemetry can be attributed to it");
271
+ });
272
+
273
+ test("the 'id only' hint says only data sent with its id shows, and how to get more", () => {
274
+ render(
275
+ inRouter(
276
+ <DatabaseServerUnscopedBanner
277
+ modelId={MODEL_ID}
278
+ signal="metrics"
279
+ variant="id-only"
280
+ />,
281
+ ),
282
+ );
283
+
284
+ const card: HTMLElement = screen.getByRole("region", {
285
+ name: DATABASE_SERVER_ID_ONLY_TITLE,
286
+ });
287
+ expect(card).toHaveTextContent(
288
+ "what shows here is only the metrics sent with its id",
289
+ );
290
+ expect(card).toHaveTextContent("DATABASE_SERVER_ID");
291
+ expect(card).toHaveTextContent("oneuptime.database.server.id.");
292
+ // Never claims nothing can be attributed: the row key is a real scope.
293
+ expect(card).not.toHaveTextContent("can be attributed");
294
+ expect(
295
+ screen.queryByRole("region", { name: "No telemetry scope yet" }),
296
+ ).not.toBeInTheDocument();
297
+ expect(
298
+ within(card).getByTestId("database-server-unscoped-banner"),
299
+ ).toHaveAttribute("data-variant", "id-only");
300
+ expect(
301
+ within(card).getByRole("link", { name: /Add an endpoint/ }),
302
+ ).toHaveAttribute("href", databasePath("/endpoints"));
303
+ expect(
304
+ within(card).getByRole("link", { name: /Connect the Database Agent/ }),
305
+ ).toHaveAttribute("href", databasePath("/documentation"));
306
+ });
307
+
308
+ test("the description for each variant", () => {
309
+ expect(getDatabaseServerScopeBannerDescription("unscoped", "logs")).toBe(
310
+ "This database has no endpoint and no Kubernetes or container members yet, so no logs can be attributed to it.",
311
+ );
312
+ expect(getDatabaseServerScopeBannerDescription("id-only")).toBe(
313
+ "This database has no endpoint and no Kubernetes or container members yet, so what shows here is only the telemetry sent with its id. The queries your applications send it are matched by endpoint.",
314
+ );
315
+ });
316
+ });
317
+
318
+ /*
319
+ * A loaded row always has its row key, so "unscoped" only happens without a
320
+ * project; "id only" is a row whose row key is its whole scope.
321
+ */
322
+ describe("isDatabaseServerScopedByIdOnly", () => {
323
+ const PROJECT: string = "10000000-0000-4000-8000-000000000001";
324
+
325
+ test("a row with no parseable endpoint and no members is scoped by its id alone", () => {
326
+ expect(
327
+ isDatabaseServerScopedByIdOnly({
328
+ projectId: PROJECT,
329
+ id: MODEL_ID,
330
+ endpoints: ["localhost:5432", "not a host", { endpoint: null }],
331
+ dbSystem: "postgresql",
332
+ memberEntityKeys: {},
333
+ }),
334
+ ).toBe(true);
335
+ expect(
336
+ isDatabaseServerScopedByIdOnly({
337
+ projectId: PROJECT,
338
+ id: MODEL_ID.toString(),
339
+ endpoints: [],
340
+ }),
341
+ ).toBe(true);
342
+ });
343
+
344
+ test("an endpoint or a member is more than the id", () => {
345
+ expect(
346
+ isDatabaseServerScopedByIdOnly({
347
+ projectId: PROJECT,
348
+ id: MODEL_ID,
349
+ endpoints: ["db.prod.internal:5432"],
350
+ dbSystem: "postgresql",
351
+ }),
352
+ ).toBe(false);
353
+ expect(
354
+ isDatabaseServerScopedByIdOnly({
355
+ projectId: PROJECT,
356
+ id: MODEL_ID,
357
+ endpoints: [],
358
+ dbSystem: "postgresql",
359
+ memberEntityKeys: { "0123456789abcdef": "2026-09-23T10:00:00.000Z" },
360
+ }),
361
+ ).toBe(false);
362
+ });
363
+
364
+ test("without an id or a project it is unscoped, not id-only", () => {
365
+ expect(
366
+ isDatabaseServerScopedByIdOnly({
367
+ projectId: PROJECT,
368
+ id: null,
369
+ endpoints: [],
370
+ }),
371
+ ).toBe(false);
372
+ expect(
373
+ isDatabaseServerScopedByIdOnly({
374
+ projectId: null,
375
+ id: MODEL_ID,
376
+ endpoints: [],
377
+ }),
378
+ ).toBe(false);
379
+ expect(isDatabaseServerScopedByIdOnly(null)).toBe(false);
380
+ });
381
+ });
382
+
383
+ describe("DatabaseCallingServicesCard", () => {
384
+ test("shows a loader while loading", () => {
385
+ render(
386
+ inRouter(
387
+ <DatabaseCallingServicesCard
388
+ services={[]}
389
+ serviceNames={{}}
390
+ isLoading={true}
391
+ />,
392
+ ),
393
+ );
394
+
395
+ expect(screen.getByTestId("component-loader")).toBeInTheDocument();
396
+ });
397
+
398
+ test("says so when nothing queried the database", () => {
399
+ render(
400
+ inRouter(
401
+ <DatabaseCallingServicesCard
402
+ services={[]}
403
+ serviceNames={{}}
404
+ isLoading={false}
405
+ />,
406
+ ),
407
+ );
408
+
409
+ expect(
410
+ screen.getByTestId("database-calling-services-empty"),
411
+ ).toHaveTextContent("No instrumented application queried this database");
412
+ });
413
+
414
+ test("lists named services as links and unknown ids as text", () => {
415
+ render(
416
+ inRouter(
417
+ <DatabaseCallingServicesCard
418
+ services={[
419
+ {
420
+ serviceId: SERVICE_ID,
421
+ calls: 1500,
422
+ errors: 15,
423
+ errorRatePercent: 1,
424
+ p95DurationMs: 12.5,
425
+ },
426
+ {
427
+ serviceId: "unknown-service",
428
+ calls: 3,
429
+ errors: 0,
430
+ errorRatePercent: 0,
431
+ p95DurationMs: null,
432
+ },
433
+ ]}
434
+ serviceNames={{ [SERVICE_ID]: "checkout" }}
435
+ isLoading={false}
436
+ />,
437
+ ),
438
+ );
439
+
440
+ const rows: Array<HTMLElement> = screen.getAllByTestId(
441
+ "database-calling-service-row",
442
+ );
443
+ expect(rows).toHaveLength(2);
444
+
445
+ expect(
446
+ within(rows[0]!).getByRole("link", { name: "checkout" }),
447
+ ).toHaveAttribute("href", `/dashboard/${PROJECT_ID}/service/${SERVICE_ID}`);
448
+ expect(rows[0]).toHaveTextContent("1.5k");
449
+ expect(rows[0]).toHaveTextContent("1.0%");
450
+ expect(rows[0]).toHaveTextContent("13 ms");
451
+
452
+ expect(within(rows[1]!).queryByRole("link")).not.toBeInTheDocument();
453
+ expect(rows[1]).toHaveTextContent("unknown-service");
454
+ expect(rows[1]).toHaveTextContent("—");
455
+ });
456
+ });
457
+
458
+ describe("DatabaseCallingServicesCard totals", () => {
459
+ const service: {
460
+ serviceId: string;
461
+ calls: number;
462
+ errors: number;
463
+ errorRatePercent: number;
464
+ p95DurationMs: number;
465
+ } = {
466
+ serviceId: SERVICE_ID,
467
+ calls: 10,
468
+ errors: 0,
469
+ errorRatePercent: 0,
470
+ p95DurationMs: 1,
471
+ };
472
+
473
+ test("says how many services called when the table holds fewer", () => {
474
+ render(
475
+ inRouter(
476
+ <DatabaseCallingServicesCard
477
+ services={[service]}
478
+ serviceNames={{}}
479
+ isLoading={false}
480
+ totalServices={25}
481
+ />,
482
+ ),
483
+ );
484
+
485
+ expect(
486
+ screen.getByTestId("database-calling-services-footer"),
487
+ ).toHaveTextContent("Showing the 1 busiest of 25 calling services.");
488
+ });
489
+
490
+ test("no footer when the table is the whole list", () => {
491
+ render(
492
+ inRouter(
493
+ <DatabaseCallingServicesCard
494
+ services={[service]}
495
+ serviceNames={{}}
496
+ isLoading={false}
497
+ totalServices={1}
498
+ />,
499
+ ),
500
+ );
501
+
502
+ expect(
503
+ screen.queryByTestId("database-calling-services-footer"),
504
+ ).not.toBeInTheDocument();
505
+ expect(getCallingServicesFooter(10, 10)).toBe("");
506
+ expect(getCallingServicesFooter(10, undefined)).toBe("");
507
+ expect(getCallingServicesFooter(10, 1500)).toBe(
508
+ "Showing the 10 busiest of 1.5k calling services.",
509
+ );
510
+ });
511
+ });
512
+
513
+ describe("DatabaseEngineMetricsSection", () => {
514
+ const catalog: Array<DatabaseServerMetricDefinition> =
515
+ getDatabaseServerMetrics("postgresql");
516
+
517
+ function renderSection(data: {
518
+ status: DatabaseEngineMetricsStatus;
519
+ results: Array<DatabaseEngineMetricResult>;
520
+ dbSystem?: string;
521
+ hasCatalog?: boolean;
522
+ isLoading?: boolean;
523
+ lastReceivedAt?: Date | null;
524
+ engineLabel?: string;
525
+ }): void {
526
+ render(
527
+ inRouter(
528
+ <DatabaseEngineMetricsSection
529
+ modelId={MODEL_ID}
530
+ engineLabel={data.engineLabel || "PostgreSQL"}
531
+ status={data.status}
532
+ hasCatalog={data.hasCatalog ?? true}
533
+ dbSystem={data.dbSystem ?? "postgresql"}
534
+ lastReceivedAt={data.lastReceivedAt}
535
+ results={data.results}
536
+ isLoading={data.isLoading ?? false}
537
+ windowStart={at(0)}
538
+ windowEnd={at(60)}
539
+ />,
540
+ ),
541
+ );
542
+ }
543
+
544
+ test("shows a loader while loading", () => {
545
+ renderSection({
546
+ status: DatabaseEngineMetricsStatus.Connected,
547
+ results: [],
548
+ isLoading: true,
549
+ });
550
+
551
+ expect(screen.getByTestId("component-loader")).toBeInTheDocument();
552
+ expect(chartCardMock).not.toHaveBeenCalled();
553
+ });
554
+
555
+ test("never connected: a gray 'Not connected', and the install guide — never 'stopped reporting'", () => {
556
+ renderSection({
557
+ status: DatabaseEngineMetricsStatus.NotConnected,
558
+ results: catalog.map((definition: DatabaseServerMetricDefinition) => {
559
+ return toEngineMetricResult(definition, []);
560
+ }),
561
+ });
562
+
563
+ const card: HTMLElement = screen.getByRole("region", {
564
+ name: ENGINE_METRICS_NOT_CONNECTED_TITLE,
565
+ });
566
+ expect(card).toHaveTextContent("Install the OneUptime Database Agent");
567
+ expect(card).toHaveTextContent("Not connected");
568
+ expect(card).not.toHaveTextContent("stopped");
569
+ expect(
570
+ within(card).getByTestId("database-engine-metrics-status"),
571
+ ).toHaveAttribute("data-status", DatabaseEngineMetricsStatus.NotConnected);
572
+ expect(
573
+ within(card).getByRole("link", { name: /Install the Database Agent/ }),
574
+ ).toHaveAttribute("href", databasePath("/documentation"));
575
+ expect(chartCardMock).not.toHaveBeenCalled();
576
+ });
577
+
578
+ test("disconnected: a red status, when it last reported, and a way to check the agent", () => {
579
+ const lastReceivedAt: Date = new Date("2026-09-20T08:00:00.000Z");
580
+ renderSection({
581
+ status: DatabaseEngineMetricsStatus.Disconnected,
582
+ results: [],
583
+ lastReceivedAt,
584
+ });
585
+
586
+ const card: HTMLElement = screen.getByRole("region", {
587
+ name: ENGINE_METRICS_DISCONNECTED_TITLE,
588
+ });
589
+ expect(card).toHaveTextContent("stopped reporting");
590
+ expect(card).toHaveTextContent(
591
+ OneUptimeDate.getDateAsLocalFormattedString(lastReceivedAt),
592
+ );
593
+ expect(card).not.toHaveTextContent("Install the OneUptime Database Agent");
594
+ expect(
595
+ within(card).getByTestId("database-engine-metrics-status"),
596
+ ).toHaveAttribute("data-status", DatabaseEngineMetricsStatus.Disconnected);
597
+ expect(
598
+ within(card).getByTestId("database-engine-metrics-status").className,
599
+ ).toContain("red");
600
+ expect(
601
+ within(card).getByRole("link", { name: /Check the agent setup/ }),
602
+ ).toHaveAttribute("href", databasePath("/documentation"));
603
+ });
604
+
605
+ test("the missing-metrics wording for each situation", () => {
606
+ expect(
607
+ getEngineMetricsMissingDescription({
608
+ status: DatabaseEngineMetricsStatus.NotConnected,
609
+ engineLabel: "MySQL",
610
+ dbSystem: "mysql",
611
+ }),
612
+ ).toContain("no Database Agent or OpenTelemetry Collector has sent them");
613
+ expect(
614
+ getEngineMetricsMissingDescription({
615
+ status: DatabaseEngineMetricsStatus.Disconnected,
616
+ engineLabel: "MySQL",
617
+ dbSystem: "mysql",
618
+ }),
619
+ ).toContain("has stopped reporting");
620
+ // The engine's metrics CAN be collected: never "there is no receiver".
621
+ for (const dbSystem of [
622
+ "cockroachdb",
623
+ "couchdb",
624
+ "aws.dynamodb",
625
+ "ibm.db2",
626
+ "acme-unknown-db",
627
+ ]) {
628
+ const text: string = getEngineMetricsMissingDescription({
629
+ status: DatabaseEngineMetricsStatus.NotConnected,
630
+ engineLabel: "X",
631
+ dbSystem,
632
+ });
633
+ expect({
634
+ dbSystem,
635
+ saysNoReceiver: text.includes("There is no OpenTelemetry Collector"),
636
+ pointsAtGuide: text.includes("Documentation tab"),
637
+ }).toEqual({ dbSystem, saysNoReceiver: false, pointsAtGuide: true });
638
+ }
639
+ });
640
+
641
+ /*
642
+ * UI review: every engine's card said "Connections, throughput, cache hit
643
+ * ratio, locks and replication come from the … engine itself" — Memcached
644
+ * has no locks or replication. An engine the agent ships a config for now
645
+ * names what that config really collects, the list the Documentation tab
646
+ * opens with, as plain text (the card is not markdown).
647
+ */
648
+ test.each([
649
+ ["memcached", "Memcached", "memcached", "get hits and misses"],
650
+ ["opensearch", "OpenSearch", "elasticsearch", "cluster health"],
651
+ ["valkey", "Valkey", "redis", "keyspace hits and misses"],
652
+ ["mariadb", "MariaDB", "mysql", "the InnoDB buffer pool"],
653
+ ["postgresql", "PostgreSQL", "postgresql", "max_connections"],
654
+ ])(
655
+ "not connected, %s: names the metrics its agent config collects",
656
+ (
657
+ dbSystem: string,
658
+ engineLabel: string,
659
+ agentEngine: string,
660
+ named: string,
661
+ ) => {
662
+ const text: string = getEngineMetricsMissingDescription({
663
+ status: DatabaseEngineMetricsStatus.NotConnected,
664
+ engineLabel,
665
+ dbSystem,
666
+ });
667
+
668
+ expect(getDatabaseAgentEngine(dbSystem)).toBe(agentEngine);
669
+ expect(text).toContain(
670
+ `Engine metrics — ${getDatabaseAgentCollectedMetricsText(
671
+ getDatabaseAgentEngine(dbSystem)!,
672
+ )} — come from the ${engineLabel} engine itself, and no Database Agent or OpenTelemetry Collector has sent them for this database yet.`,
673
+ );
674
+ expect(text).toContain(named);
675
+ expect(text).not.toContain("`");
676
+ expect(text).not.toContain("DATABASE_QUERY_EVENTS");
677
+ expect(text).not.toContain("throughput, cache hit ratio, locks");
678
+ },
679
+ );
680
+
681
+ test("Memcached and Elasticsearch are never promised locks or replication", () => {
682
+ for (const dbSystem of ["memcached", "elasticsearch"]) {
683
+ const text: string = getEngineMetricsMissingDescription({
684
+ status: DatabaseEngineMetricsStatus.NotConnected,
685
+ engineLabel: "X",
686
+ dbSystem,
687
+ });
688
+ expect({
689
+ dbSystem,
690
+ locks: text.split(/\W+/).some((word: string): boolean => {
691
+ return word === "lock" || word === "locks";
692
+ }),
693
+ replication: text.includes("replication"),
694
+ }).toEqual({ dbSystem, locks: false, replication: false });
695
+ }
696
+ });
697
+
698
+ test("an engine the agent has no config for keeps the generic words", () => {
699
+ expect(
700
+ getEngineMetricsMissingDescription({
701
+ status: DatabaseEngineMetricsStatus.NotConnected,
702
+ engineLabel: "CouchDB",
703
+ dbSystem: "couchdb",
704
+ }),
705
+ ).toContain(
706
+ "Connections, throughput, cache hit ratio, locks and replication come from the CouchDB engine itself",
707
+ );
708
+ });
709
+
710
+ /*
711
+ * What "not connected" offers follows where the engine's metrics come
712
+ * from — the same source the Documentation tab builds its guide from.
713
+ */
714
+ test.each([
715
+ [
716
+ "an engine the Database Agent ships a config for",
717
+ "mysql",
718
+ "MySQL",
719
+ "Install the OneUptime Database Agent",
720
+ "Install the Database Agent →",
721
+ ],
722
+ [
723
+ "a fork the agent monitors with its family's config",
724
+ "mariadb",
725
+ "MariaDB",
726
+ "Install the OneUptime Database Agent",
727
+ "Install the Database Agent →",
728
+ ],
729
+ [
730
+ "an engine with a collector receiver but no agent config",
731
+ "couchdb",
732
+ "CouchDB",
733
+ "ready-made collector config",
734
+ "Connect engine metrics →",
735
+ ],
736
+ [
737
+ "an engine that serves Prometheus metrics",
738
+ "cockroachdb",
739
+ "CockroachDB",
740
+ "serves Prometheus metrics itself",
741
+ "Connect engine metrics →",
742
+ ],
743
+ [
744
+ "a managed service read from its provider's monitoring API",
745
+ "aws.dynamodb",
746
+ "Amazon DynamoDB",
747
+ "provider's monitoring API",
748
+ "Connect engine metrics →",
749
+ ],
750
+ [
751
+ "an engine with no ready-made path",
752
+ "ibm.db2",
753
+ "IBM Db2",
754
+ "can still be collected",
755
+ "Connect engine metrics →",
756
+ ],
757
+ [
758
+ "an engine OneUptime does not know",
759
+ "acme-unknown-db",
760
+ "acme-unknown-db",
761
+ "can still be collected",
762
+ "Connect engine metrics →",
763
+ ],
764
+ ])(
765
+ "not connected, %s: says how and links the Documentation tab",
766
+ (
767
+ _case: string,
768
+ dbSystem: string,
769
+ engineLabel: string,
770
+ wording: string,
771
+ linkLabel: string,
772
+ ) => {
773
+ renderSection({
774
+ status: DatabaseEngineMetricsStatus.NotConnected,
775
+ results: [],
776
+ hasCatalog: false,
777
+ dbSystem,
778
+ engineLabel,
779
+ });
780
+
781
+ const card: HTMLElement = screen.getByRole("region", {
782
+ name: ENGINE_METRICS_NOT_CONNECTED_TITLE,
783
+ });
784
+ expect(card).toHaveTextContent(wording);
785
+ expect(card).not.toHaveTextContent("There is no OpenTelemetry Collector");
786
+ expect(card).not.toHaveTextContent("stopped");
787
+ const link: HTMLElement = within(card).getByRole("link");
788
+ expect(link).toHaveTextContent(linkLabel);
789
+ expect(link).toHaveAttribute("href", databasePath("/documentation"));
790
+ },
791
+ );
792
+
793
+ test("a disconnected engine always offers the setup check, whatever its source", () => {
794
+ const guidance: EngineMetricsGuidance = getEngineMetricsGuidance({
795
+ status: DatabaseEngineMetricsStatus.Disconnected,
796
+ engineLabel: "CockroachDB",
797
+ dbSystem: "cockroachdb",
798
+ });
799
+ expect(guidance.description).toContain("has stopped reporting");
800
+ expect(guidance.linkLabel).toBe("Check the agent setup →");
801
+ });
802
+
803
+ test("connected with nothing in range: points at the Metrics tab, not the install guide", () => {
804
+ renderSection({
805
+ status: DatabaseEngineMetricsStatus.Connected,
806
+ results: catalog.map((definition: DatabaseServerMetricDefinition) => {
807
+ return toEngineMetricResult(definition, []);
808
+ }),
809
+ });
810
+
811
+ const card: HTMLElement = screen.getByRole("region", {
812
+ name: ENGINE_METRICS_NO_DATA_TITLE,
813
+ });
814
+ expect(card).toHaveTextContent("is connected");
815
+ expect(card).toHaveTextContent("Connected");
816
+ expect(card).not.toHaveTextContent("Install the OneUptime Database Agent");
817
+ expect(
818
+ within(card).getByRole("link", { name: /All metrics/ }),
819
+ ).toHaveAttribute("href", databasePath("/metrics"));
820
+ expect(
821
+ screen.queryByRole("region", {
822
+ name: ENGINE_METRICS_NOT_CONNECTED_TITLE,
823
+ }),
824
+ ).not.toBeInTheDocument();
825
+ expect(chartCardMock).not.toHaveBeenCalled();
826
+ });
827
+
828
+ /*
829
+ * E2E: a Connected Memcached (7f1cbfa6, engine metrics last at 00:38) read
830
+ * "No engine metrics in this range" while 69-76 metrics arrived every
831
+ * 30 s. Its metrics DO arrive; only the curated overview is missing.
832
+ */
833
+ test("connected, but the engine has no curated overview", () => {
834
+ renderSection({
835
+ status: DatabaseEngineMetricsStatus.Connected,
836
+ results: [],
837
+ hasCatalog: false,
838
+ dbSystem: "memcached",
839
+ engineLabel: "Memcached",
840
+ });
841
+
842
+ const card: HTMLElement = screen.getByRole("region", {
843
+ name: ENGINE_METRICS_NO_CURATED_OVERVIEW_TITLE,
844
+ });
845
+ expect(
846
+ screen.queryByRole("region", { name: ENGINE_METRICS_NO_DATA_TITLE }),
847
+ ).not.toBeInTheDocument();
848
+ expect(card).toHaveTextContent("no curated overview for Memcached");
849
+ expect(
850
+ within(card).getByRole("link", { name: /All metrics/ }),
851
+ ).toHaveAttribute("href", databasePath("/metrics"));
852
+ });
853
+
854
+ test("an engine inside the application's process has nothing to connect, and says so", () => {
855
+ renderSection({
856
+ status: DatabaseEngineMetricsStatus.NotConnected,
857
+ results: [],
858
+ hasCatalog: false,
859
+ dbSystem: "sqlite",
860
+ engineLabel: "SQLite",
861
+ });
862
+
863
+ const card: HTMLElement = screen.getByRole("region", {
864
+ name: ENGINE_METRICS_NOT_CONNECTED_TITLE,
865
+ });
866
+ expect(card).toHaveTextContent(
867
+ "SQLite runs inside your application's process",
868
+ );
869
+ expect(card).toHaveTextContent("the queries they send");
870
+ expect(within(card).queryByRole("link")).not.toBeInTheDocument();
871
+ expect(
872
+ getEngineMetricsGuidance({
873
+ status: DatabaseEngineMetricsStatus.NotConnected,
874
+ engineLabel: "DuckDB",
875
+ dbSystem: "duckdb",
876
+ }).linkLabel,
877
+ ).toBeNull();
878
+ });
879
+
880
+ test("with data: a tile per catalog metric and a chart per metric with points", () => {
881
+ const gauge: DatabaseServerMetricDefinition = catalog.find(
882
+ (definition: DatabaseServerMetricDefinition): boolean => {
883
+ return definition.metricName === "postgresql.backends";
884
+ },
885
+ )!;
886
+ const counter: DatabaseServerMetricDefinition = catalog.find(
887
+ (definition: DatabaseServerMetricDefinition): boolean => {
888
+ return definition.metricName === "postgresql.commits";
889
+ },
890
+ )!;
891
+
892
+ renderSection({
893
+ status: DatabaseEngineMetricsStatus.Connected,
894
+ results: [
895
+ toEngineMetricResult(gauge, [
896
+ { x: at(0), y: 10 },
897
+ { x: at(1), y: 12 },
898
+ ]),
899
+ // A counter's series arrives as its per-second rate.
900
+ toEngineMetricResult(counter, [{ x: at(1), y: 10 }]),
901
+ ...catalog
902
+ .filter((definition: DatabaseServerMetricDefinition): boolean => {
903
+ return definition !== gauge && definition !== counter;
904
+ })
905
+ .map((definition: DatabaseServerMetricDefinition) => {
906
+ return toEngineMetricResult(definition, []);
907
+ }),
908
+ ],
909
+ });
910
+
911
+ expect(screen.getByTestId("database-engine-metrics")).toBeInTheDocument();
912
+ expect(screen.getAllByTestId("database-engine-metric-tile")).toHaveLength(
913
+ catalog.length,
914
+ );
915
+ expect(screen.getByText("12 connections")).toBeInTheDocument();
916
+ expect(screen.getByText("10 commits/s")).toBeInTheDocument();
917
+ // Every tile explains itself in an (i), not a bare title attribute.
918
+ expect(
919
+ screen.getByRole("button", { name: "About Connections" }),
920
+ ).toBeInTheDocument();
921
+
922
+ // Only metrics that returned points are charted; counters say "per second".
923
+ const titles: Array<string> = screen
924
+ .getAllByTestId("chart-card")
925
+ .map((element: HTMLElement): string => {
926
+ return element.textContent || "";
927
+ });
928
+ expect(titles).toEqual(["Connections", "Commits (per second)"]);
929
+ expect(
930
+ (chartCardMock.mock.calls[0]![0] as { description: string }).description,
931
+ ).toBe(gauge.description);
932
+ expect(screen.getByRole("link", { name: /All metrics/ })).toHaveAttribute(
933
+ "href",
934
+ databasePath("/metrics"),
935
+ );
936
+ });
937
+
938
+ test("two entries of one metric (MySQL threads) render as two tiles and charts", () => {
939
+ const mysql: Array<DatabaseServerMetricDefinition> =
940
+ getDatabaseServerMetrics("mysql");
941
+ const consoleError: ReturnType<typeof jest.spyOn> = jest
942
+ .spyOn(console, "error")
943
+ .mockImplementation((): void => {});
944
+
945
+ renderSection({
946
+ status: DatabaseEngineMetricsStatus.Connected,
947
+ engineLabel: "MySQL",
948
+ results: mysql.map((definition: DatabaseServerMetricDefinition) => {
949
+ return toEngineMetricResult(definition, [{ x: at(0), y: 3 }]);
950
+ }),
951
+ });
952
+
953
+ expect(screen.getAllByTestId("database-engine-metric-tile")).toHaveLength(
954
+ mysql.length,
955
+ );
956
+ // One tile and one chart each.
957
+ expect(screen.getAllByText("Connected threads")).toHaveLength(2);
958
+ expect(screen.getAllByText("Running threads")).toHaveLength(2);
959
+ // No duplicate React keys.
960
+ const duplicateKeyWarnings: Array<unknown> = consoleError.mock.calls.filter(
961
+ (call: Array<unknown>): boolean => {
962
+ return String(call[0]).includes("same key");
963
+ },
964
+ );
965
+ expect(duplicateKeyWarnings).toEqual([]);
966
+ consoleError.mockRestore();
967
+ });
968
+
969
+ test("Oracle's tiles read in their own units: a fullness as a percentage, DB time per second", () => {
970
+ const oracle: Array<DatabaseServerMetricDefinition> =
971
+ getDatabaseServerMetrics("oracle.db");
972
+ const values: Record<string, number> = {
973
+ "oracledb.tablespace.utilization": 0.853,
974
+ "oracledb.sga.usage": 1610612736,
975
+ "oracledb.db.time": 0.35,
976
+ };
977
+
978
+ renderSection({
979
+ status: DatabaseEngineMetricsStatus.Connected,
980
+ engineLabel: "Oracle",
981
+ dbSystem: "oracle.db",
982
+ results: oracle.map((definition: DatabaseServerMetricDefinition) => {
983
+ const value: number | undefined = values[definition.metricName];
984
+ return toEngineMetricResult(
985
+ definition,
986
+ value === undefined ? [] : [{ x: at(0), y: value }],
987
+ );
988
+ }),
989
+ });
990
+
991
+ const tile: (title: string) => HTMLElement = (
992
+ title: string,
993
+ ): HTMLElement => {
994
+ return screen
995
+ .getAllByTestId("database-engine-metric-tile")
996
+ .find((element: HTMLElement): boolean => {
997
+ return (element.textContent || "").startsWith(title);
998
+ })!;
999
+ };
1000
+ expect(tile("Fullest tablespace")).toHaveTextContent("85%");
1001
+ expect(tile("SGA in use")).toHaveTextContent("1.5 GiB");
1002
+ expect(tile("DB time")).toHaveTextContent("0.35 s/s");
1003
+ // No tile for a metric a pluggable database never reports.
1004
+ expect(screen.queryByText("Session limit")).not.toBeInTheDocument();
1005
+ expect(screen.queryByText("Processes")).not.toBeInTheDocument();
1006
+ });
1007
+
1008
+ test("a 0..1 share reads as a percentage without rounding a small one away", () => {
1009
+ expect(formatDatabaseFraction(0.853)).toBe("85%");
1010
+ expect(formatDatabaseFraction(0.042)).toBe("4.2%");
1011
+ expect(formatDatabaseFraction(0.000389)).toBe("0.04%");
1012
+ expect(formatDatabaseFraction(0)).toBe("0%");
1013
+ expect(formatDatabaseFraction(1)).toBe("100%");
1014
+ expect(formatDatabaseFraction(null)).toBe("—");
1015
+ expect(formatDatabaseFraction(Number.NaN)).toBe("—");
1016
+ expect(formatDatabaseMetricValue(0.5, "fraction", "gauge")).toBe("50%");
1017
+ });
1018
+
1019
+ test("with data from a stopped agent, the header still says so", () => {
1020
+ renderSection({
1021
+ status: DatabaseEngineMetricsStatus.Disconnected,
1022
+ results: [toEngineMetricResult(catalog[0]!, [{ x: at(0), y: 1 }])],
1023
+ });
1024
+
1025
+ expect(
1026
+ within(screen.getByTestId("database-engine-metrics")).getByTestId(
1027
+ "database-engine-metrics-status",
1028
+ ),
1029
+ ).toHaveTextContent("Disconnected");
1030
+ });
1031
+
1032
+ test("charts with no collector sighting carry no status pill", () => {
1033
+ renderSection({
1034
+ status: DatabaseEngineMetricsStatus.NotConnected,
1035
+ results: [toEngineMetricResult(catalog[0]!, [{ x: at(0), y: 1 }])],
1036
+ });
1037
+
1038
+ expect(screen.getByTestId("database-engine-metrics")).toBeInTheDocument();
1039
+ expect(
1040
+ screen.queryByTestId("database-engine-metrics-status"),
1041
+ ).not.toBeInTheDocument();
1042
+ });
1043
+ });
1044
+
1045
+ describe("DatabaseRunsOnLink", () => {
1046
+ const CLUSTER_ID: string = "c1a5e000-0000-4000-8000-000000000001";
1047
+ const HOST_ID: string = "d0c4e000-0000-4000-8000-000000000001";
1048
+
1049
+ function server(data: Partial<DatabaseServer>): DatabaseServer {
1050
+ const row: DatabaseServer = new DatabaseServer();
1051
+ Object.assign(row, data);
1052
+ return row;
1053
+ }
1054
+
1055
+ test("a Kubernetes database links to its cluster, its StatefulSet to the StatefulSet page", () => {
1056
+ const row: DatabaseServer = server({
1057
+ kubernetesClusterId: new ObjectID(CLUSTER_ID),
1058
+ kubernetesNamespace: "payments",
1059
+ workloadKind: "StatefulSet",
1060
+ workloadName: "postgres",
1061
+ });
1062
+ (
1063
+ row as unknown as { kubernetesCluster: { name: string } }
1064
+ ).kubernetesCluster = { name: "prod-eu" };
1065
+
1066
+ render(
1067
+ inRouter(
1068
+ <div>
1069
+ <DatabaseRunsOnLink source={row} />
1070
+ <DatabaseWorkloadLink source={row} />
1071
+ </div>,
1072
+ ),
1073
+ );
1074
+
1075
+ expect(
1076
+ screen.getByRole("link", { name: "Kubernetes · prod-eu" }),
1077
+ ).toHaveAttribute(
1078
+ "href",
1079
+ `/dashboard/${PROJECT_ID}/kubernetes/${CLUSTER_ID}`,
1080
+ );
1081
+ expect(
1082
+ screen.getByRole("link", { name: "payments/StatefulSet/postgres" }),
1083
+ ).toHaveAttribute(
1084
+ "href",
1085
+ `/dashboard/${PROJECT_ID}/kubernetes/${CLUSTER_ID}/statefulsets/postgres`,
1086
+ );
1087
+ });
1088
+
1089
+ test.each([
1090
+ ["Deployment", "deployments"],
1091
+ ["DaemonSet", "daemonsets"],
1092
+ ["Pod", "pods"],
1093
+ ])("a %s workload links to its page", (kind: string, segment: string) => {
1094
+ const route: string | undefined = getDatabaseWorkloadRoute({
1095
+ kubernetesClusterId: CLUSTER_ID,
1096
+ workloadKind: kind,
1097
+ workloadName: "redis",
1098
+ })?.toString();
1099
+ expect(route).toBe(
1100
+ `/dashboard/${PROJECT_ID}/kubernetes/${CLUSTER_ID}/${segment}/redis`,
1101
+ );
1102
+ });
1103
+
1104
+ test("an operator's Cluster or a ReplicaSet has no page to link", () => {
1105
+ for (const kind of ["Cluster", "ReplicaSet", ""]) {
1106
+ expect(
1107
+ getDatabaseWorkloadRoute({
1108
+ kubernetesClusterId: CLUSTER_ID,
1109
+ workloadKind: kind,
1110
+ workloadName: "pg",
1111
+ }),
1112
+ ).toBeNull();
1113
+ }
1114
+ });
1115
+
1116
+ test("Docker and Podman databases link to their host and its containers", () => {
1117
+ expect(
1118
+ getDatabaseRunsOnRoute({
1119
+ dockerHostId: new ObjectID(HOST_ID),
1120
+ })?.toString(),
1121
+ ).toBe(`/dashboard/${PROJECT_ID}/docker/${HOST_ID}`);
1122
+ expect(
1123
+ getDatabaseWorkloadRoute({
1124
+ dockerHostId: HOST_ID,
1125
+ workloadKind: "Container",
1126
+ workloadName: "shop-db",
1127
+ })?.toString(),
1128
+ ).toBe(`/dashboard/${PROJECT_ID}/docker/${HOST_ID}/containers`);
1129
+ expect(getDatabaseRunsOnRoute({ podmanHostId: HOST_ID })?.toString()).toBe(
1130
+ `/dashboard/${PROJECT_ID}/podman/${HOST_ID}`,
1131
+ );
1132
+ expect(
1133
+ getDatabaseWorkloadRoute({
1134
+ podmanHostId: HOST_ID,
1135
+ workloadName: "shop-db",
1136
+ })?.toString(),
1137
+ ).toBe(`/dashboard/${PROJECT_ID}/podman/${HOST_ID}/containers`);
1138
+ });
1139
+
1140
+ test("without a parent id it is plain text; off-platform it is a dash", () => {
1141
+ const { unmount } = render(
1142
+ inRouter(
1143
+ <DatabaseRunsOnLink
1144
+ source={{ discoverySource: "kubernetes", workloadName: "pg" }}
1145
+ />,
1146
+ ),
1147
+ );
1148
+ expect(screen.getByText("Kubernetes")).toBeInTheDocument();
1149
+ expect(screen.queryByRole("link")).not.toBeInTheDocument();
1150
+ unmount();
1151
+
1152
+ render(inRouter(<DatabaseRunsOnLink source={{ serverAddress: "db" }} />));
1153
+ expect(screen.getByText("—")).toBeInTheDocument();
1154
+ expect(getDatabaseRunsOnRoute(null)).toBeNull();
1155
+ expect(getDatabaseWorkloadRoute({ serverAddress: "db" })).toBeNull();
1156
+ });
1157
+ });
1158
+
1159
+ describe("DatabaseRuntimeSection", () => {
1160
+ test("renders Runs on and Workload as links, and explains its charts", () => {
1161
+ const row: DatabaseServer = new DatabaseServer();
1162
+ row.kubernetesClusterId = new ObjectID(
1163
+ "c1a5e000-0000-4000-8000-000000000001",
1164
+ );
1165
+ row.kubernetesNamespace = "payments";
1166
+ row.workloadKind = "StatefulSet";
1167
+ row.workloadName = "postgres";
1168
+
1169
+ render(
1170
+ inRouter(
1171
+ <DatabaseRuntimeSection
1172
+ modelId={MODEL_ID}
1173
+ platform={DatabaseRuntimePlatform.Kubernetes}
1174
+ source={row}
1175
+ instanceCount={3}
1176
+ memberCount={3}
1177
+ cpuSeries={[]}
1178
+ memorySeries={[]}
1179
+ isLoading={false}
1180
+ windowStart={at(0)}
1181
+ windowEnd={at(60)}
1182
+ />,
1183
+ ),
1184
+ );
1185
+
1186
+ expect(screen.getByRole("link", { name: "Kubernetes" })).toHaveAttribute(
1187
+ "href",
1188
+ `/dashboard/${PROJECT_ID}/kubernetes/c1a5e000-0000-4000-8000-000000000001`,
1189
+ );
1190
+ expect(
1191
+ screen.getByRole("link", { name: "payments/StatefulSet/postgres" }),
1192
+ ).toBeInTheDocument();
1193
+ for (const call of chartCardMock.mock.calls) {
1194
+ expect(
1195
+ ((call[0] as { description?: string }).description || "").length,
1196
+ ).toBeGreaterThan(0);
1197
+ }
1198
+ });
1199
+ });
1200
+
1201
+ /*
1202
+ * The Metrics tab charts a clicked metric here, over the database's key set
1203
+ * (the explorer would chart the whole project). The query goes through the
1204
+ * scoped helper: keys only, no request at all without keys, each metric
1205
+ * charted by what it is — a curated counter as a per-second rate with no
1206
+ * aggregation to pick, a histogram by percentile, a cumulative counter as a
1207
+ * rate — with values in the metric's unit.
1208
+ */
1209
+ describe("DatabaseMetricChartModal", () => {
1210
+ const KEY: string = "0123456789abcdef";
1211
+
1212
+ type ChartProps = {
1213
+ title: string;
1214
+ loading?: boolean;
1215
+ yFormatter?: (value: number) => string;
1216
+ series: Array<{ seriesName: string; data: Array<{ x: Date; y: number }> }>;
1217
+ };
1218
+
1219
+ function lastChart(): ChartProps {
1220
+ const calls: Array<Array<unknown>> = chartCardMock.mock.calls;
1221
+ expect(calls.length).toBeGreaterThan(0);
1222
+ return calls[calls.length - 1]![0] as ChartProps;
1223
+ }
1224
+
1225
+ function lastAggregateBy(): Record<string, unknown> {
1226
+ const calls: Array<Array<unknown>> = aggregateMock.mock.calls;
1227
+ expect(calls.length).toBeGreaterThan(0);
1228
+ return (
1229
+ calls[calls.length - 1]![0] as { aggregateBy: Record<string, unknown> }
1230
+ ).aggregateBy;
1231
+ }
1232
+
1233
+ function shapeRow(row: Record<string, unknown>): void {
1234
+ getListAnalyticsMock.mockResolvedValue({ data: [row], count: 1 });
1235
+ }
1236
+
1237
+ function renderChart(data: {
1238
+ metricName: string;
1239
+ keys: Array<string>;
1240
+ unit?: string;
1241
+ dbSystem?: string;
1242
+ initialTimeRange?: RangeStartAndEndDateTime;
1243
+ onClose?: () => void;
1244
+ }): void {
1245
+ render(
1246
+ <DatabaseMetricChartModal
1247
+ metricName={data.metricName}
1248
+ unit={data.unit}
1249
+ keys={data.keys}
1250
+ projectId={PROJECT_ID}
1251
+ dbSystem={data.dbSystem || "postgresql"}
1252
+ initialTimeRange={data.initialTimeRange}
1253
+ onClose={data.onClose || ((): void => {})}
1254
+ />,
1255
+ );
1256
+ }
1257
+
1258
+ test("a curated counter is charted as a per-second rate over the key set", async () => {
1259
+ aggregateMock.mockResolvedValue({
1260
+ data: [
1261
+ { timestamp: at(0), value: 0, attributes: { "db.namespace": "a" } },
1262
+ { timestamp: at(1), value: 600, attributes: { "db.namespace": "a" } },
1263
+ ],
1264
+ });
1265
+
1266
+ renderChart({ metricName: "postgresql.commits", keys: [KEY] });
1267
+
1268
+ await waitFor(() => {
1269
+ expect(lastChart().loading).toBe(false);
1270
+ });
1271
+ expect(lastChart().series[0]!.data).toEqual([{ x: at(1), y: 10 }]);
1272
+ expect(
1273
+ screen.getByRole("dialog", { name: "Commits (per second)" }),
1274
+ ).toBeInTheDocument();
1275
+ expect(
1276
+ screen.queryByRole("group", { name: "Aggregation" }),
1277
+ ).not.toBeInTheDocument();
1278
+ // A curated metric needs no shape lookup.
1279
+ expect(getListAnalyticsMock).not.toHaveBeenCalled();
1280
+
1281
+ const aggregateBy: Record<string, unknown> = lastAggregateBy();
1282
+ expect(aggregateBy["aggregationType"]).toBe(AggregationType.Max);
1283
+ expect(aggregateBy["groupBy"]).toEqual({ attributes: true });
1284
+ const query: Record<string, unknown> = aggregateBy["query"] as Record<
1285
+ string,
1286
+ unknown
1287
+ >;
1288
+ expect(query["name"]).toBe("postgresql.commits");
1289
+ expect((query["entityKeys"] as Includes).values).toEqual([KEY]);
1290
+ expect(query["attributes"]).toBeUndefined();
1291
+ /*
1292
+ * The axis carries the number; the unit is said once, by the title (a
1293
+ * 64 px axis clipped "2.5 commits/s" to "ommits/s").
1294
+ */
1295
+ expect(lastChart().yFormatter!(2.5)).toBe("2.5");
1296
+ expect(lastChart().title).toBe("postgresql.commits (per second)");
1297
+ });
1298
+
1299
+ /*
1300
+ * e2e: MariaDB 11.4's mysql.buffer_pool.limit is its page count (8112),
1301
+ * sent under the receiver's unit "By" — the chart read it as "7.9 KiB".
1302
+ */
1303
+ test("MariaDB's buffer pool limit is charted as a page count, with a note saying why", async () => {
1304
+ shapeRow({ metricPointType: "Sum", isMonotonic: false });
1305
+ aggregateMock.mockResolvedValue({
1306
+ data: [{ timestamp: at(0), value: 8112 }],
1307
+ });
1308
+
1309
+ renderChart({
1310
+ metricName: "mysql.buffer_pool.limit",
1311
+ keys: [KEY],
1312
+ unit: "By",
1313
+ dbSystem: "mariadb",
1314
+ });
1315
+
1316
+ await waitFor(() => {
1317
+ expect(lastChart().loading).toBe(false);
1318
+ });
1319
+ expect(lastChart().series[0]!.data).toEqual([{ x: at(0), y: 8112 }]);
1320
+ expect(lastChart().title).toBe("mysql.buffer_pool.limit (pages)");
1321
+ expect(lastChart().yFormatter!(8112)).not.toMatch(/B\b/);
1322
+ expect(screen.getByTestId("database-metric-chart-note")).toHaveTextContent(
1323
+ "size in pages (Innodb_buffer_pool_pages_total), not in bytes",
1324
+ );
1325
+ });
1326
+
1327
+ test("a gauge offers the aggregations and refetches on a change", async () => {
1328
+ shapeRow({ metricPointType: "Gauge", isMonotonic: false });
1329
+ aggregateMock.mockResolvedValue({
1330
+ data: [{ timestamp: at(0), value: 3 }],
1331
+ });
1332
+
1333
+ renderChart({
1334
+ metricName: "db.client.connection.count",
1335
+ keys: [KEY],
1336
+ unit: "{connection}",
1337
+ });
1338
+
1339
+ await waitFor(() => {
1340
+ expect(lastChart().loading).toBe(false);
1341
+ });
1342
+ expect(
1343
+ screen.getByRole("dialog", { name: "db.client.connection.count" }),
1344
+ ).toBeInTheDocument();
1345
+ expect(lastAggregateBy()["aggregationType"]).toBe(AggregationType.Avg);
1346
+ // The annotation word moves from every tick to the title.
1347
+ expect(lastChart().yFormatter!(3)).toBe("3");
1348
+ expect(lastChart().title).toBe("db.client.connection.count (connection)");
1349
+
1350
+ const group: HTMLElement = screen.getByRole("group", {
1351
+ name: "Aggregation",
1352
+ });
1353
+ expect(
1354
+ within(group).getByRole("button", { name: "Average" }),
1355
+ ).toHaveAttribute("aria-pressed", "true");
1356
+
1357
+ fireEvent.click(within(group).getByRole("button", { name: "Max" }));
1358
+
1359
+ await waitFor(() => {
1360
+ expect(lastAggregateBy()["aggregationType"]).toBe(AggregationType.Max);
1361
+ });
1362
+ expect(within(group).getByRole("button", { name: "Max" })).toHaveAttribute(
1363
+ "aria-pressed",
1364
+ "true",
1365
+ );
1366
+ });
1367
+
1368
+ test("a histogram opens on p95, offers percentiles and reads in its unit", async () => {
1369
+ shapeRow({
1370
+ metricPointType: "Histogram",
1371
+ isMonotonic: false,
1372
+ aggregationTemporality: "Cumulative",
1373
+ });
1374
+ aggregateMock.mockResolvedValue({
1375
+ data: [{ timestamp: at(0), value: 0.004 }],
1376
+ });
1377
+
1378
+ renderChart({
1379
+ metricName: "db.client.operation.duration",
1380
+ keys: [KEY],
1381
+ unit: "s",
1382
+ });
1383
+
1384
+ await waitFor(() => {
1385
+ expect(lastChart().loading).toBe(false);
1386
+ });
1387
+ expect(lastAggregateBy()["aggregationType"]).toBe(AggregationType.P95);
1388
+ const group: HTMLElement = screen.getByRole("group", {
1389
+ name: "Aggregation",
1390
+ });
1391
+ for (const label of ["p50", "p90", "p95", "p99", "Average"]) {
1392
+ expect(
1393
+ within(group).getByRole("button", { name: label }),
1394
+ ).toBeInTheDocument();
1395
+ }
1396
+ expect(
1397
+ within(group).queryByRole("button", { name: "Sum" }),
1398
+ ).not.toBeInTheDocument();
1399
+ expect(within(group).getByRole("button", { name: "p95" })).toHaveAttribute(
1400
+ "aria-pressed",
1401
+ "true",
1402
+ );
1403
+ // 4 ms in seconds reads as a latency, not "0".
1404
+ expect(lastChart().yFormatter!(0.004)).toBe("4 ms");
1405
+ expect(screen.getByTestId("database-metric-chart-note")).toHaveTextContent(
1406
+ "percentiles",
1407
+ );
1408
+
1409
+ fireEvent.click(within(group).getByRole("button", { name: "p99" }));
1410
+ await waitFor(() => {
1411
+ expect(lastAggregateBy()["aggregationType"]).toBe(AggregationType.P99);
1412
+ });
1413
+ });
1414
+
1415
+ test("a cumulative counter outside the catalog is a per-second rate", async () => {
1416
+ shapeRow({
1417
+ metricPointType: "Sum",
1418
+ isMonotonic: true,
1419
+ aggregationTemporality: "Cumulative",
1420
+ });
1421
+ aggregateMock.mockResolvedValue({
1422
+ data: [
1423
+ { timestamp: at(0), value: 0, attributes: { "db.namespace": "a" } },
1424
+ { timestamp: at(1), value: 120, attributes: { "db.namespace": "a" } },
1425
+ ],
1426
+ });
1427
+
1428
+ renderChart({ metricName: "postgresql.deadlocks", keys: [KEY], unit: "1" });
1429
+
1430
+ await waitFor(() => {
1431
+ expect(lastChart().loading).toBe(false);
1432
+ });
1433
+ expect(
1434
+ screen.getByRole("dialog", { name: "postgresql.deadlocks (per second)" }),
1435
+ ).toBeInTheDocument();
1436
+ expect(lastChart().series[0]!.data).toEqual([{ x: at(1), y: 2 }]);
1437
+ expect(lastAggregateBy()["groupBy"]).toEqual({ attributes: true });
1438
+ expect(lastChart().yFormatter!(2)).toBe("2/s");
1439
+ expect(
1440
+ screen.queryByRole("group", { name: "Aggregation" }),
1441
+ ).not.toBeInTheDocument();
1442
+ });
1443
+
1444
+ test("opens on the range the metric list shows", async () => {
1445
+ shapeRow({ metricPointType: "Gauge" });
1446
+ aggregateMock.mockResolvedValue({ data: [] });
1447
+
1448
+ renderChart({
1449
+ metricName: "db.client.connection.count",
1450
+ keys: [KEY],
1451
+ initialTimeRange: { range: TimeRange.PAST_ONE_DAY },
1452
+ });
1453
+
1454
+ expect(screen.getByTestId("time-range-picker")).toHaveTextContent(
1455
+ TimeRange.PAST_ONE_DAY,
1456
+ );
1457
+ await waitFor(() => {
1458
+ expect(lastChart().loading).toBe(false);
1459
+ });
1460
+ const start: Date = lastAggregateBy()["startTimestamp"] as Date;
1461
+ const end: Date = lastAggregateBy()["endTimestamp"] as Date;
1462
+ expect((end.getTime() - start.getTime()) / 3_600_000).toBeCloseTo(24, 0);
1463
+ });
1464
+
1465
+ test("never queries without keys and closes on request", async () => {
1466
+ const onClose: MockFunction = getJestMockFunction();
1467
+
1468
+ renderChart({
1469
+ metricName: "postgresql.backends",
1470
+ keys: [],
1471
+ onClose: () => {
1472
+ onClose();
1473
+ },
1474
+ });
1475
+
1476
+ await waitFor(() => {
1477
+ expect(lastChart().loading).toBe(false);
1478
+ });
1479
+ expect(aggregateMock).not.toHaveBeenCalled();
1480
+ expect(getListAnalyticsMock).not.toHaveBeenCalled();
1481
+ expect(lastChart().series[0]!.data).toEqual([]);
1482
+
1483
+ fireEvent.click(screen.getByRole("button", { name: "Close" }));
1484
+ expect(onClose).toHaveBeenCalledTimes(1);
1485
+ });
1486
+ });