@oneuptime/common 13.0.8 → 14.0.0

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 (333) hide show
  1. package/Models/AnalyticsModels/AuditLog.ts +23 -15
  2. package/Models/DatabaseModels/GlobalConfig.ts +24 -0
  3. package/Models/DatabaseModels/Monitor.ts +3 -2
  4. package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
  5. package/Scripts/generate-service-worker.js +65 -4
  6. package/Server/API/BaseAPI.ts +26 -5
  7. package/Server/API/GlobalConfigAPI.ts +228 -462
  8. package/Server/API/ProjectOIDC.ts +25 -16
  9. package/Server/API/ProjectSSO.ts +24 -16
  10. package/Server/API/StatusPageAPI.ts +100 -42
  11. package/Server/API/TeamMemberAPI.ts +10 -1
  12. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  13. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  14. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  15. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  16. package/Server/EnvironmentConfig.ts +147 -2
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  19. package/Server/Middleware/UserAuthorization.ts +38 -16
  20. package/Server/Services/AuditLogService.ts +84 -1079
  21. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  22. package/Server/Services/GlobalConfigService.ts +80 -1
  23. package/Server/Services/MonitorProbeService.ts +116 -20
  24. package/Server/Services/MonitorService.ts +56 -4
  25. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  26. package/Server/Services/MonitorTemplateService.ts +26 -0
  27. package/Server/Services/ProjectService.ts +284 -0
  28. package/Server/Services/RumSessionPinService.ts +25 -6
  29. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  30. package/Server/Services/StatusPageService.ts +78 -1
  31. package/Server/Services/TeamMemberService.ts +11 -0
  32. package/Server/Services/TeamService.ts +10 -0
  33. package/Server/Services/UserService.ts +10 -17
  34. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  35. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  36. package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
  37. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  38. package/Server/Types/Markdown.ts +41 -0
  39. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  40. package/Server/Utils/EditionEnforcement.ts +233 -0
  41. package/Server/Utils/FrontendEnvironment.ts +41 -2
  42. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  43. package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
  44. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  45. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
  46. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  47. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  48. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  49. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  50. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  51. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  52. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  53. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  54. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  55. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  56. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  57. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  58. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  59. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  60. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
  61. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  62. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  63. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
  64. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  65. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  66. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  67. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  68. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  69. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  70. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  71. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  72. package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
  73. package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
  74. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  75. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  76. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  77. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  78. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  79. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  80. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  81. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  82. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  83. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  84. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  85. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  86. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  87. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  88. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  89. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  90. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  91. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  92. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  93. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  94. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  95. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  96. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  97. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  98. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  99. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  100. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  101. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  102. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  103. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  104. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  105. package/Tests/Server/Types/Markdown.test.ts +181 -0
  106. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  107. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  108. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  109. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  110. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  111. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  112. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
  113. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  114. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  115. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  116. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  117. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  118. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  119. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  120. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
  121. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  122. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  123. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  124. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  125. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  126. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  127. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  128. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  129. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  130. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  131. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  132. package/Types/API/Hostname.ts +63 -16
  133. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  134. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  135. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  136. package/Types/Database/CanonicalModelInstance.ts +102 -0
  137. package/Types/Database/TableColumn.ts +20 -6
  138. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  139. package/Types/IP/IPv6.ts +6 -1
  140. package/Types/Monitor/MonitorStep.ts +6 -1
  141. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  142. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  143. package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
  144. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  145. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  146. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  147. package/UI/Components/ModelList/ModelList.tsx +11 -1
  148. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  149. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  150. package/UI/esbuild-enterprise.js +263 -0
  151. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  152. package/Utils/HostAddressUtil.ts +94 -0
  153. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  154. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  155. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  156. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  157. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  158. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  159. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  160. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  161. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  162. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  163. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  164. package/build/dist/Server/API/BaseAPI.js +26 -5
  165. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  166. package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
  167. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  168. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  169. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  170. package/build/dist/Server/API/ProjectSSO.js +24 -16
  171. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  172. package/build/dist/Server/API/StatusPageAPI.js +93 -43
  173. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  174. package/build/dist/Server/API/TeamMemberAPI.js +8 -1
  175. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  176. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  177. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  178. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  179. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  180. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  181. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  182. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  183. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  184. package/build/dist/Server/EnvironmentConfig.js +84 -2
  185. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  186. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  187. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  188. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  190. package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
  191. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  192. package/build/dist/Server/Services/AuditLogService.js +71 -740
  193. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  194. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  195. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  196. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  197. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  198. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  199. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  200. package/build/dist/Server/Services/MonitorService.js +45 -4
  201. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  202. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  203. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  204. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  205. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  206. package/build/dist/Server/Services/ProjectService.js +214 -0
  207. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  208. package/build/dist/Server/Services/RumSessionPinService.js +24 -6
  209. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  210. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  211. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  212. package/build/dist/Server/Services/StatusPageService.js +71 -0
  213. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  214. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  215. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  216. package/build/dist/Server/Services/TeamService.js +9 -0
  217. package/build/dist/Server/Services/TeamService.js.map +1 -1
  218. package/build/dist/Server/Services/UserService.js +10 -16
  219. package/build/dist/Server/Services/UserService.js.map +1 -1
  220. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  221. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  222. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  223. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  224. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
  225. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  226. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  227. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  228. package/build/dist/Server/Types/Markdown.js +31 -0
  229. package/build/dist/Server/Types/Markdown.js.map +1 -1
  230. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  231. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  232. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  233. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  234. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  235. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  236. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  237. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  238. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
  239. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  240. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  241. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  242. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
  243. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  245. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  246. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  247. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  248. package/build/dist/Types/API/Hostname.js +59 -11
  249. package/build/dist/Types/API/Hostname.js.map +1 -1
  250. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  251. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  252. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  253. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  254. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  255. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  256. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  257. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  258. package/build/dist/Types/Database/TableColumn.js +19 -6
  259. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  260. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  261. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  262. package/build/dist/Types/IP/IPv6.js +6 -1
  263. package/build/dist/Types/IP/IPv6.js.map +1 -1
  264. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  265. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  266. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  267. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  268. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  269. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
  270. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  271. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  272. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  273. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  274. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  275. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  276. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  277. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  278. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  279. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  280. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  281. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  282. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  283. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  284. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  285. package/build/dist/Utils/HostAddressUtil.js +80 -0
  286. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  287. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  288. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  289. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  290. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  291. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  292. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  293. package/jest.config.json +3 -1
  294. package/package.json +1 -1
  295. package/test-setup.sh +103 -34
  296. package/tsconfig.json +13 -0
  297. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  298. package/Server/API/TeamComplianceAPI.ts +0 -158
  299. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  300. package/Server/Services/TeamComplianceService.ts +0 -733
  301. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  302. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  303. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  304. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  305. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
  306. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  307. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  308. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
  309. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  310. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  311. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  312. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  313. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  314. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  315. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  316. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  317. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  318. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  319. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  320. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  321. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  322. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  323. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  324. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  325. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  326. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  327. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  328. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  329. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  330. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  331. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  332. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  333. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -0,0 +1,659 @@
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 { cleanup, render, screen, waitFor } from "@testing-library/react";
11
+ import React, { FunctionComponent, ReactElement } from "react";
12
+
13
+ /*
14
+ * The OneUptime Health pages in the Admin Dashboard, per edition.
15
+ *
16
+ * Every page keeps its module (App.tsx and the route wiring never change) and
17
+ * its layout. What differs:
18
+ * - Enterprise content (live datastore health, queues, diagnostic logs,
19
+ * telemetry, the query console, ClickHouse cluster health, the live
20
+ * overview) comes from the enterprise plugin, or an upsell on the
21
+ * Community Edition - decided from the edition the server runs (env.js),
22
+ * never from a per-page flag;
23
+ * - ClickHouse capacity and its settings, the instance log, migrations and
24
+ * the support bundle render on every edition;
25
+ * - the landing page is useful on the Community Edition (capacity and the
26
+ * every-edition tools), not a full-page upsell.
27
+ *
28
+ * Billing and the edition are pinned in every test.
29
+ */
30
+
31
+ let billingEnabledForTest: boolean = false;
32
+ let enterpriseEditionForTest: boolean = false;
33
+
34
+ jest.mock("../../../UI/Config", () => {
35
+ const actual: Record<string, unknown> = jest.requireActual(
36
+ "../../../UI/Config",
37
+ ) as Record<string, unknown>;
38
+
39
+ const mocked: Record<string, unknown> = { ...actual };
40
+
41
+ Object.defineProperty(mocked, "BILLING_ENABLED", {
42
+ get: (): boolean => {
43
+ return billingEnabledForTest;
44
+ },
45
+ });
46
+
47
+ Object.defineProperty(mocked, "IS_ENTERPRISE_EDITION", {
48
+ get: (): boolean => {
49
+ return enterpriseEditionForTest;
50
+ },
51
+ });
52
+
53
+ return mocked;
54
+ });
55
+
56
+ // The plugin door reads this object; each test puts in what its build ships.
57
+ jest.mock("@oneuptime/ee-admin-dashboard", () => {
58
+ return { __esModule: true, default: {} };
59
+ });
60
+
61
+ jest.mock("../../../UI/Components/Page/Page", () => {
62
+ const react: typeof import("react") = jest.requireActual(
63
+ "react",
64
+ ) as typeof import("react");
65
+
66
+ return {
67
+ __esModule: true,
68
+ default: (props: {
69
+ title: string;
70
+ children: ReactElement | Array<ReactElement>;
71
+ }): ReactElement => {
72
+ return react.createElement(
73
+ "main",
74
+ { "data-testid": "page", "data-title": props.title },
75
+ props.children,
76
+ );
77
+ },
78
+ };
79
+ });
80
+
81
+ jest.mock("../../../UI/Utils/API/API", () => {
82
+ return {
83
+ __esModule: true,
84
+ default: {
85
+ get: jest.fn(),
86
+ getFriendlyMessage: (err: unknown): string => {
87
+ return err instanceof Error ? err.message : String(err);
88
+ },
89
+ },
90
+ };
91
+ });
92
+
93
+ jest.mock(
94
+ "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/ClickhouseCapacity",
95
+ () => {
96
+ const react: typeof import("react") = jest.requireActual(
97
+ "react",
98
+ ) as typeof import("react");
99
+
100
+ return {
101
+ __esModule: true,
102
+ default: (): ReactElement => {
103
+ return react.createElement("div", {
104
+ "data-testid": "clickhouse-capacity",
105
+ });
106
+ },
107
+ };
108
+ },
109
+ );
110
+
111
+ jest.mock(
112
+ "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/ClickhouseCapacitySettings",
113
+ () => {
114
+ const react: typeof import("react") = jest.requireActual(
115
+ "react",
116
+ ) as typeof import("react");
117
+
118
+ return {
119
+ __esModule: true,
120
+ default: (): ReactElement => {
121
+ return react.createElement("div", {
122
+ "data-testid": "clickhouse-capacity-settings",
123
+ });
124
+ },
125
+ };
126
+ },
127
+ );
128
+
129
+ jest.mock(
130
+ "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/InstanceHealthLogs",
131
+ () => {
132
+ const react: typeof import("react") = jest.requireActual(
133
+ "react",
134
+ ) as typeof import("react");
135
+
136
+ return {
137
+ __esModule: true,
138
+ default: (): ReactElement => {
139
+ return react.createElement("div", {
140
+ "data-testid": "instance-health-logs",
141
+ });
142
+ },
143
+ };
144
+ },
145
+ );
146
+
147
+ import HealthLanding from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/Index";
148
+ import HealthQueues from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/Queues";
149
+ import HealthPostgres from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/Postgres";
150
+ import HealthRedis from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/Redis";
151
+ import HealthLogs from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/Logs";
152
+ import HealthTelemetry from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/Telemetry";
153
+ import HealthQueryConsole, {
154
+ QUERY_CONSOLE_NOT_ON_CLOUD_NOTICE,
155
+ } from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/QueryConsole";
156
+ import HealthClickhouse from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/Clickhouse";
157
+ import HealthInstanceLogs from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/InstanceLogs";
158
+ import HealthPage from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/HealthPage";
159
+ import {
160
+ ENTERPRISE_HEALTH_NOTE_DESCRIPTION,
161
+ EVERY_EDITION_HEALTH_NOTE,
162
+ } from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/EnterpriseHealthUpgrade";
163
+ import { EVERY_EDITION_HEALTH_TOOLS } from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/CommunityHealthOverview";
164
+ import {
165
+ formatBytes,
166
+ summarizeClickhouseCapacity,
167
+ } from "../../../../App/FeatureSet/AdminDashboard/src/Pages/Health/ClickhouseCapacitySummary";
168
+ import {
169
+ AdminDashboardEnterprisePluginKey,
170
+ AdminDashboardEnterprisePlugins,
171
+ EnterprisePluginComponent,
172
+ } from "../../../../App/FeatureSet/AdminDashboard/src/Enterprise/EnterprisePlugins";
173
+ import { getAdminDashboardPlugins } from "../../../../App/FeatureSet/AdminDashboard/src/Enterprise/Plugins";
174
+ import PageMap from "../../../../App/FeatureSet/AdminDashboard/src/Utils/PageMap";
175
+ import RouteMap from "../../../../App/FeatureSet/AdminDashboard/src/Utils/RouteMap";
176
+ import API from "../../../UI/Utils/API/API";
177
+ import Route from "../../../Types/API/Route";
178
+ import { JSONObject } from "../../../Types/JSON";
179
+
180
+ /*
181
+ * The mocked plugin module, reached through the door the shells use (core
182
+ * never imports the plugin specifier itself).
183
+ */
184
+ const plugins: AdminDashboardEnterprisePlugins = getAdminDashboardPlugins();
185
+
186
+ const makePlugin: (testId: string) => EnterprisePluginComponent = (
187
+ testId: string,
188
+ ): EnterprisePluginComponent => {
189
+ const Plugin: FunctionComponent = (): ReactElement => {
190
+ return <div data-testid={testId} />;
191
+ };
192
+
193
+ return Plugin;
194
+ };
195
+
196
+ /*
197
+ * The admin plugin keys that hold a page. LicenseManager is the one that does
198
+ * not: it is the edition dialog's license manager, with props of its own.
199
+ */
200
+ type PagePluginKey = Exclude<
201
+ AdminDashboardEnterprisePluginKey,
202
+ "LicenseManager"
203
+ >;
204
+
205
+ const installPlugin: (key: PagePluginKey) => string = (
206
+ key: PagePluginKey,
207
+ ): string => {
208
+ const testId: string = `plugin-${key}`;
209
+ plugins[key] = makePlugin(testId);
210
+ return testId;
211
+ };
212
+
213
+ const apiGet: jest.Mock = API.get as unknown as jest.Mock;
214
+
215
+ interface GatedPage {
216
+ label: string;
217
+ Page: FunctionComponent;
218
+ pluginKey: PagePluginKey;
219
+ featureName: string;
220
+ }
221
+
222
+ const GATED_PAGES: Array<GatedPage> = [
223
+ {
224
+ label: "Background Queues",
225
+ Page: HealthQueues,
226
+ pluginKey: "HealthQueues",
227
+ featureName: "Background queue health",
228
+ },
229
+ {
230
+ label: "PostgreSQL",
231
+ Page: HealthPostgres,
232
+ pluginKey: "HealthPostgres",
233
+ featureName: "PostgreSQL cluster health",
234
+ },
235
+ {
236
+ label: "Valkey",
237
+ Page: HealthRedis,
238
+ pluginKey: "HealthRedis",
239
+ featureName: "Valkey health",
240
+ },
241
+ {
242
+ label: "Diagnostic Logs",
243
+ Page: HealthLogs,
244
+ pluginKey: "HealthLogs",
245
+ featureName: "Diagnostic logs",
246
+ },
247
+ {
248
+ label: "Telemetry",
249
+ Page: HealthTelemetry,
250
+ pluginKey: "HealthTelemetry",
251
+ featureName: "Telemetry ingestion",
252
+ },
253
+ {
254
+ label: "Query Console",
255
+ Page: HealthQueryConsole,
256
+ pluginKey: "HealthQueryConsole",
257
+ featureName: "Query console",
258
+ },
259
+ ];
260
+
261
+ const CAPACITY: JSONObject = {
262
+ connected: true,
263
+ dataSizeInBytes: 5 * 1024 * 1024 * 1024,
264
+ diskTotalInBytes: 300,
265
+ diskFreeInBytes: 150,
266
+ diskByNode: [
267
+ {
268
+ shardNum: 1,
269
+ host: "ch-1",
270
+ diskName: "default",
271
+ totalInBytes: 100,
272
+ freeInBytes: 60,
273
+ },
274
+ {
275
+ shardNum: 2,
276
+ host: "ch-2",
277
+ diskName: "default",
278
+ totalInBytes: 200,
279
+ freeInBytes: 20,
280
+ },
281
+ ],
282
+ };
283
+
284
+ beforeEach(() => {
285
+ billingEnabledForTest = false;
286
+ enterpriseEditionForTest = false;
287
+
288
+ for (const key of Object.keys(plugins)) {
289
+ delete (plugins as Record<string, unknown>)[key];
290
+ }
291
+
292
+ apiGet.mockReset();
293
+ apiGet.mockImplementation(async (): Promise<{ data: JSONObject }> => {
294
+ return { data: CAPACITY };
295
+ });
296
+ });
297
+
298
+ afterEach(() => {
299
+ cleanup();
300
+ });
301
+
302
+ describe("the Health layout has no edition gate of its own", () => {
303
+ test.each([false, true])(
304
+ "renders its content on either edition (Enterprise=%s)",
305
+ (isEnterprise: boolean) => {
306
+ enterpriseEditionForTest = isEnterprise;
307
+
308
+ render(
309
+ <HealthPage
310
+ title="Migrations"
311
+ currentRoute={RouteMap[PageMap.HEALTH_MIGRATIONS] as Route}
312
+ >
313
+ <div data-testid="content" />
314
+ </HealthPage>,
315
+ );
316
+
317
+ expect(screen.getByTestId("page")).toHaveAttribute(
318
+ "data-title",
319
+ "Migrations",
320
+ );
321
+ expect(screen.getByTestId("content")).toBeInTheDocument();
322
+ expect(
323
+ screen.queryByText("Learn about Enterprise Edition"),
324
+ ).not.toBeInTheDocument();
325
+ },
326
+ );
327
+ });
328
+
329
+ describe.each(GATED_PAGES)("Health > $label", (page: GatedPage) => {
330
+ test("the Community Edition shows the upsell with the Community tools note, and never the plugin", () => {
331
+ enterpriseEditionForTest = false;
332
+ const testId: string = installPlugin(page.pluginKey);
333
+
334
+ render(<page.Page />);
335
+
336
+ expect(
337
+ screen.getByRole("heading", { name: page.featureName }),
338
+ ).toBeInTheDocument();
339
+ expect(screen.getByText(EVERY_EDITION_HEALTH_NOTE)).toBeInTheDocument();
340
+ expect(screen.queryByTestId(testId)).not.toBeInTheDocument();
341
+ expect(screen.getByTestId("page")).toHaveAttribute(
342
+ "data-title",
343
+ page.label,
344
+ );
345
+ });
346
+
347
+ test("the Enterprise Edition renders the plugin inside the Health layout", async () => {
348
+ enterpriseEditionForTest = true;
349
+ const testId: string = installPlugin(page.pluginKey);
350
+
351
+ render(<page.Page />);
352
+
353
+ expect(await screen.findByTestId(testId)).toBeInTheDocument();
354
+ expect(
355
+ screen.queryByRole("heading", { name: page.featureName }),
356
+ ).not.toBeInTheDocument();
357
+ expect(screen.getByTestId("page")).toHaveAttribute(
358
+ "data-title",
359
+ page.label,
360
+ );
361
+ });
362
+
363
+ test("an Enterprise env.js with a Community bundle (no plugin) shows the upsell", () => {
364
+ enterpriseEditionForTest = true;
365
+
366
+ render(<page.Page />);
367
+
368
+ expect(
369
+ screen.getByRole("heading", { name: page.featureName }),
370
+ ).toBeInTheDocument();
371
+ });
372
+ });
373
+
374
+ describe("Health > Query Console on OneUptime Cloud", () => {
375
+ test("says the console is not offered on Cloud, and never renders it", () => {
376
+ billingEnabledForTest = true;
377
+ enterpriseEditionForTest = true;
378
+ const testId: string = installPlugin("HealthQueryConsole");
379
+
380
+ render(<HealthQueryConsole />);
381
+
382
+ expect(
383
+ screen.getByText(QUERY_CONSOLE_NOT_ON_CLOUD_NOTICE),
384
+ ).toBeInTheDocument();
385
+ expect(screen.queryByTestId(testId)).not.toBeInTheDocument();
386
+ expect(
387
+ screen.queryByText("Learn about Enterprise Edition"),
388
+ ).not.toBeInTheDocument();
389
+ });
390
+
391
+ test("other Enterprise pages still render on Cloud", async () => {
392
+ billingEnabledForTest = true;
393
+ enterpriseEditionForTest = true;
394
+ const testId: string = installPlugin("HealthPostgres");
395
+
396
+ render(<HealthPostgres />);
397
+
398
+ expect(await screen.findByTestId(testId)).toBeInTheDocument();
399
+ });
400
+ });
401
+
402
+ describe("Health > ClickHouse", () => {
403
+ test("the Community Edition gets capacity and its settings, with the cluster section as an upsell", () => {
404
+ enterpriseEditionForTest = false;
405
+ const testId: string = installPlugin("HealthClickhouseCluster");
406
+
407
+ render(<HealthClickhouse />);
408
+
409
+ expect(screen.getByTestId("clickhouse-capacity")).toBeInTheDocument();
410
+ expect(
411
+ screen.getByTestId("clickhouse-capacity-settings"),
412
+ ).toBeInTheDocument();
413
+ expect(screen.getByText("ClickHouse cluster health")).toBeInTheDocument();
414
+ expect(screen.queryByTestId(testId)).not.toBeInTheDocument();
415
+ // The page itself shows the Community tools, so no side-menu reminder.
416
+ expect(
417
+ screen.queryByText(EVERY_EDITION_HEALTH_NOTE),
418
+ ).not.toBeInTheDocument();
419
+ });
420
+
421
+ test("the Enterprise Edition gets capacity, the cluster plugin and the settings, in that order", async () => {
422
+ enterpriseEditionForTest = true;
423
+ const testId: string = installPlugin("HealthClickhouseCluster");
424
+
425
+ render(<HealthClickhouse />);
426
+
427
+ const cluster: HTMLElement = await screen.findByTestId(testId);
428
+ const capacity: HTMLElement = screen.getByTestId("clickhouse-capacity");
429
+ const settings: HTMLElement = screen.getByTestId(
430
+ "clickhouse-capacity-settings",
431
+ );
432
+
433
+ expect(
434
+ capacity.compareDocumentPosition(cluster) &
435
+ Node.DOCUMENT_POSITION_FOLLOWING,
436
+ ).toBeTruthy();
437
+ expect(
438
+ cluster.compareDocumentPosition(settings) &
439
+ Node.DOCUMENT_POSITION_FOLLOWING,
440
+ ).toBeTruthy();
441
+ expect(
442
+ screen.queryByText("ClickHouse cluster health"),
443
+ ).not.toBeInTheDocument();
444
+ });
445
+ });
446
+
447
+ describe("Health > Instance Logs", () => {
448
+ test.each([false, true])(
449
+ "renders the instance log on either edition (Enterprise=%s)",
450
+ (isEnterprise: boolean) => {
451
+ enterpriseEditionForTest = isEnterprise;
452
+
453
+ render(<HealthInstanceLogs />);
454
+
455
+ expect(screen.getByTestId("instance-health-logs")).toBeInTheDocument();
456
+ expect(
457
+ screen.queryByText("Learn about Enterprise Edition"),
458
+ ).not.toBeInTheDocument();
459
+ },
460
+ );
461
+ });
462
+
463
+ describe("Health > Overview (the landing page)", () => {
464
+ const expectCommunitySections: () => Promise<void> =
465
+ async (): Promise<void> => {
466
+ expect(
467
+ await screen.findByText(
468
+ "Fullest of 2 disks · Shard 2 · ch-2 · default",
469
+ ),
470
+ ).toBeInTheDocument();
471
+ expect(
472
+ screen.getByText("Available on every edition"),
473
+ ).toBeInTheDocument();
474
+
475
+ for (const tool of EVERY_EDITION_HEALTH_TOOLS) {
476
+ expect(screen.getByText(tool.title).closest("a")).toHaveAttribute(
477
+ "href",
478
+ (RouteMap[tool.page] as Route).toString(),
479
+ );
480
+ }
481
+ };
482
+
483
+ test("the Community Edition shows ClickHouse capacity and the every-edition tools under a short note", async () => {
484
+ enterpriseEditionForTest = false;
485
+ const testId: string = installPlugin("HealthOverview");
486
+
487
+ render(<HealthLanding />);
488
+
489
+ expect(
490
+ screen.getByText(ENTERPRISE_HEALTH_NOTE_DESCRIPTION),
491
+ ).toBeInTheDocument();
492
+ // A note, not the full-page upsell.
493
+ expect(screen.queryByText("Live component health")).not.toBeInTheDocument();
494
+ expect(screen.queryByTestId(testId)).not.toBeInTheDocument();
495
+
496
+ await expectCommunitySections();
497
+ });
498
+
499
+ test("links to Migrations and the Support Bundle on the Community Edition", async () => {
500
+ render(<HealthLanding />);
501
+
502
+ await screen.findByText("Available on every edition");
503
+
504
+ expect(screen.getByText("Migrations").closest("a")).toHaveAttribute(
505
+ "href",
506
+ "/admin/health/migrations",
507
+ );
508
+ expect(screen.getByText("Support Bundle").closest("a")).toHaveAttribute(
509
+ "href",
510
+ "/admin/health/support-bundle",
511
+ );
512
+ });
513
+
514
+ test("the Enterprise Edition shows its live overview above the same Community sections", async () => {
515
+ enterpriseEditionForTest = true;
516
+ const testId: string = installPlugin("HealthOverview");
517
+
518
+ render(<HealthLanding />);
519
+
520
+ const overview: HTMLElement = await screen.findByTestId(testId);
521
+ await expectCommunitySections();
522
+
523
+ expect(
524
+ overview.compareDocumentPosition(
525
+ screen.getByText("Available on every edition"),
526
+ ) & Node.DOCUMENT_POSITION_FOLLOWING,
527
+ ).toBeTruthy();
528
+ expect(
529
+ screen.queryByText(ENTERPRISE_HEALTH_NOTE_DESCRIPTION),
530
+ ).not.toBeInTheDocument();
531
+ });
532
+
533
+ test("asks only the Community capacity endpoint on the Community Edition", async () => {
534
+ render(<HealthLanding />);
535
+
536
+ await screen.findByText("Available on every edition");
537
+ await waitFor(() => {
538
+ expect(apiGet).toHaveBeenCalledTimes(1);
539
+ });
540
+
541
+ const url: string = String(
542
+ (apiGet.mock.calls[0]?.[0] as { url: { toString: () => string } }).url,
543
+ );
544
+
545
+ expect(url).toContain("/admin/health/clickhouse-capacity");
546
+ });
547
+
548
+ test("shows the capacity endpoint's error instead of numbers", async () => {
549
+ apiGet.mockImplementation(async (): Promise<never> => {
550
+ throw new Error("ClickHouse capacity is unavailable");
551
+ });
552
+
553
+ render(<HealthLanding />);
554
+
555
+ expect(
556
+ await screen.findByText("ClickHouse capacity is unavailable"),
557
+ ).toBeInTheDocument();
558
+ });
559
+
560
+ test("says when ClickHouse cannot be reached", async () => {
561
+ apiGet.mockImplementation(async (): Promise<{ data: JSONObject }> => {
562
+ return { data: { connected: false } };
563
+ });
564
+
565
+ render(<HealthLanding />);
566
+
567
+ expect(
568
+ await screen.findByText((content: string): boolean => {
569
+ return content.includes("ClickHouse is not reachable");
570
+ }),
571
+ ).toBeInTheDocument();
572
+ expect(screen.queryByText(/Fullest of/)).not.toBeInTheDocument();
573
+ });
574
+ });
575
+
576
+ describe("summarizeClickhouseCapacity", () => {
577
+ test("names the fullest disk, not the average", () => {
578
+ expect(summarizeClickhouseCapacity(CAPACITY)).toEqual({
579
+ connected: true,
580
+ dataSizeInBytes: 5 * 1024 * 1024 * 1024,
581
+ fullestDisk: {
582
+ label: "Shard 2 · ch-2 · default",
583
+ usedInBytes: 180,
584
+ totalInBytes: 200,
585
+ percent: 90,
586
+ },
587
+ diskCount: 2,
588
+ });
589
+ });
590
+
591
+ test("falls back to the aggregate totals when no node reports", () => {
592
+ expect(
593
+ summarizeClickhouseCapacity({
594
+ connected: true,
595
+ diskTotalInBytes: 400,
596
+ diskFreeInBytes: 100,
597
+ diskByNode: [],
598
+ }).fullestDisk,
599
+ ).toEqual({
600
+ label: "ClickHouse disk",
601
+ usedInBytes: 300,
602
+ totalInBytes: 400,
603
+ percent: 75,
604
+ });
605
+ });
606
+
607
+ test("skips nodes it cannot read, and a zero-sized disk", () => {
608
+ const summary: ReturnType<typeof summarizeClickhouseCapacity> =
609
+ summarizeClickhouseCapacity({
610
+ connected: true,
611
+ diskByNode: [
612
+ { host: "broken", totalInBytes: null, freeInBytes: 1 },
613
+ { host: "empty", totalInBytes: 0, freeInBytes: 0 },
614
+ { host: "ok", totalInBytes: "1000", freeInBytes: "250" },
615
+ ],
616
+ });
617
+
618
+ expect(summary.diskCount).toBe(1);
619
+ expect(summary.fullestDisk?.label).toBe("ok");
620
+ expect(summary.fullestDisk?.percent).toBe(75);
621
+ });
622
+
623
+ test("reports nothing it does not know", () => {
624
+ expect(summarizeClickhouseCapacity(null)).toEqual({
625
+ connected: false,
626
+ dataSizeInBytes: null,
627
+ fullestDisk: null,
628
+ diskCount: 0,
629
+ });
630
+ });
631
+
632
+ test("formatBytes", () => {
633
+ expect(formatBytes(null)).toBe("—");
634
+ expect(formatBytes(0)).toBe("0 B");
635
+ expect(formatBytes(1536)).toBe("1.5 KB");
636
+ expect(formatBytes(5 * 1024 * 1024 * 1024)).toBe("5.0 GB");
637
+ expect(formatBytes(20 * 1024 * 1024 * 1024)).toBe("20 GB");
638
+ });
639
+ });
640
+
641
+ describe("the every-edition tools", () => {
642
+ test("are the Community Health pages, each with a route", () => {
643
+ expect(
644
+ EVERY_EDITION_HEALTH_TOOLS.map((tool: { page: PageMap }): PageMap => {
645
+ return tool.page;
646
+ }),
647
+ ).toEqual([
648
+ PageMap.HEALTH_CLICKHOUSE,
649
+ PageMap.HEALTH_INSTANCE_LOGS,
650
+ PageMap.HEALTH_PROBES,
651
+ PageMap.HEALTH_MIGRATIONS,
652
+ PageMap.HEALTH_SUPPORT_BUNDLE,
653
+ ]);
654
+
655
+ for (const tool of EVERY_EDITION_HEALTH_TOOLS) {
656
+ expect(RouteMap[tool.page]).toBeDefined();
657
+ }
658
+ });
659
+ });