@oneuptime/common 14.0.3 → 14.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (617) 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/OpenTelemetryIngestService.ts +19 -0
  37. package/Server/Services/PodmanResourceService.ts +43 -2
  38. package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +107 -23
  39. package/Server/Services/ServiceService.ts +1 -1
  40. package/Server/Services/TelemetryUsageBillingService.ts +29 -2
  41. package/Server/Services/UserProjectSsoConsentService.ts +74 -0
  42. package/Server/Services/UserService.ts +14 -1
  43. package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +18 -0
  44. package/Server/Types/Database/QueryHelper.ts +30 -1
  45. package/Server/Utils/AI/Toolbox/ResourceTools.ts +69 -1
  46. package/Server/Utils/Database/AffectedResourceRelations.ts +6 -0
  47. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +188 -6
  48. package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
  49. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +2262 -755
  50. package/Server/Utils/Monitor/MonitorIncident.ts +28 -1
  51. package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +268 -6
  52. package/Server/Utils/Monitor/MonitorResource.ts +47 -11
  53. package/Server/Utils/Monitor/MonitorResourceContext.ts +1 -0
  54. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +239 -21
  55. package/Server/Utils/Monitor/PerSeriesResolutionRootCause.ts +43 -0
  56. package/Server/Utils/Monitor/SeriesResourceLabels.ts +291 -0
  57. package/Server/Utils/Monitor/SeriesResourceLinker.ts +111 -12
  58. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
  59. package/Server/Utils/StartServer.ts +26 -6
  60. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
  61. package/Server/Utils/Telemetry/DatabaseEndpointDiscovery.ts +688 -0
  62. package/Server/Utils/Telemetry/EntityRegistry.ts +5 -1
  63. package/Server/Utils/Telemetry/LogExceptionExtractor.ts +18 -1
  64. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +175 -12
  65. package/Server/Utils/Telemetry/ResourceFacetResolver.ts +12 -0
  66. package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +606 -36
  67. package/Server/Utils/Telemetry/Telemetry.ts +16 -0
  68. package/Server/Utils/Telemetry/TelemetryEntity.ts +22 -22
  69. package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +16 -10
  70. package/Tests/App/Accounts/SignedInRedirect.test.tsx +241 -0
  71. package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +221 -4
  72. package/Tests/App/Dashboard/AIInsightsResourcePageContext.test.ts +42 -0
  73. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +100 -0
  74. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +86 -0
  75. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +2 -0
  76. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +19 -0
  77. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +14 -0
  78. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +26 -1
  79. package/Tests/App/Dashboard/DashboardAppSsoError.test.tsx +501 -0
  80. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +64 -1
  81. package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +178 -18
  82. package/Tests/App/Dashboard/DashboardMasterPageSsoRedirect.test.tsx +444 -0
  83. package/Tests/App/Dashboard/DashboardNavBarForeignHiddenRule.test.tsx +573 -0
  84. package/Tests/App/Dashboard/DatabaseChartAxes.test.tsx +431 -0
  85. package/Tests/App/Dashboard/DatabaseContainerDetailBadge.test.tsx +249 -0
  86. package/Tests/App/Dashboard/DatabaseDashboardE2EFixes.test.ts +681 -0
  87. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +1801 -0
  88. package/Tests/App/Dashboard/DatabaseFleetSummaryRefresh.test.tsx +280 -0
  89. package/Tests/App/Dashboard/DatabaseFormHelpers.test.tsx +321 -0
  90. package/Tests/App/Dashboard/DatabaseMetricMonitorLink.test.tsx +795 -0
  91. package/Tests/App/Dashboard/DatabaseOverviewHeader.test.tsx +580 -0
  92. package/Tests/App/Dashboard/DatabaseRecommendationsEngineMetrics.test.ts +270 -0
  93. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +1486 -0
  94. package/Tests/App/Dashboard/DatabaseServerEngineMetricsFilter.test.ts +223 -0
  95. package/Tests/App/Dashboard/DatabaseServerPresentation.test.ts +435 -0
  96. package/Tests/App/Dashboard/DatabaseServerSummary.test.ts +186 -0
  97. package/Tests/App/Dashboard/DatabaseServerTelemetryQueries.test.ts +1553 -0
  98. package/Tests/App/Dashboard/DatabaseTableTruncation.test.tsx +326 -0
  99. package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +512 -0
  100. package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +774 -0
  101. package/Tests/App/Dashboard/DatabaseViewLayout.test.tsx +233 -0
  102. package/Tests/App/Dashboard/DatabaseWorkloadBadge.test.tsx +972 -0
  103. package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +5 -0
  104. package/Tests/App/Dashboard/DiscoveryReviewUnnamedHostHint.test.tsx +1061 -0
  105. package/Tests/App/Dashboard/EventOverlayScope.test.ts +147 -0
  106. package/Tests/App/Dashboard/EventOverlayScopeHook.test.tsx +1 -0
  107. package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +1 -0
  108. package/Tests/App/Dashboard/MetricsViewerDrillDown.test.tsx +310 -0
  109. package/Tests/App/Dashboard/MetricsViewerEntityScopedRows.test.tsx +495 -0
  110. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +132 -1
  111. package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +5 -0
  112. package/Tests/App/Dashboard/ProductOrientation.test.tsx +4 -3
  113. package/Tests/App/Dashboard/RecommendationsLoadedContext.test.tsx +349 -0
  114. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +62 -0
  115. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
  116. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +14 -0
  117. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +2 -0
  118. package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +1085 -0
  119. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +10 -0
  120. package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +2 -1
  121. package/Tests/App/StatusPage/StatusPageHeaderForeignHiddenRule.test.tsx +284 -0
  122. package/Tests/ClassStrings.ts +178 -0
  123. package/Tests/Fixtures/MobilePasskeyUser.json +1 -2
  124. package/Tests/ForeignHiddenRuleGuard.ts +2152 -0
  125. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +2250 -0
  126. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +24 -5
  127. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  128. package/Tests/RenderedMarkup.ts +518 -0
  129. package/Tests/ResponsiveVisibility.test.ts +490 -4
  130. package/Tests/ResponsiveVisibility.ts +505 -52
  131. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  132. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +541 -0
  133. package/Tests/Server/Infrastructure/Postgres/AddUserProjectSsoConsentMigration.test.ts +234 -0
  134. package/Tests/Server/Services/ContainerSnapshotUpsertPostgres.test.ts +319 -0
  135. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +13 -0
  136. package/Tests/Server/Services/DatabaseServerEndpointService.test.ts +2033 -0
  137. package/Tests/Server/Services/DatabaseServerFeedAndOwnerServices.test.ts +529 -0
  138. package/Tests/Server/Services/DatabaseServerLifecycle.test.ts +3302 -0
  139. package/Tests/Server/Services/DatabaseServerRegistries.test.ts +603 -0
  140. package/Tests/Server/Services/DatabaseServerRuleEngines.test.ts +754 -0
  141. package/Tests/Server/Services/DatabaseServerService.test.ts +3558 -0
  142. package/Tests/Server/Services/DatabaseServerSqlPostgres.test.ts +2045 -0
  143. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedPostgres.test.ts +362 -0
  144. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedReply.test.ts +210 -0
  145. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +18 -0
  146. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +2 -0
  147. package/Tests/Server/Services/IncomingCallPolicyEscalationRuleTarget.test.ts +225 -0
  148. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +280 -2
  149. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +8 -2
  150. package/Tests/Server/Services/IoTDeviceLatestMetricsPostgres.test.ts +689 -0
  151. package/Tests/Server/Services/IoTDeviceService.test.ts +379 -0
  152. package/Tests/Server/Services/LatestMetricsValuesShape.test.ts +360 -0
  153. package/Tests/Server/Services/LlmModelPriceService.test.ts +402 -0
  154. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +7 -1
  155. package/Tests/Server/Services/OwnerRowUniqueness.test.ts +61 -9
  156. package/Tests/Server/Services/ReminderRuleLookupPostgres.test.ts +2250 -0
  157. package/Tests/Server/Services/ReminderRuleRefreshOpenSubjectsPaging.test.ts +653 -0
  158. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +68 -20
  159. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
  160. package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +9 -0
  161. package/Tests/Server/Services/UserProjectSsoConsentPostgres.test.ts +153 -0
  162. package/Tests/Server/Services/UserProjectSsoConsentService.test.ts +192 -0
  163. package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +71 -0
  164. package/Tests/Server/Services/UserSignupIdTakeoverPostgres.test.ts +238 -0
  165. package/Tests/Server/Services/WorkspaceNotificationSummaryHelpers.test.ts +592 -0
  166. package/Tests/Server/Types/Billing/PayAsYouGoMeteredAuthorization.test.ts +3 -0
  167. package/Tests/Server/Types/Database/EventOverlayScopePostgres.test.ts +1 -0
  168. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +11 -0
  169. package/Tests/Server/Types/Database/Permissions/InfrastructureOwnershipScope.test.ts +8 -0
  170. package/Tests/Server/Types/Database/QueryHelperBooleanForCriteriaBackedRule.test.ts +1094 -0
  171. package/Tests/Server/Utils/AI/ResourceTools.test.ts +134 -0
  172. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +41 -0
  173. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +279 -9
  174. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaDisplayContext.test.ts +933 -0
  175. package/Tests/Server/Utils/Monitor/DatabaseMaintenanceSuppression.test.ts +582 -0
  176. package/Tests/Server/Utils/Monitor/DatabaseServerMonitorLinking.test.ts +753 -0
  177. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +1 -0
  178. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +1 -0
  179. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +119 -0
  180. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +60 -25
  181. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +1719 -0
  182. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesAffectedResources.test.ts +1447 -0
  183. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesRatioEmail.test.ts +352 -0
  184. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformMetricUnits.test.ts +717 -0
  185. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformRawScan.test.ts +2125 -0
  186. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformSeriesAffectedResources.test.ts +1288 -0
  187. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +1 -0
  188. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +1 -0
  189. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +1 -0
  190. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +88 -0
  191. package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +205 -0
  192. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +62 -0
  193. package/Tests/Server/Utils/Monitor/MonitorResourceDatabaseMaintenance.test.ts +803 -0
  194. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +133 -3
  195. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +1 -0
  196. package/Tests/Server/Utils/Monitor/PerSeriesRecoveryResolution.test.ts +472 -1
  197. package/Tests/Server/Utils/Monitor/PerSeriesSecondEntityAlerting.test.ts +1 -0
  198. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +236 -2
  199. package/Tests/Server/Utils/SeriesResourceLabels.test.ts +70 -0
  200. package/Tests/Server/Utils/StartServerProtobufContentType.test.ts +54 -0
  201. package/Tests/Server/Utils/Telemetry/DatabaseEndpointDiscovery.test.ts +1895 -0
  202. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +7 -0
  203. package/Tests/Server/Utils/Telemetry/LogExceptionExtractorSeverityGate.test.ts +90 -0
  204. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +570 -0
  205. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +97 -2
  206. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +1337 -0
  207. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +295 -0
  208. package/Tests/Types/AI/AIResourceContext.test.ts +30 -0
  209. package/Tests/Types/DatabaseServer/DatabaseContainerClassifier.test.ts +3644 -0
  210. package/Tests/Types/DatabaseServer/DatabaseContainerCommand.test.ts +340 -0
  211. package/Tests/Types/DatabaseServer/DatabaseContainerCommandCorpus.ts +826 -0
  212. package/Tests/Types/DatabaseServer/DatabaseContainerCommandPostgres.test.ts +150 -0
  213. package/Tests/Types/DatabaseServer/DatabaseEndpoint.test.ts +2657 -0
  214. package/Tests/Types/DatabaseServer/DatabaseServerDiscoverySource.test.ts +64 -0
  215. package/Tests/Types/DatabaseServer/DatabaseServerEntityKeys.test.ts +952 -0
  216. package/Tests/Types/DatabaseServer/DatabaseServerMetricCatalog.test.ts +1161 -0
  217. package/Tests/Types/DatabaseServer/DatabaseSystem.test.ts +1514 -0
  218. package/Tests/Types/DatabaseServer/DatabaseTelemetryResolver.test.ts +1558 -0
  219. package/Tests/Types/Monitor/CephAlertTemplates.test.ts +255 -6
  220. package/Tests/Types/Monitor/DatabaseAlertTemplates.test.ts +2254 -0
  221. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +298 -0
  222. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +271 -0
  223. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +253 -9
  224. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +13 -5
  225. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +4 -2
  226. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +111 -2
  227. package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +61 -0
  228. package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +114 -0
  229. package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +181 -0
  230. package/Tests/Types/NetworkDevice/DiscoveredHostNamingStatus.test.ts +401 -0
  231. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +4 -1
  232. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +24 -2
  233. package/Tests/UI/Components/Card.test.tsx +93 -2
  234. package/Tests/UI/Components/FeedOptions.test.ts +6 -0
  235. package/Tests/UI/Components/FeedOptionsButton.test.tsx +8 -1
  236. package/Tests/UI/Components/ForeignHiddenRuleResilience.test.tsx +1147 -0
  237. package/Tests/UI/Components/HeaderRightRail.test.tsx +41 -1
  238. package/Tests/UI/Components/LogsEntityNames.test.ts +16 -1
  239. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +7 -3
  240. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +2 -1
  241. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  242. package/Tests/UI/Components/SrOnlyForeignRule.test.tsx +994 -0
  243. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +34 -1
  244. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +4 -1
  245. package/Tests/UI/ForeignHiddenRuleGuard.test.ts +1951 -0
  246. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +19 -1
  247. package/Tests/UI/Rum/ReplayTabSwitcher.test.tsx +19 -1
  248. package/Tests/UI/TailwindPlayCdnForeignHiddenRule.test.ts +1005 -0
  249. package/Tests/UI/ThemeGroupHoverReveal.test.ts +847 -0
  250. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +70 -0
  251. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +21 -0
  252. package/Tests/UI/Utils/ModelAPISsoRedirect.test.ts +127 -0
  253. package/Tests/UI/Utils/Navigation.test.ts +102 -0
  254. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +23 -0
  255. package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
  256. package/Tests/Utils/MetricUnitUtil.test.ts +227 -2
  257. package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +288 -2
  258. package/Tests/Utils/Monitor/PlatformResourceIdentityAndUnits.test.ts +1559 -0
  259. package/Tests/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.test.ts +2546 -0
  260. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +315 -0
  261. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +336 -1
  262. package/Tests/Utils/NetworkDiscovery/NetbiosNameUtil.test.ts +244 -0
  263. package/Tests/Utils/ValueFormatter.test.ts +50 -0
  264. package/Tests/tsconfig.json +16 -0
  265. package/Types/AI/AIResourceContext.ts +7 -0
  266. package/Types/Dashboard/DashboardTemplates.ts +2 -1
  267. package/Types/DatabaseServer/DatabaseConnectionSpan.ts +77 -0
  268. package/Types/DatabaseServer/DatabaseContainerClassifier.ts +2068 -0
  269. package/Types/DatabaseServer/DatabaseContainerCommand.ts +801 -0
  270. package/Types/DatabaseServer/DatabaseEndpoint.ts +1943 -0
  271. package/Types/DatabaseServer/DatabaseServerDiscoverySource.ts +68 -0
  272. package/Types/DatabaseServer/DatabaseServerMetricCatalog.ts +966 -0
  273. package/Types/DatabaseServer/DatabaseSystem.ts +2124 -0
  274. package/Types/DatabaseServer/DatabaseTelemetryResolver.ts +555 -0
  275. package/Types/Email/EmailTemplateType.ts +1 -0
  276. package/Types/Monitor/CephAlertTemplates.ts +18 -0
  277. package/Types/Monitor/DatabaseAlertTemplates.ts +2020 -0
  278. package/Types/Monitor/KubernetesAlertTemplates.ts +64 -24
  279. package/Types/Monitor/KubernetesMetricCatalog.ts +118 -19
  280. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +14 -0
  281. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +93 -10
  282. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +109 -8
  283. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +36 -7
  284. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +43 -2
  285. package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +41 -6
  286. package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +26 -0
  287. package/Types/NetworkDevice/DiscoveredHostNamingStatus.ts +147 -0
  288. package/Types/Permission.ts +267 -0
  289. package/Types/Probe/ProbeApiIngestResponse.ts +8 -0
  290. package/Types/Rules/RuleCriteriaFieldRegistry.ts +10 -0
  291. package/Types/Rules/RuleRun.ts +12 -0
  292. package/Types/Telemetry/EntityType.ts +23 -0
  293. package/Types/Telemetry/ResourceFacetCatalog.ts +14 -0
  294. package/Types/Telemetry/ServiceType.ts +8 -0
  295. package/UI/Components/Banner/Banner.tsx +1 -1
  296. package/UI/Components/Card/Card.tsx +1 -1
  297. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +2 -2
  298. package/UI/Components/Charts/Line/LineChart.tsx +7 -0
  299. package/UI/Components/CommandPalette/CommandPalette.tsx +1 -1
  300. package/UI/Components/Date/StartAndEndDate.tsx +5 -5
  301. package/UI/Components/Diagram/HorizontalStepChain.tsx +1 -1
  302. package/UI/Components/Feed/FeedOptionsButton.tsx +1 -1
  303. package/UI/Components/Forms/BasicForm.tsx +1 -1
  304. package/UI/Components/Header/Header.tsx +1 -1
  305. package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -1
  306. package/UI/Components/HeaderAlert/HeaderAlert.tsx +1 -1
  307. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +1 -1
  308. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +1 -1
  309. package/UI/Components/Modal/ModalFooter.tsx +1 -1
  310. package/UI/Components/ModelDetail/CardModelDetail.tsx +2 -2
  311. package/UI/Components/ModelTable/BaseModelTable.tsx +4 -4
  312. package/UI/Components/Navbar/NavBar.tsx +9 -2
  313. package/UI/Components/Navbar/NavBarMenuModal.tsx +2 -2
  314. package/UI/Components/Page/Page.tsx +1 -1
  315. package/UI/Components/Pagination/Pagination.tsx +3 -3
  316. package/UI/Components/ProgressButtons/ProgressButtonItem.tsx +1 -1
  317. package/UI/Components/SideMenu/SideMenu.tsx +4 -1
  318. package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +4 -2
  319. package/UI/Components/StatusPage/ResourceList.tsx +6 -6
  320. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +1 -1
  321. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +1 -1
  322. package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +1 -1
  323. package/UI/Components/Workflow/ComponentsModal.tsx +2 -2
  324. package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
  325. package/UI/Styles/Theme.css +8 -1
  326. package/UI/Utils/Navigation.ts +89 -42
  327. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +7 -0
  328. package/UI/Utils/TelemetryService.ts +4 -3
  329. package/Utils/LabelRuleImportExport.ts +2 -0
  330. package/Utils/MetricUnitUtil.ts +103 -11
  331. package/Utils/Monitor/MetricValueFormatter.ts +66 -13
  332. package/Utils/Monitor/PlatformMetricUnitUtil.ts +215 -0
  333. package/Utils/Monitor/PlatformResourceIdentity.ts +514 -0
  334. package/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.ts +425 -0
  335. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +32 -0
  336. package/Utils/NetworkDiscovery/NetbiosNameUtil.ts +73 -0
  337. package/Utils/Telemetry/DatabaseServerEntityKeys.ts +275 -0
  338. package/Utils/Telemetry/EntityKey.ts +176 -0
  339. package/Utils/ValueFormatter.ts +16 -0
  340. package/build/dist/Models/DatabaseModels/Alert.js +54 -0
  341. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  342. package/build/dist/Models/DatabaseModels/DatabaseServer.js +1683 -0
  343. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -0
  344. package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js +559 -0
  345. package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js.map +1 -0
  346. package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js +648 -0
  347. package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js.map +1 -0
  348. package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js +522 -0
  349. package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js.map +1 -0
  350. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js +603 -0
  351. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js.map +1 -0
  352. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js +506 -0
  353. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js.map +1 -0
  354. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js +505 -0
  355. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js.map +1 -0
  356. package/build/dist/Models/DatabaseModels/Incident.js +54 -0
  357. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  358. package/build/dist/Models/DatabaseModels/Index.js +16 -0
  359. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  360. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  361. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +54 -0
  362. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  363. package/build/dist/Models/DatabaseModels/TelemetryException.js +2 -0
  364. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  365. package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js +162 -0
  366. package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js.map +1 -0
  367. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js +240 -0
  368. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js.map +1 -0
  369. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js +26 -0
  370. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js.map +1 -0
  371. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  372. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  373. package/build/dist/Server/Middleware/MultipartFormData.js +1 -1
  374. package/build/dist/Server/Middleware/TelemetryIngest.js +15 -8
  375. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  376. package/build/dist/Server/Services/AlertReminderRuleService.js +72 -23
  377. package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
  378. package/build/dist/Server/Services/DatabaseServerEndpointService.js +694 -0
  379. package/build/dist/Server/Services/DatabaseServerEndpointService.js.map +1 -0
  380. package/build/dist/Server/Services/DatabaseServerFeedService.js +110 -0
  381. package/build/dist/Server/Services/DatabaseServerFeedService.js.map +1 -0
  382. package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js +272 -0
  383. package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js.map +1 -0
  384. package/build/dist/Server/Services/DatabaseServerLabelRuleService.js +13 -0
  385. package/build/dist/Server/Services/DatabaseServerLabelRuleService.js.map +1 -0
  386. package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js +340 -0
  387. package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js.map +1 -0
  388. package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js +13 -0
  389. package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js.map +1 -0
  390. package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js +160 -0
  391. package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js.map +1 -0
  392. package/build/dist/Server/Services/DatabaseServerOwnerUserService.js +153 -0
  393. package/build/dist/Server/Services/DatabaseServerOwnerUserService.js.map +1 -0
  394. package/build/dist/Server/Services/DatabaseServerService.js +3272 -0
  395. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -0
  396. package/build/dist/Server/Services/DatabaseService.js +8 -4
  397. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  398. package/build/dist/Server/Services/DockerResourceService.js +30 -3
  399. package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
  400. package/build/dist/Server/Services/IncidentReminderRuleService.js +73 -23
  401. package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
  402. package/build/dist/Server/Services/Index.js +16 -0
  403. package/build/dist/Server/Services/Index.js.map +1 -1
  404. package/build/dist/Server/Services/InventoryItemService.js +88 -5
  405. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  406. package/build/dist/Server/Services/IoTDeviceService.js +111 -24
  407. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  408. package/build/dist/Server/Services/OpenTelemetryIngestService.js +24 -9
  409. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  410. package/build/dist/Server/Services/PodmanResourceService.js +30 -3
  411. package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
  412. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +75 -23
  413. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
  414. package/build/dist/Server/Services/ServiceService.js +1 -1
  415. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  416. package/build/dist/Server/Services/TelemetryUsageBillingService.js +23 -2
  417. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  418. package/build/dist/Server/Services/UserProjectSsoConsentService.js +79 -0
  419. package/build/dist/Server/Services/UserProjectSsoConsentService.js.map +1 -0
  420. package/build/dist/Server/Services/UserService.js +13 -1
  421. package/build/dist/Server/Services/UserService.js.map +1 -1
  422. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +18 -0
  423. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
  424. package/build/dist/Server/Types/Database/QueryHelper.js +26 -1
  425. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  426. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +46 -0
  427. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  428. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +6 -0
  429. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -1
  430. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +134 -13
  431. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  432. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -6
  433. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  434. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1489 -524
  435. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  436. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +22 -6
  437. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  438. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +196 -6
  439. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
  440. package/build/dist/Server/Utils/Monitor/MonitorResource.js +36 -9
  441. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  442. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +1 -0
  443. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -1
  444. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +175 -17
  445. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
  446. package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js +29 -0
  447. package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js.map +1 -0
  448. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +211 -0
  449. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  450. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +82 -10
  451. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  452. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
  453. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  454. package/build/dist/Server/Utils/StartServer.js +20 -4
  455. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  456. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
  457. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
  458. package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js +442 -0
  459. package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js.map +1 -0
  460. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +5 -1
  461. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  462. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +15 -1
  463. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
  464. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +121 -5
  465. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  466. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +12 -0
  467. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  468. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +438 -36
  469. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -1
  470. package/build/dist/Server/Utils/Telemetry/Telemetry.js +12 -0
  471. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  472. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +17 -18
  473. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  474. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +16 -10
  475. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -1
  476. package/build/dist/Types/AI/AIResourceContext.js +7 -0
  477. package/build/dist/Types/AI/AIResourceContext.js.map +1 -1
  478. package/build/dist/Types/Dashboard/DashboardTemplates.js +2 -1
  479. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  480. package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js +71 -0
  481. package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js.map +1 -0
  482. package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js +1462 -0
  483. package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js.map +1 -0
  484. package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js +691 -0
  485. package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js.map +1 -0
  486. package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js +1407 -0
  487. package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js.map +1 -0
  488. package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js +60 -0
  489. package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js.map +1 -0
  490. package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js +804 -0
  491. package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js.map +1 -0
  492. package/build/dist/Types/DatabaseServer/DatabaseSystem.js +1860 -0
  493. package/build/dist/Types/DatabaseServer/DatabaseSystem.js.map +1 -0
  494. package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js +406 -0
  495. package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js.map +1 -0
  496. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  497. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  498. package/build/dist/Types/Monitor/CephAlertTemplates.js +18 -0
  499. package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
  500. package/build/dist/Types/Monitor/DatabaseAlertTemplates.js +1518 -0
  501. package/build/dist/Types/Monitor/DatabaseAlertTemplates.js.map +1 -0
  502. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +57 -24
  503. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  504. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +104 -18
  505. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
  506. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +80 -3
  507. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  508. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +10 -3
  509. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  510. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +29 -0
  511. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  512. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +21 -6
  513. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -1
  514. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +25 -0
  515. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -1
  516. package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js +136 -0
  517. package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js.map +1 -0
  518. package/build/dist/Types/Permission.js +234 -0
  519. package/build/dist/Types/Permission.js.map +1 -1
  520. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +10 -0
  521. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  522. package/build/dist/Types/Rules/RuleRun.js +4 -0
  523. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  524. package/build/dist/Types/Telemetry/EntityType.js +23 -0
  525. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  526. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +14 -0
  527. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -1
  528. package/build/dist/Types/Telemetry/ServiceType.js +8 -0
  529. package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
  530. package/build/dist/UI/Components/Banner/Banner.js +1 -1
  531. package/build/dist/UI/Components/Banner/Banner.js.map +1 -1
  532. package/build/dist/UI/Components/Card/Card.js +1 -1
  533. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  534. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
  535. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  536. package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
  537. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  538. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +1 -1
  539. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
  540. package/build/dist/UI/Components/Date/StartAndEndDate.js +5 -5
  541. package/build/dist/UI/Components/Date/StartAndEndDate.js.map +1 -1
  542. package/build/dist/UI/Components/Diagram/HorizontalStepChain.js +1 -1
  543. package/build/dist/UI/Components/Diagram/HorizontalStepChain.js.map +1 -1
  544. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +1 -1
  545. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -1
  546. package/build/dist/UI/Components/Forms/BasicForm.js +1 -1
  547. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  548. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  549. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  550. package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js +1 -1
  551. package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js.map +1 -1
  552. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +1 -1
  553. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  554. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  555. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  556. package/build/dist/UI/Components/Modal/ModalFooter.js +1 -1
  557. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  558. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
  559. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  560. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +4 -4
  561. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  562. package/build/dist/UI/Components/Navbar/NavBar.js +9 -2
  563. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  564. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
  565. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  566. package/build/dist/UI/Components/Page/Page.js +1 -1
  567. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  568. package/build/dist/UI/Components/Pagination/Pagination.js +3 -3
  569. package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
  570. package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js +1 -1
  571. package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js.map +1 -1
  572. package/build/dist/UI/Components/SideMenu/SideMenu.js +4 -1
  573. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  574. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +3 -1
  575. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -1
  576. package/build/dist/UI/Components/StatusPage/ResourceList.js +6 -6
  577. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  578. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  579. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  580. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +1 -1
  581. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  582. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +1 -1
  583. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -1
  584. package/build/dist/UI/Components/Workflow/ComponentsModal.js +2 -2
  585. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  586. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
  587. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
  588. package/build/dist/UI/Utils/Navigation.js +66 -33
  589. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  590. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +7 -0
  591. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -1
  592. package/build/dist/UI/Utils/TelemetryService.js +3 -2
  593. package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
  594. package/build/dist/Utils/LabelRuleImportExport.js +2 -0
  595. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  596. package/build/dist/Utils/MetricUnitUtil.js +77 -13
  597. package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
  598. package/build/dist/Utils/Monitor/MetricValueFormatter.js +58 -13
  599. package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -1
  600. package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js +161 -0
  601. package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js.map +1 -0
  602. package/build/dist/Utils/Monitor/PlatformResourceIdentity.js +323 -0
  603. package/build/dist/Utils/Monitor/PlatformResourceIdentity.js.map +1 -0
  604. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js +270 -0
  605. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js.map +1 -0
  606. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +23 -0
  607. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
  608. package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js +61 -0
  609. package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js.map +1 -1
  610. package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js +178 -0
  611. package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js.map +1 -0
  612. package/build/dist/Utils/Telemetry/EntityKey.js +133 -0
  613. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  614. package/build/dist/Utils/ValueFormatter.js +15 -0
  615. package/build/dist/Utils/ValueFormatter.js.map +1 -1
  616. package/package.json +1 -1
  617. package/tsconfig.tests.json +7 -5
@@ -0,0 +1,1061 @@
1
+ import "@testing-library/jest-dom";
2
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
3
+ import {
4
+ act,
5
+ cleanup,
6
+ fireEvent,
7
+ render,
8
+ screen,
9
+ waitFor,
10
+ } from "@testing-library/react";
11
+ import React from "react";
12
+ import { MemoryRouter } from "react-router-dom";
13
+ import DiscoveryPage from "../../../../App/FeatureSet/Dashboard/src/Pages/NetworkDevice/Discovery";
14
+ import ProbeUtil from "../../../../App/FeatureSet/Dashboard/src/Utils/Probe";
15
+ import NetworkDeviceDiscoveryScan, {
16
+ DiscoveredNetworkDevice,
17
+ } from "../../../Models/DatabaseModels/NetworkDeviceDiscoveryScan";
18
+ import Probe from "../../../Models/DatabaseModels/Probe";
19
+ import Project from "../../../Models/DatabaseModels/Project";
20
+ import ObjectID from "../../../Types/ObjectID";
21
+ import Route from "../../../Types/API/Route";
22
+ import Permission from "../../../Types/Permission";
23
+ import { VoidFunction } from "../../../Types/FunctionTypes";
24
+ import {
25
+ DiscoveredHostNetbiosStatus,
26
+ DiscoveredHostReverseDnsStatus,
27
+ } from "../../../Types/NetworkDevice/DiscoveredHostNamingStatus";
28
+ import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
29
+ import ProjectUtil from "../../../UI/Utils/Project";
30
+ import PermissionUtil from "../../../UI/Utils/Permission";
31
+ import { ComponentProps as ModalProps } from "../../../UI/Components/Modal/Modal";
32
+ import {
33
+ ASK_THE_DEVICE_TIP,
34
+ DiscoveredHostNamingExplanation,
35
+ DiscoveredHostNamingScan,
36
+ FAILED_SCAN_EXPLANATION,
37
+ IN_PROGRESS_EXPLANATION,
38
+ NETBIOS_NOT_RECORDED_SENTENCE,
39
+ NETBIOS_OFF_SENTENCE,
40
+ NOT_YET_NAMED_HOST_LABEL,
41
+ NO_RECORD_TIP,
42
+ REVERSE_DNS_NOT_RECORDED_SENTENCE,
43
+ SNMP_NOT_CHECKED_SENTENCE,
44
+ SNMP_NO_SYSNAME_SENTENCE,
45
+ TRANSIENT_FAILURE_TIP,
46
+ UNNAMED_HOST_LABEL,
47
+ explainUnnamedDiscoveredHost,
48
+ } from "../../../Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis";
49
+ import { DiscoveryScanStatus } from "../../../Utils/NetworkDiscovery/DiscoveryScanStatus";
50
+
51
+ /*
52
+ * OneUptime issue #3916, RENDERED: the Review Discovered Devices dialog says
53
+ * why a host is listed by its bare address.
54
+ *
55
+ * The report: an ICMP-only scan of twelve kitchen displays named four of them
56
+ * by reverse DNS and listed the other eight as addresses, with nothing
57
+ * anywhere to say why. The probe now records per unnamed host what each
58
+ * naming lookup came back with, Common's explainUnnamedDiscoveredHost turns
59
+ * that (or, for a row from an older probe, what the scan was set to ask) into
60
+ * fixed sentences, and the row shows a muted "No name found" and an (i)
61
+ * carrying those sentences beside the name line.
62
+ *
63
+ * The wording itself is pinned by Common's own unit tests of that function.
64
+ * What this file pins is the WIRING, through the real page with only the
65
+ * scans table and the modal chrome mocked (the same harness as
66
+ * DiscoveryReviewInventoryRefresh.test.tsx):
67
+ *
68
+ * - the (i) is on unnamed rows and ONLY on unnamed rows;
69
+ * - hovering it shows exactly the explanation for THAT host under THIS
70
+ * scan — so the row hands the function the fresh scan (status, SNMP,
71
+ * NetBIOS) and whether the scan's probe is a global one;
72
+ * - the dialog's fresh read selects isNetbiosLookupEnabled, without which
73
+ * every scan would read as "NetBIOS off";
74
+ * - nothing the scanned host chose reaches the tooltip.
75
+ *
76
+ * Each explanation is asserted twice over: EXACTLY equal to what the Common
77
+ * function says for the inputs the test states (which is what catches a row
78
+ * passing the wrong scan or dropping the global-probe answer), and containing
79
+ * the specific sentences that make the case distinguishable (which is what
80
+ * stops the first assertion from passing because both sides went blank).
81
+ *
82
+ * Asserting tippy under jsdom: see Common/Tests/UI/Components/
83
+ * InfoTooltip.test.tsx. The (i) is lazy, so no tooltip exists until it is
84
+ * hovered; the popup is portalled to document.body and never finishes
85
+ * animating out, so each explanation is read through the aria-describedby
86
+ * link Tippy puts on the very (i) that was hovered, rather than by assuming
87
+ * it is the only tooltip on the page.
88
+ */
89
+
90
+ interface TableProps {
91
+ actionButtons: Array<{
92
+ title: string;
93
+ onClick: (
94
+ scan: NetworkDeviceDiscoveryScan,
95
+ onComplete: VoidFunction,
96
+ ) => Promise<void>;
97
+ }>;
98
+ }
99
+
100
+ let capturedTable: TableProps | null = null;
101
+
102
+ jest.mock("../../../UI/Components/ModelTable/ModelTable", () => {
103
+ return {
104
+ __esModule: true,
105
+ default: (props: TableProps) => {
106
+ capturedTable = props;
107
+ return null;
108
+ },
109
+ };
110
+ });
111
+
112
+ jest.mock("../../../UI/Components/Modal/Modal", () => {
113
+ return {
114
+ __esModule: true,
115
+ ModalWidth: { Medium: 1 },
116
+ default: (props: ModalProps) => {
117
+ return (
118
+ <div role="dialog" aria-label={props.title}>
119
+ <p>{props.description}</p>
120
+ {props.error && <p role="alert">{props.error}</p>}
121
+ {props.isBodyLoading ? <p>Refreshing inventory</p> : props.children}
122
+ <button onClick={props.onClose}>Close review</button>
123
+ <button
124
+ disabled={props.disableSubmitButton || props.isLoading}
125
+ onClick={props.onSubmit}
126
+ >
127
+ {props.submitButtonText}
128
+ </button>
129
+ </div>
130
+ );
131
+ },
132
+ };
133
+ });
134
+
135
+ const PROJECT_ID: ObjectID = new ObjectID(
136
+ "11111111-1111-4111-8111-111111111111",
137
+ );
138
+ const SCAN_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
139
+ const OTHER_SCAN_ID: ObjectID = new ObjectID(
140
+ "33333333-3333-4333-8333-333333333333",
141
+ );
142
+ // The probe the scans below ran on, unless a test says otherwise.
143
+ const PROBE_ID: ObjectID = new ObjectID("44444444-4444-4444-8444-444444444444");
144
+ const OTHER_PROBE_ID: ObjectID = new ObjectID(
145
+ "55555555-5555-4555-8555-555555555555",
146
+ );
147
+
148
+ // Every (i) this feature draws is named "About why <address> has no name".
149
+ const UNNAMED_HINT_NAME: RegExp = /^About why .+ has no name$/;
150
+
151
+ let getItemSpy: jest.SpyInstance;
152
+ let createSpy: jest.SpyInstance;
153
+ let getAllProbesSpy: jest.SpyInstance;
154
+
155
+ interface ScanShape {
156
+ hosts: Array<DiscoveredNetworkDevice>;
157
+ id?: ObjectID | undefined;
158
+ status?: string | undefined;
159
+ statusMessage?: string | undefined;
160
+ isSnmpEnabled?: boolean | undefined;
161
+ isNetbiosLookupEnabled?: boolean | undefined;
162
+ probeId?: ObjectID | undefined;
163
+ }
164
+
165
+ /*
166
+ * A scan as the dialog's fresh read returns it. Only the columns a test sets
167
+ * are set: an unset isNetbiosLookupEnabled is exactly what the dialog would
168
+ * have seen before it started selecting the column.
169
+ */
170
+ function reviewScan(shape: ScanShape): NetworkDeviceDiscoveryScan {
171
+ const value: NetworkDeviceDiscoveryScan = new NetworkDeviceDiscoveryScan();
172
+ value.id = shape.id || SCAN_ID;
173
+ value.projectId = PROJECT_ID;
174
+ value.probeId = shape.probeId || PROBE_ID;
175
+ value.name = "Kitchen displays";
176
+ value.cidr = "10.18.166.0/24";
177
+ value.status = shape.status || DiscoveryScanStatus.Completed;
178
+
179
+ if (shape.statusMessage !== undefined) {
180
+ value.statusMessage = shape.statusMessage;
181
+ }
182
+
183
+ if (shape.isSnmpEnabled !== undefined) {
184
+ value.isSnmpEnabled = shape.isSnmpEnabled;
185
+ }
186
+
187
+ if (shape.isNetbiosLookupEnabled !== undefined) {
188
+ value.isNetbiosLookupEnabled = shape.isNetbiosLookupEnabled;
189
+ }
190
+
191
+ value.discoveredDevices = shape.hosts;
192
+ return value;
193
+ }
194
+
195
+ /*
196
+ * The facts of a scan the explanation reads, stated separately from the scan
197
+ * object so each expected text below says in plain view what it was computed
198
+ * from.
199
+ */
200
+ function namingFactsOf(
201
+ scan: NetworkDeviceDiscoveryScan,
202
+ ): DiscoveredHostNamingScan {
203
+ return {
204
+ status: scan.status,
205
+ isSnmpEnabled: scan.isSnmpEnabled,
206
+ isNetbiosLookupEnabled: scan.isNetbiosLookupEnabled,
207
+ };
208
+ }
209
+
210
+ function expectedExplanation(data: {
211
+ host: DiscoveredNetworkDevice;
212
+ scan: NetworkDeviceDiscoveryScan;
213
+ isGlobalProbe: boolean | undefined;
214
+ }): DiscoveredHostNamingExplanation {
215
+ const explanation: DiscoveredHostNamingExplanation | undefined =
216
+ explainUnnamedDiscoveredHost({
217
+ host: data.host,
218
+ scan: namingFactsOf(data.scan),
219
+ isGlobalProbe: data.isGlobalProbe,
220
+ });
221
+
222
+ // A test that expects an explanation for a named host is a broken test.
223
+ if (!explanation) {
224
+ throw new Error(
225
+ `The fixture ${data.host.ipAddress} is named, so it has no explanation.`,
226
+ );
227
+ }
228
+
229
+ return explanation;
230
+ }
231
+
232
+ function probe(id: ObjectID, isGlobalProbe: boolean): Probe {
233
+ const value: Probe = new Probe();
234
+ value.id = id;
235
+ value.name = isGlobalProbe ? "probe-1" : "Kitchen probe";
236
+ // What ProbeUtil.getAllProbes stamps on every probe it returns.
237
+ value.isGlobalProbe = isGlobalProbe;
238
+ return value;
239
+ }
240
+
241
+ async function renderPage(): Promise<void> {
242
+ const project: Project = new Project();
243
+ project.id = PROJECT_ID;
244
+ render(
245
+ <MemoryRouter>
246
+ <DiscoveryPage
247
+ pageRoute={new Route("/dashboard/network-devices/discovery")}
248
+ currentProject={project}
249
+ hasPaymentMethod={true}
250
+ />
251
+ </MemoryRouter>,
252
+ );
253
+ await waitFor(() => {
254
+ expect(capturedTable).not.toBeNull();
255
+ });
256
+ }
257
+
258
+ function reviewAction(): TableProps["actionButtons"][number] {
259
+ const action: TableProps["actionButtons"][number] | undefined =
260
+ capturedTable?.actionButtons.find(
261
+ (button: TableProps["actionButtons"][number]) => {
262
+ return button.title === "Review Results";
263
+ },
264
+ );
265
+ if (!action) {
266
+ throw new Error("Review Results action was not rendered");
267
+ }
268
+ return action;
269
+ }
270
+
271
+ /*
272
+ * Open Review Results on `scan`, with the dialog's fresh read answering with
273
+ * that same scan — so what is rendered is exactly the fixture.
274
+ */
275
+ async function openReviewOf(scan: NetworkDeviceDiscoveryScan): Promise<void> {
276
+ getItemSpy.mockResolvedValue(scan);
277
+ await act(async () => {
278
+ await reviewAction().onClick(scan, () => {});
279
+ });
280
+ }
281
+
282
+ function unnamedHint(ipAddress: string): HTMLElement {
283
+ return screen.getByTestId(`discovered-device-unnamed-${ipAddress}`);
284
+ }
285
+
286
+ function queryUnnamedHint(ipAddress: string): HTMLElement | null {
287
+ return screen.queryByTestId(`discovered-device-unnamed-${ipAddress}`);
288
+ }
289
+
290
+ function checkbox(ipAddress: string): HTMLElement {
291
+ return screen.getByTestId(`discovered-device-checkbox-${ipAddress}`);
292
+ }
293
+
294
+ /*
295
+ * Hover the row's (i) and return exactly what its tooltip says: the popup
296
+ * Tippy linked to THAT (i) through aria-describedby, so a tooltip left over
297
+ * from an earlier hover cannot be read by mistake. Unhovered afterwards.
298
+ */
299
+ async function explanationShownFor(ipAddress: string): Promise<string> {
300
+ const trigger: HTMLElement = unnamedHint(ipAddress);
301
+
302
+ fireEvent.mouseEnter(trigger);
303
+ await act(async () => {
304
+ jest.advanceTimersByTime(200);
305
+ });
306
+
307
+ const describedBy: string | null = trigger.getAttribute("aria-describedby");
308
+
309
+ expect(describedBy).toBeTruthy();
310
+
311
+ const popup: HTMLElement | null = document.getElementById(
312
+ describedBy as string,
313
+ );
314
+
315
+ expect(popup).not.toBeNull();
316
+
317
+ const text: string = popup!.textContent || "";
318
+
319
+ fireEvent.mouseLeave(trigger);
320
+ await act(async () => {
321
+ jest.advanceTimersByTime(400);
322
+ });
323
+
324
+ return text;
325
+ }
326
+
327
+ /*
328
+ * The row element for an address: the checkbox's row, found by walking up to
329
+ * the element that carries the row's bottom border.
330
+ */
331
+ function rowOf(ipAddress: string): HTMLElement {
332
+ const row: HTMLElement | null = checkbox(ipAddress).closest(".border-b");
333
+
334
+ if (!row) {
335
+ throw new Error(`No row found for ${ipAddress}`);
336
+ }
337
+
338
+ return row as HTMLElement;
339
+ }
340
+
341
+ beforeEach(() => {
342
+ jest.useFakeTimers();
343
+ capturedTable = null;
344
+ jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(PROJECT_ID);
345
+ getAllProbesSpy = jest.spyOn(ProbeUtil, "getAllProbes").mockResolvedValue([]);
346
+ jest
347
+ .spyOn(PermissionUtil, "getAllPermissions")
348
+ .mockReturnValue([Permission.ProjectAdmin]);
349
+ getItemSpy = jest.spyOn(ModelAPI, "getItem");
350
+ createSpy = jest
351
+ .spyOn(ModelAPI, "create")
352
+ .mockResolvedValue(undefined as never);
353
+ });
354
+
355
+ afterEach(() => {
356
+ cleanup();
357
+ jest.restoreAllMocks();
358
+ jest.useRealTimers();
359
+ });
360
+
361
+ /*
362
+ * The reporter's scan, cut down to one row of each kind: ICMP-only, NetBIOS
363
+ * lookup off, run on a project probe. Two hosts named by their PTR records;
364
+ * one the probe's DNS server has no PTR record for; one whose lookup timed
365
+ * out even on the retry; and one stored by a probe older than the per-host
366
+ * codes, which carries none.
367
+ */
368
+ describe("the reporter's ICMP-only scan says why each unnamed host has no name (issue #3916)", () => {
369
+ const NAMED_FIRST: DiscoveredNetworkDevice = {
370
+ ipAddress: "10.18.166.50",
371
+ snmpReachable: false,
372
+ dnsHostname: "wb-0660-kds01.wbhq.com",
373
+ };
374
+ const NO_RECORD: DiscoveredNetworkDevice = {
375
+ ipAddress: "10.18.166.51",
376
+ snmpReachable: false,
377
+ dnsHostnameStatus: DiscoveredHostReverseDnsStatus.NoRecord,
378
+ };
379
+ const NAMED_SECOND: DiscoveredNetworkDevice = {
380
+ ipAddress: "10.18.166.52",
381
+ snmpReachable: false,
382
+ dnsHostname: "wb-0660-kds02.wbhq.com",
383
+ };
384
+ const TIMED_OUT: DiscoveredNetworkDevice = {
385
+ ipAddress: "10.18.166.53",
386
+ snmpReachable: false,
387
+ dnsHostnameStatus: DiscoveredHostReverseDnsStatus.Timeout,
388
+ };
389
+ const LEGACY: DiscoveredNetworkDevice = {
390
+ ipAddress: "10.18.166.54",
391
+ snmpReachable: false,
392
+ };
393
+
394
+ function reporterScan(): NetworkDeviceDiscoveryScan {
395
+ return reviewScan({
396
+ hosts: [NAMED_FIRST, NO_RECORD, NAMED_SECOND, TIMED_OUT, LEGACY],
397
+ isSnmpEnabled: false,
398
+ isNetbiosLookupEnabled: false,
399
+ });
400
+ }
401
+
402
+ beforeEach(() => {
403
+ getAllProbesSpy.mockResolvedValue([probe(PROBE_ID, false)]);
404
+ });
405
+
406
+ test("the (i) is drawn on the three unnamed rows and on neither named one", async () => {
407
+ await renderPage();
408
+ await openReviewOf(reporterScan());
409
+
410
+ expect(queryUnnamedHint("10.18.166.50")).not.toBeInTheDocument();
411
+ expect(queryUnnamedHint("10.18.166.52")).not.toBeInTheDocument();
412
+ expect(unnamedHint("10.18.166.51")).toBeInTheDocument();
413
+ expect(unnamedHint("10.18.166.53")).toBeInTheDocument();
414
+ expect(unnamedHint("10.18.166.54")).toBeInTheDocument();
415
+
416
+ // And nothing else on the page is one: exactly three, one per row.
417
+ expect(
418
+ screen.getAllByRole("button", { name: UNNAMED_HINT_NAME }),
419
+ ).toHaveLength(3);
420
+ });
421
+
422
+ test("each (i) is a button named after the address it explains", async () => {
423
+ await renderPage();
424
+ await openReviewOf(reporterScan());
425
+
426
+ const button: HTMLElement = screen.getByRole("button", {
427
+ name: "About why 10.18.166.51 has no name",
428
+ });
429
+
430
+ expect(button).toBe(unnamedHint("10.18.166.51"));
431
+ expect(button).toHaveAttribute("type", "button");
432
+ });
433
+
434
+ test("a muted 'No name found' sits beside each unnamed name, and only there", async () => {
435
+ await renderPage();
436
+ await openReviewOf(reporterScan());
437
+
438
+ expect(screen.getAllByText(UNNAMED_HOST_LABEL)).toHaveLength(3);
439
+
440
+ for (const ipAddress of ["10.18.166.51", "10.18.166.53", "10.18.166.54"]) {
441
+ const label: HTMLElement | null =
442
+ rowOf(ipAddress).querySelector("span.text-gray-400");
443
+
444
+ expect(label).toHaveTextContent(UNNAMED_HOST_LABEL);
445
+ // Muted, small, and never squeezed out by a long name beside it.
446
+ expect(label).toHaveClass("text-xs", "flex-shrink-0");
447
+ }
448
+
449
+ for (const ipAddress of ["10.18.166.50", "10.18.166.52"]) {
450
+ expect(rowOf(ipAddress)).not.toHaveTextContent(UNNAMED_HOST_LABEL);
451
+ }
452
+ });
453
+
454
+ test("the explanation is not in the page until someone reaches for it", async () => {
455
+ await renderPage();
456
+ await openReviewOf(reporterScan());
457
+
458
+ expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
459
+ expect(
460
+ screen.queryByText(REVERSE_DNS_NOT_RECORDED_SENTENCE, { exact: false }),
461
+ ).not.toBeInTheDocument();
462
+ });
463
+
464
+ test("hovering a host the DNS server has no PTR record for says exactly that", async () => {
465
+ await renderPage();
466
+ await openReviewOf(reporterScan());
467
+
468
+ fireEvent.mouseEnter(unnamedHint("10.18.166.51"));
469
+ await act(async () => {
470
+ jest.advanceTimersByTime(200);
471
+ });
472
+
473
+ const expected: DiscoveredHostNamingExplanation = expectedExplanation({
474
+ host: NO_RECORD,
475
+ scan: reporterScan(),
476
+ isGlobalProbe: false,
477
+ });
478
+
479
+ // The only tooltip on the page, and exactly the Common function's text.
480
+ expect(screen.getByRole("tooltip").textContent).toBe(expected.text);
481
+
482
+ const text: string = screen.getByRole("tooltip").textContent || "";
483
+
484
+ // SNMP was never asked on this scan, and the tooltip says so first.
485
+ expect(text.startsWith(SNMP_NOT_CHECKED_SENTENCE)).toBe(true);
486
+ // The per-host code, not the legacy "nothing was recorded".
487
+ expect(text).toContain("no PTR record");
488
+ expect(text).not.toContain(REVERSE_DNS_NOT_RECORDED_SENTENCE);
489
+ // NetBIOS was off, and the way to ask the device itself is named.
490
+ expect(text).toContain(NETBIOS_OFF_SENTENCE);
491
+ expect(text).toContain(NO_RECORD_TIP);
492
+ });
493
+
494
+ test("hovering a host whose lookup timed out says it timed out, not that it has no record", async () => {
495
+ await renderPage();
496
+ await openReviewOf(reporterScan());
497
+
498
+ const text: string = await explanationShownFor("10.18.166.53");
499
+
500
+ expect(text).toBe(
501
+ expectedExplanation({
502
+ host: TIMED_OUT,
503
+ scan: reporterScan(),
504
+ isGlobalProbe: false,
505
+ }).text,
506
+ );
507
+ expect(text).toContain("did not answer in time");
508
+ expect(text).toContain(TRANSIENT_FAILURE_TIP);
509
+ // The defect #3916 was about: a DNS failure filed as "no PTR record".
510
+ expect(text).not.toContain("no PTR record");
511
+ expect(text).not.toContain(NO_RECORD_TIP);
512
+ });
513
+
514
+ test("hovering a row from an older probe says only what the scan itself can support", async () => {
515
+ await renderPage();
516
+ await openReviewOf(reporterScan());
517
+
518
+ const text: string = await explanationShownFor("10.18.166.54");
519
+
520
+ /*
521
+ * Spelled out from the exported sentences as well as compared to the
522
+ * function: this is the reporter's case exactly — no codes, ICMP only,
523
+ * NetBIOS off — and the one where the honest answer is "SNMP and NetBIOS
524
+ * were never asked, and reverse DNS recorded nothing", plus how to get
525
+ * the device's own name.
526
+ */
527
+ expect(text).toBe(
528
+ expectedExplanation({
529
+ host: LEGACY,
530
+ scan: reporterScan(),
531
+ isGlobalProbe: false,
532
+ }).text,
533
+ );
534
+ expect(text).toBe(
535
+ [
536
+ SNMP_NOT_CHECKED_SENTENCE,
537
+ REVERSE_DNS_NOT_RECORDED_SENTENCE,
538
+ NETBIOS_OFF_SENTENCE,
539
+ ASK_THE_DEVICE_TIP,
540
+ ].join(" "),
541
+ );
542
+ });
543
+
544
+ test("the three unnamed rows are three different explanations", async () => {
545
+ await renderPage();
546
+ await openReviewOf(reporterScan());
547
+
548
+ const texts: Array<string> = [];
549
+
550
+ for (const ipAddress of ["10.18.166.51", "10.18.166.53", "10.18.166.54"]) {
551
+ texts.push(await explanationShownFor(ipAddress));
552
+ }
553
+
554
+ expect(new Set<string>(texts).size).toBe(3);
555
+
556
+ for (const text of texts) {
557
+ expect(text.trim().length).toBeGreaterThan(0);
558
+ }
559
+ });
560
+
561
+ test("the (i) sits on the name line, after the name, not on the address line", async () => {
562
+ await renderPage();
563
+ await openReviewOf(reporterScan());
564
+
565
+ const button: HTMLElement = unnamedHint("10.18.166.51");
566
+ const nameLine: HTMLElement = button.parentElement as HTMLElement;
567
+ const name: HTMLElement = nameLine.firstElementChild as HTMLElement;
568
+
569
+ // The name div is unchanged: truncating, titled, and first.
570
+ expect(name).toHaveAttribute("title", "10.18.166.51");
571
+ expect(name).toHaveClass("truncate");
572
+ expect(name).toHaveTextContent("10.18.166.51");
573
+ // The wrapper lets the name shrink rather than push the (i) off the row.
574
+ expect(nameLine).toHaveClass("flex", "min-w-0");
575
+ // Name, then the muted label, then the (i).
576
+ expect(nameLine.lastElementChild).toBe(button);
577
+
578
+ const addressLine: HTMLElement = nameLine.nextElementSibling as HTMLElement;
579
+
580
+ expect(addressLine).toHaveTextContent("10.18.166.51");
581
+ expect(addressLine).not.toHaveTextContent(UNNAMED_HOST_LABEL);
582
+ expect(addressLine.querySelector("button")).toBeNull();
583
+ });
584
+
585
+ test("a named row's name line is exactly the name, with nothing beside it", async () => {
586
+ await renderPage();
587
+ await openReviewOf(reporterScan());
588
+
589
+ const name: HTMLElement = screen.getByTitle("wb-0660-kds01.wbhq.com");
590
+ const nameLine: HTMLElement = name.parentElement as HTMLElement;
591
+
592
+ expect(nameLine.children).toHaveLength(1);
593
+ expect(nameLine).toHaveTextContent(/^wb-0660-kds01\.wbhq\.com$/);
594
+ });
595
+
596
+ test("the dialog's fresh read selects the NetBIOS setting the explanation needs", async () => {
597
+ await renderPage();
598
+ await openReviewOf(reporterScan());
599
+
600
+ expect(getItemSpy).toHaveBeenCalledTimes(1);
601
+ expect(getItemSpy.mock.calls[0]![0].select).toMatchObject({
602
+ status: true,
603
+ probeId: true,
604
+ isSnmpEnabled: true,
605
+ isNetbiosLookupEnabled: true,
606
+ discoveredDevices: true,
607
+ });
608
+ });
609
+
610
+ test("asking why does not select, deselect or import anything", async () => {
611
+ await renderPage();
612
+ await openReviewOf(reporterScan());
613
+
614
+ expect(checkbox("10.18.166.51")).toBeChecked();
615
+
616
+ fireEvent.click(unnamedHint("10.18.166.51"));
617
+ fireEvent.keyDown(unnamedHint("10.18.166.51"), { key: "Enter" });
618
+
619
+ expect(checkbox("10.18.166.51")).toBeChecked();
620
+ expect(createSpy).not.toHaveBeenCalled();
621
+ expect(
622
+ screen.getByRole("button", { name: /Import Selected/ }),
623
+ ).toHaveTextContent("Import Selected (5)");
624
+ });
625
+
626
+ test("the explanation opens on keyboard focus too", async () => {
627
+ await renderPage();
628
+ await openReviewOf(reporterScan());
629
+
630
+ const button: HTMLElement = unnamedHint("10.18.166.54");
631
+
632
+ act(() => {
633
+ button.focus();
634
+ });
635
+ await act(async () => {
636
+ jest.advanceTimersByTime(200);
637
+ });
638
+
639
+ expect(screen.getByRole("tooltip")).toHaveTextContent(
640
+ REVERSE_DNS_NOT_RECORDED_SENTENCE,
641
+ );
642
+ });
643
+
644
+ test("the unnamed hosts still import under their address, exactly as before", async () => {
645
+ await renderPage();
646
+ await openReviewOf(reporterScan());
647
+
648
+ fireEvent.click(screen.getByRole("button", { name: /Import Selected/ }));
649
+ await waitFor(() => {
650
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
651
+ });
652
+
653
+ const names: Array<string> = createSpy.mock.calls.map(
654
+ (call: Array<{ model: { name?: string } }>): string => {
655
+ return String(call[0]!.model.name);
656
+ },
657
+ );
658
+
659
+ expect(names).toEqual([
660
+ "wb-0660-kds01.wbhq.com",
661
+ "10.18.166.51",
662
+ "wb-0660-kds02.wbhq.com",
663
+ "10.18.166.53",
664
+ "10.18.166.54",
665
+ ]);
666
+ });
667
+ });
668
+
669
+ /*
670
+ * A row with no NetBIOS code, on a scan with NetBIOS turned ON. Either the
671
+ * probe predates the codes, or it is a GLOBAL probe — which never sends
672
+ * NetBIOS queries, and which is what the bundled self-hosted probe-1 and
673
+ * probe-2 register as. The dialog can only tell those apart from the probe
674
+ * list the page loaded, matched to the scan's probe; the probe used to record
675
+ * the refusal in a debug log and nowhere else.
676
+ */
677
+ describe("a row with no NetBIOS code says whether the scan's probe could ever have asked", () => {
678
+ const LEGACY_PING_ONLY: DiscoveredNetworkDevice = {
679
+ ipAddress: "10.18.166.60",
680
+ snmpReachable: false,
681
+ };
682
+
683
+ function netbiosOnScan(
684
+ probeId: ObjectID = PROBE_ID,
685
+ id: ObjectID = SCAN_ID,
686
+ ): NetworkDeviceDiscoveryScan {
687
+ return reviewScan({
688
+ hosts: [LEGACY_PING_ONLY],
689
+ isSnmpEnabled: true,
690
+ isNetbiosLookupEnabled: true,
691
+ probeId: probeId,
692
+ id: id,
693
+ });
694
+ }
695
+
696
+ const GLOBAL_PROBE_SENTENCE: string = "global probes never send NetBIOS";
697
+
698
+ test("on a global probe, the row says NetBIOS was never going to be asked", async () => {
699
+ getAllProbesSpy.mockResolvedValue([probe(PROBE_ID, true)]);
700
+ await renderPage();
701
+ await openReviewOf(netbiosOnScan());
702
+
703
+ const text: string = await explanationShownFor("10.18.166.60");
704
+
705
+ expect(text).toBe(
706
+ expectedExplanation({
707
+ host: LEGACY_PING_ONLY,
708
+ scan: netbiosOnScan(),
709
+ isGlobalProbe: true,
710
+ }).text,
711
+ );
712
+ expect(text).toContain(GLOBAL_PROBE_SENTENCE);
713
+ expect(text).not.toContain(NETBIOS_NOT_RECORDED_SENTENCE);
714
+ });
715
+
716
+ test("on a project probe, the same row says only that no NetBIOS name was recorded", async () => {
717
+ getAllProbesSpy.mockResolvedValue([probe(PROBE_ID, false)]);
718
+ await renderPage();
719
+ await openReviewOf(netbiosOnScan());
720
+
721
+ const text: string = await explanationShownFor("10.18.166.60");
722
+
723
+ expect(text).toBe(
724
+ expectedExplanation({
725
+ host: LEGACY_PING_ONLY,
726
+ scan: netbiosOnScan(),
727
+ isGlobalProbe: false,
728
+ }).text,
729
+ );
730
+ expect(text).toContain(NETBIOS_NOT_RECORDED_SENTENCE);
731
+ expect(text).not.toContain(GLOBAL_PROBE_SENTENCE);
732
+ });
733
+
734
+ test("a global probe that is NOT the scan's probe changes nothing", async () => {
735
+ /*
736
+ * Matched by id: the page's probe list holds global probes as a matter of
737
+ * course, and one of them existing says nothing about the probe this
738
+ * scan ran on.
739
+ */
740
+ getAllProbesSpy.mockResolvedValue([
741
+ probe(OTHER_PROBE_ID, true),
742
+ probe(PROBE_ID, false),
743
+ ]);
744
+ await renderPage();
745
+ await openReviewOf(netbiosOnScan());
746
+
747
+ const text: string = await explanationShownFor("10.18.166.60");
748
+
749
+ expect(text).toContain(NETBIOS_NOT_RECORDED_SENTENCE);
750
+ expect(text).not.toContain(GLOBAL_PROBE_SENTENCE);
751
+ });
752
+
753
+ test("a probe the page does not know is not assumed to be global", async () => {
754
+ /*
755
+ * Deleted since the scan ran, say. Unknown costs the row the more
756
+ * specific sentence; it must never buy it a wrong one.
757
+ */
758
+ getAllProbesSpy.mockResolvedValue([probe(OTHER_PROBE_ID, true)]);
759
+ await renderPage();
760
+ await openReviewOf(netbiosOnScan());
761
+
762
+ const text: string = await explanationShownFor("10.18.166.60");
763
+
764
+ expect(text).toBe(
765
+ expectedExplanation({
766
+ host: LEGACY_PING_ONLY,
767
+ scan: netbiosOnScan(),
768
+ isGlobalProbe: undefined,
769
+ }).text,
770
+ );
771
+ expect(text).toContain(NETBIOS_NOT_RECORDED_SENTENCE);
772
+ expect(text).not.toContain(GLOBAL_PROBE_SENTENCE);
773
+ });
774
+
775
+ test("the answer follows the scan under review, not the first scan reviewed", async () => {
776
+ /*
777
+ * The global-probe answer is worked out per render from the scan the
778
+ * dialog is showing. Reviewing a scan on a global probe and then one on
779
+ * a project probe must not carry the first answer over.
780
+ */
781
+ getAllProbesSpy.mockResolvedValue([
782
+ probe(PROBE_ID, true),
783
+ probe(OTHER_PROBE_ID, false),
784
+ ]);
785
+ await renderPage();
786
+
787
+ await openReviewOf(netbiosOnScan(PROBE_ID, SCAN_ID));
788
+ expect(await explanationShownFor("10.18.166.60")).toContain(
789
+ GLOBAL_PROBE_SENTENCE,
790
+ );
791
+
792
+ fireEvent.click(screen.getByText("Close review"));
793
+ await openReviewOf(netbiosOnScan(OTHER_PROBE_ID, OTHER_SCAN_ID));
794
+
795
+ const text: string = await explanationShownFor("10.18.166.60");
796
+
797
+ expect(text).toContain(NETBIOS_NOT_RECORDED_SENTENCE);
798
+ expect(text).not.toContain(GLOBAL_PROBE_SENTENCE);
799
+ });
800
+
801
+ test("on a global probe with NetBIOS off, the row gets the global probe's explanation", async () => {
802
+ /*
803
+ * The global-probe answer reaches the explanation on a NetBIOS-off scan
804
+ * too, where it decides what the row is advised to turn on: a global
805
+ * probe would never send the queries, so the page must not leave the
806
+ * function believing it might. Compared to the function's own answer for
807
+ * a global probe, so the wording of that advice stays Common's to own.
808
+ */
809
+ const scan: NetworkDeviceDiscoveryScan = reviewScan({
810
+ hosts: [LEGACY_PING_ONLY],
811
+ isSnmpEnabled: false,
812
+ isNetbiosLookupEnabled: false,
813
+ });
814
+
815
+ getAllProbesSpy.mockResolvedValue([probe(PROBE_ID, true)]);
816
+ await renderPage();
817
+ await openReviewOf(scan);
818
+
819
+ const text: string = await explanationShownFor("10.18.166.60");
820
+
821
+ expect(text).toBe(
822
+ expectedExplanation({
823
+ host: LEGACY_PING_ONLY,
824
+ scan: scan,
825
+ isGlobalProbe: true,
826
+ }).text,
827
+ );
828
+ expect(text).toContain(SNMP_NOT_CHECKED_SENTENCE);
829
+ expect(text).toContain(NETBIOS_OFF_SENTENCE);
830
+ });
831
+
832
+ test("a per-host NetBIOS code wins over what the dialog would infer", async () => {
833
+ /*
834
+ * A newer probe stamps what actually happened. A host it queried that
835
+ * never replied says so, whatever the page believes about the probe.
836
+ */
837
+ const queried: DiscoveredNetworkDevice = {
838
+ ...LEGACY_PING_ONLY,
839
+ dnsHostnameStatus: DiscoveredHostReverseDnsStatus.NoRecord,
840
+ netbiosNameStatus: DiscoveredHostNetbiosStatus.NoReply,
841
+ };
842
+ const scan: NetworkDeviceDiscoveryScan = reviewScan({
843
+ hosts: [queried],
844
+ isSnmpEnabled: true,
845
+ isNetbiosLookupEnabled: true,
846
+ });
847
+
848
+ getAllProbesSpy.mockResolvedValue([probe(PROBE_ID, false)]);
849
+ await renderPage();
850
+ await openReviewOf(scan);
851
+
852
+ const text: string = await explanationShownFor("10.18.166.60");
853
+
854
+ expect(text).toBe(
855
+ expectedExplanation({ host: queried, scan: scan, isGlobalProbe: false })
856
+ .text,
857
+ );
858
+ expect(text).toContain("UDP 137");
859
+ expect(text).not.toContain(NETBIOS_NOT_RECORDED_SENTENCE);
860
+ });
861
+ });
862
+
863
+ describe("what the row says depends on the scan's own state", () => {
864
+ test("a scan still sweeping says its hosts are not named YET", async () => {
865
+ /*
866
+ * Names are looked up after the sweep, so every host a running scan has
867
+ * uploaded so far is unnamed by construction — the question has not been
868
+ * asked, and the row must not read as though it had been.
869
+ */
870
+ const host: DiscoveredNetworkDevice = {
871
+ ipAddress: "10.18.166.70",
872
+ snmpReachable: false,
873
+ };
874
+ const scan: NetworkDeviceDiscoveryScan = reviewScan({
875
+ hosts: [host],
876
+ status: DiscoveryScanStatus.InProgress,
877
+ statusMessage: "Swept 64 of 254 addresses.",
878
+ isSnmpEnabled: false,
879
+ });
880
+
881
+ await renderPage();
882
+ await openReviewOf(scan);
883
+
884
+ expect(rowOf("10.18.166.70")).toHaveTextContent(NOT_YET_NAMED_HOST_LABEL);
885
+ expect(rowOf("10.18.166.70")).not.toHaveTextContent(UNNAMED_HOST_LABEL);
886
+ expect(await explanationShownFor("10.18.166.70")).toBe(
887
+ IN_PROGRESS_EXPLANATION,
888
+ );
889
+ });
890
+
891
+ test("a failed scan says it stopped before names were looked up", async () => {
892
+ const host: DiscoveredNetworkDevice = {
893
+ ipAddress: "10.18.166.71",
894
+ snmpReachable: false,
895
+ };
896
+
897
+ await renderPage();
898
+ await openReviewOf(
899
+ reviewScan({ hosts: [host], status: DiscoveryScanStatus.Failed }),
900
+ );
901
+
902
+ expect(rowOf("10.18.166.71")).toHaveTextContent(UNNAMED_HOST_LABEL);
903
+ expect(await explanationShownFor("10.18.166.71")).toBe(
904
+ FAILED_SCAN_EXPLANATION,
905
+ );
906
+ });
907
+
908
+ test("an SNMP responder with a blank sysName says it answered with no name", async () => {
909
+ const host: DiscoveredNetworkDevice = {
910
+ ipAddress: "10.18.166.72",
911
+ snmpReachable: true,
912
+ sysName: " ",
913
+ sysDescr: "Linux kds 5.10",
914
+ dnsHostnameStatus: DiscoveredHostReverseDnsStatus.ServerFailure,
915
+ };
916
+ const scan: NetworkDeviceDiscoveryScan = reviewScan({
917
+ hosts: [host],
918
+ isSnmpEnabled: true,
919
+ isNetbiosLookupEnabled: false,
920
+ });
921
+
922
+ await renderPage();
923
+ await openReviewOf(scan);
924
+
925
+ const text: string = await explanationShownFor("10.18.166.72");
926
+
927
+ expect(text).toBe(
928
+ expectedExplanation({ host: host, scan: scan, isGlobalProbe: undefined })
929
+ .text,
930
+ );
931
+ expect(text.startsWith(SNMP_NO_SYSNAME_SENTENCE)).toBe(true);
932
+ expect(text).toContain("SERVFAIL");
933
+ expect(text).toContain(TRANSIENT_FAILURE_TIP);
934
+ });
935
+ });
936
+
937
+ describe("hosts with a name carry no hint at all", () => {
938
+ test("named by SNMP, by reverse DNS or by NetBIOS: no label and no (i)", async () => {
939
+ const scan: NetworkDeviceDiscoveryScan = reviewScan({
940
+ hosts: [
941
+ { ipAddress: "10.0.0.1", snmpReachable: true, sysName: "core-sw-01" },
942
+ {
943
+ ipAddress: "10.0.0.2",
944
+ snmpReachable: false,
945
+ dnsHostname: "printer.corp.example.com",
946
+ },
947
+ {
948
+ ipAddress: "10.0.0.3",
949
+ snmpReachable: false,
950
+ netbiosName: "accounts-pc01",
951
+ },
952
+ /*
953
+ * A name AND a stale code: a host an earlier pass could not name and
954
+ * a later one did. The name is what counts, so no hint.
955
+ */
956
+ {
957
+ ipAddress: "10.0.0.4",
958
+ snmpReachable: false,
959
+ dnsHostname: "kds04.corp.example.com",
960
+ dnsHostnameStatus: DiscoveredHostReverseDnsStatus.Timeout,
961
+ },
962
+ ],
963
+ isSnmpEnabled: true,
964
+ isNetbiosLookupEnabled: true,
965
+ });
966
+
967
+ await renderPage();
968
+ await openReviewOf(scan);
969
+
970
+ for (const ipAddress of ["10.0.0.1", "10.0.0.2", "10.0.0.3", "10.0.0.4"]) {
971
+ expect(checkbox(ipAddress)).toBeInTheDocument();
972
+ expect(queryUnnamedHint(ipAddress)).not.toBeInTheDocument();
973
+ }
974
+
975
+ expect(
976
+ screen.queryAllByRole("button", { name: UNNAMED_HINT_NAME }),
977
+ ).toHaveLength(0);
978
+ expect(screen.queryByText(UNNAMED_HOST_LABEL)).not.toBeInTheDocument();
979
+ expect(
980
+ screen.queryByText(NOT_YET_NAMED_HOST_LABEL),
981
+ ).not.toBeInTheDocument();
982
+ });
983
+ });
984
+
985
+ /*
986
+ * The tooltip is fixed copy picked by whitelisted codes. A scanned network
987
+ * chooses its PTR records, its NetBIOS answers and its sysDescr, and a probe
988
+ * (or anything that can write the jsonb) chooses the code strings — none of
989
+ * that may be rendered in the explanation.
990
+ */
991
+ describe("nothing the scanned network chose reaches the tooltip", () => {
992
+ test("rejected names and a hostile sysDescr leave the host unnamed, and out of the text", async () => {
993
+ const HOSTILE: string = "<img src=x onerror=alert(1)>";
994
+ const host: DiscoveredNetworkDevice = {
995
+ ipAddress: "10.0.0.9",
996
+ snmpReachable: true,
997
+ sysDescr: `evil ${HOSTILE}`,
998
+ dnsHostname: HOSTILE,
999
+ netbiosName: HOSTILE,
1000
+ dnsHostnameStatus: DiscoveredHostReverseDnsStatus.UnusableName,
1001
+ };
1002
+ const scan: NetworkDeviceDiscoveryScan = reviewScan({
1003
+ hosts: [host],
1004
+ isSnmpEnabled: true,
1005
+ isNetbiosLookupEnabled: true,
1006
+ });
1007
+
1008
+ await renderPage();
1009
+ await openReviewOf(scan);
1010
+
1011
+ // Unnamed: every name it offered was rejected.
1012
+ expect(checkbox("10.0.0.9")).toHaveAttribute(
1013
+ "aria-label",
1014
+ "Import 10.0.0.9 (10.0.0.9)",
1015
+ );
1016
+
1017
+ const text: string = await explanationShownFor("10.0.0.9");
1018
+
1019
+ expect(text).toBe(
1020
+ expectedExplanation({ host: host, scan: scan, isGlobalProbe: undefined })
1021
+ .text,
1022
+ );
1023
+ expect(text).not.toContain("onerror");
1024
+ expect(text).not.toContain("<img");
1025
+ expect(text).not.toContain("evil");
1026
+ expect(text).toContain("not a valid hostname");
1027
+ });
1028
+
1029
+ test("a code that is not one of ours is dropped, and the row falls back to what the scan supports", async () => {
1030
+ const host: DiscoveredNetworkDevice = {
1031
+ ipAddress: "10.0.0.10",
1032
+ snmpReachable: false,
1033
+ };
1034
+ // Written straight into the jsonb, past the type system.
1035
+ (host as unknown as Record<string, unknown>)["dnsHostnameStatus"] =
1036
+ "<b>call 555-0100</b>";
1037
+ (host as unknown as Record<string, unknown>)["netbiosNameStatus"] = 7;
1038
+
1039
+ const scan: NetworkDeviceDiscoveryScan = reviewScan({
1040
+ hosts: [host],
1041
+ isSnmpEnabled: false,
1042
+ isNetbiosLookupEnabled: false,
1043
+ });
1044
+
1045
+ await renderPage();
1046
+ await openReviewOf(scan);
1047
+
1048
+ const text: string = await explanationShownFor("10.0.0.10");
1049
+
1050
+ expect(text).toBe(
1051
+ [
1052
+ SNMP_NOT_CHECKED_SENTENCE,
1053
+ REVERSE_DNS_NOT_RECORDED_SENTENCE,
1054
+ NETBIOS_OFF_SENTENCE,
1055
+ ASK_THE_DEVICE_TIP,
1056
+ ].join(" "),
1057
+ );
1058
+ expect(text).not.toContain("555-0100");
1059
+ expect(text).not.toContain("<b>");
1060
+ });
1061
+ });