@oneuptime/common 12.0.0 → 12.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/Models/DatabaseModels/Host.ts +12 -4
  2. package/Models/DatabaseModels/KubernetesContainer.ts +5 -4
  3. package/Models/DatabaseModels/KubernetesResource.ts +8 -7
  4. package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.ts +12 -2
  5. package/Models/DatabaseModels/PodmanResource.ts +6 -6
  6. package/Models/DatabaseModels/ServiceLevelObjective.ts +91 -0
  7. package/Models/DatabaseModels/StatusPage.ts +95 -0
  8. package/Server/API/TeamMemberAPI.ts +85 -0
  9. package/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.ts +12 -2
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.ts +84 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.ts +40 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.ts +131 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.ts +25 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  15. package/Server/Middleware/ProjectAuthorization.ts +66 -4
  16. package/Server/Services/BillingService.ts +600 -51
  17. package/Server/Services/CephClusterService.ts +47 -5
  18. package/Server/Services/CephResourceService.ts +43 -4
  19. package/Server/Services/CloudResourceService.ts +47 -5
  20. package/Server/Services/DatabaseService.ts +87 -6
  21. package/Server/Services/DockerHostService.ts +47 -5
  22. package/Server/Services/DockerResourceService.ts +62 -8
  23. package/Server/Services/DockerSwarmClusterService.ts +48 -5
  24. package/Server/Services/DockerSwarmResourceService.ts +53 -4
  25. package/Server/Services/HostService.ts +48 -5
  26. package/Server/Services/IoTDeviceService.ts +1 -17
  27. package/Server/Services/IoTFleetService.ts +47 -5
  28. package/Server/Services/KubernetesClusterService.ts +48 -5
  29. package/Server/Services/KubernetesContainerService.ts +50 -9
  30. package/Server/Services/KubernetesResourceService.ts +53 -7
  31. package/Server/Services/LabelService.ts +106 -1
  32. package/Server/Services/LlmProviderService.ts +69 -9
  33. package/Server/Services/MonitorService.ts +57 -0
  34. package/Server/Services/PodmanHostService.ts +47 -5
  35. package/Server/Services/PodmanResourceService.ts +62 -8
  36. package/Server/Services/ProjectService.ts +165 -35
  37. package/Server/Services/ProxmoxClusterService.ts +47 -5
  38. package/Server/Services/ProxmoxResourceService.ts +43 -4
  39. package/Server/Services/RumApplicationService.ts +48 -5
  40. package/Server/Services/RunnerService.ts +1 -7
  41. package/Server/Services/ServerlessFunctionService.ts +48 -5
  42. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +426 -0
  43. package/Server/Services/ServiceLevelObjectiveService.ts +39 -0
  44. package/Server/Services/StatusPageService.ts +95 -36
  45. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +18 -1
  46. package/Server/Services/StatusPageSubscriberService.ts +3 -0
  47. package/Server/Types/Database/JSONColumnQuery.ts +621 -0
  48. package/Server/Types/Database/Permissions/PublicPermission.ts +9 -2
  49. package/Server/Types/Database/QueryHelper.ts +17 -32
  50. package/Server/Utils/Database/TruncateColumnValue.ts +84 -0
  51. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +12 -0
  52. package/Tests/App/Dashboard/RunnerInstallInstructions.test.tsx +119 -0
  53. package/Tests/App/Dashboard/RunnerStatus.test.tsx +274 -0
  54. package/Tests/App/Dashboard/UsersTableGroupsByPerson.test.tsx +448 -0
  55. package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +704 -0
  56. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +303 -0
  57. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +350 -0
  58. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyHeader.test.ts +362 -0
  59. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +687 -0
  60. package/Tests/Server/Services/BillingService.test.ts +78 -3
  61. package/Tests/Server/Services/BillingServiceChangePlanCancel.test.ts +645 -0
  62. package/Tests/Server/Services/BillingServiceTrialPlanChange.test.ts +1005 -0
  63. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +503 -0
  64. package/Tests/Server/Services/HostIpAddressesColumnWidth.test.ts +198 -0
  65. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +510 -0
  66. package/Tests/Server/Services/InventoryResourceNameColumnWidth.test.ts +500 -0
  67. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +586 -0
  68. package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +1242 -0
  69. package/Tests/Server/Services/LlmProviderUsableByProject.test.ts +293 -0
  70. package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +650 -0
  71. package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +1 -0
  72. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +387 -0
  73. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +770 -0
  74. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +130 -0
  75. package/Tests/Server/Services/SloMonitorLabelRuleHooks.test.ts +352 -0
  76. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +29 -13
  77. package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +69 -8
  78. package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +593 -0
  79. package/Tests/Server/Types/Database/Permissions/PublicPermission.test.ts +495 -0
  80. package/Tests/Server/Types/Database/QueryUtil.test.ts +113 -0
  81. package/Tests/Server/Utils/AI/ToolArgsExtractors.test.ts +141 -0
  82. package/Tests/Server/Utils/AI/Toolbox/WidgetBuilder.test.ts +205 -0
  83. package/Tests/Server/Utils/Database/TruncateColumnValue.test.ts +179 -0
  84. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +897 -0
  85. package/Tests/Types/Events/Recurring.test.ts +157 -0
  86. package/Tests/Types/Runner/RunnerLiveStatus.test.ts +320 -0
  87. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +285 -0
  88. package/Tests/UI/Components/Detail/EntityFields.test.tsx +166 -0
  89. package/Tests/UI/Components/ModelTable/ModelTableGroupsProjectUsers.test.tsx +375 -0
  90. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +196 -0
  91. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +739 -0
  92. package/Tests/UI/Utils/TeamMembersByUser.test.ts +633 -0
  93. package/Tests/Utils/StatusPage/ReportPeriod.test.ts +218 -0
  94. package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +291 -0
  95. package/Types/CustomField/CustomFieldDefinition.ts +24 -0
  96. package/Types/Date.ts +112 -0
  97. package/Types/Events/Recurring.ts +99 -1
  98. package/Types/Runbook/RunbookStep.ts +15 -0
  99. package/Types/Runner/RunnerLiveStatus.ts +114 -0
  100. package/Types/StatusPage/StatusPageReport.ts +11 -0
  101. package/Types/StatusPage/StatusPageReportPeriodType.ts +21 -0
  102. package/UI/Components/Detail/Detail.tsx +155 -0
  103. package/UI/Components/ModelTable/CustomFieldColumns.tsx +2 -6
  104. package/UI/Components/ModelTable/useCustomFieldColumns.ts +21 -2
  105. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +305 -0
  106. package/UI/Utils/TeamMembersByUser.ts +302 -0
  107. package/Utils/StatusPage/ReportPeriod.ts +236 -0
  108. package/Utils/Telemetry/HostIpAddresses.ts +98 -0
  109. package/build/dist/Models/DatabaseModels/Host.js +12 -4
  110. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  111. package/build/dist/Models/DatabaseModels/KubernetesContainer.js +4 -4
  112. package/build/dist/Models/DatabaseModels/KubernetesContainer.js.map +1 -1
  113. package/build/dist/Models/DatabaseModels/KubernetesResource.js +7 -7
  114. package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
  115. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js +16 -4
  116. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js.map +1 -1
  117. package/build/dist/Models/DatabaseModels/PodmanResource.js +6 -6
  118. package/build/dist/Models/DatabaseModels/PodmanResource.js.map +1 -1
  119. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +87 -0
  120. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
  121. package/build/dist/Models/DatabaseModels/StatusPage.js +98 -0
  122. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  123. package/build/dist/Server/API/TeamMemberAPI.js +57 -2
  124. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  125. package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js +11 -1
  126. package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js.map +1 -1
  127. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js +45 -0
  128. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js.map +1 -0
  129. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js +31 -0
  130. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js.map +1 -0
  131. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js +90 -0
  132. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js.map +1 -0
  133. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js +14 -0
  134. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js.map +1 -0
  135. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  136. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  137. package/build/dist/Server/Middleware/ProjectAuthorization.js +59 -4
  138. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  139. package/build/dist/Server/Services/BillingService.js +466 -36
  140. package/build/dist/Server/Services/BillingService.js.map +1 -1
  141. package/build/dist/Server/Services/CephClusterService.js +42 -5
  142. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  143. package/build/dist/Server/Services/CephResourceService.js +28 -3
  144. package/build/dist/Server/Services/CephResourceService.js.map +1 -1
  145. package/build/dist/Server/Services/CloudResourceService.js +42 -5
  146. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  147. package/build/dist/Server/Services/DatabaseService.js +69 -2
  148. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  149. package/build/dist/Server/Services/DockerHostService.js +42 -5
  150. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  151. package/build/dist/Server/Services/DockerResourceService.js +34 -4
  152. package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
  153. package/build/dist/Server/Services/DockerSwarmClusterService.js +43 -5
  154. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  155. package/build/dist/Server/Services/DockerSwarmResourceService.js +28 -3
  156. package/build/dist/Server/Services/DockerSwarmResourceService.js.map +1 -1
  157. package/build/dist/Server/Services/HostService.js +43 -5
  158. package/build/dist/Server/Services/HostService.js.map +1 -1
  159. package/build/dist/Server/Services/IoTDeviceService.js +1 -8
  160. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  161. package/build/dist/Server/Services/IoTFleetService.js +42 -5
  162. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  163. package/build/dist/Server/Services/KubernetesClusterService.js +43 -5
  164. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  165. package/build/dist/Server/Services/KubernetesContainerService.js +29 -3
  166. package/build/dist/Server/Services/KubernetesContainerService.js.map +1 -1
  167. package/build/dist/Server/Services/KubernetesResourceService.js +32 -4
  168. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
  169. package/build/dist/Server/Services/LabelService.js +92 -0
  170. package/build/dist/Server/Services/LabelService.js.map +1 -1
  171. package/build/dist/Server/Services/LlmProviderService.js +61 -8
  172. package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
  173. package/build/dist/Server/Services/MonitorService.js +47 -0
  174. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  175. package/build/dist/Server/Services/PodmanHostService.js +42 -5
  176. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  177. package/build/dist/Server/Services/PodmanResourceService.js +34 -4
  178. package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
  179. package/build/dist/Server/Services/ProjectService.js +135 -21
  180. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  181. package/build/dist/Server/Services/ProxmoxClusterService.js +42 -5
  182. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  183. package/build/dist/Server/Services/ProxmoxResourceService.js +28 -3
  184. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  185. package/build/dist/Server/Services/RumApplicationService.js +43 -5
  186. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  187. package/build/dist/Server/Services/RunnerService.js +1 -6
  188. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  189. package/build/dist/Server/Services/ServerlessFunctionService.js +43 -5
  190. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  191. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +347 -0
  192. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -0
  193. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
  194. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  195. package/build/dist/Server/Services/StatusPageService.js +69 -46
  196. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  197. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
  198. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  199. package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
  200. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  201. package/build/dist/Server/Types/Database/JSONColumnQuery.js +402 -0
  202. package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -0
  203. package/build/dist/Server/Types/Database/Permissions/PublicPermission.js +9 -2
  204. package/build/dist/Server/Types/Database/Permissions/PublicPermission.js.map +1 -1
  205. package/build/dist/Server/Types/Database/QueryHelper.js +17 -27
  206. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  207. package/build/dist/Server/Utils/Database/TruncateColumnValue.js +32 -0
  208. package/build/dist/Server/Utils/Database/TruncateColumnValue.js.map +1 -0
  209. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +11 -0
  210. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  211. package/build/dist/Types/CustomField/CustomFieldDefinition.js +2 -0
  212. package/build/dist/Types/CustomField/CustomFieldDefinition.js.map +1 -0
  213. package/build/dist/Types/Date.js +68 -0
  214. package/build/dist/Types/Date.js.map +1 -1
  215. package/build/dist/Types/Events/Recurring.js +63 -0
  216. package/build/dist/Types/Events/Recurring.js.map +1 -1
  217. package/build/dist/Types/Runbook/RunbookStep.js.map +1 -1
  218. package/build/dist/Types/Runner/RunnerLiveStatus.js +69 -0
  219. package/build/dist/Types/Runner/RunnerLiveStatus.js.map +1 -0
  220. package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js +22 -0
  221. package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js.map +1 -0
  222. package/build/dist/UI/Components/Detail/Detail.js +97 -0
  223. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  224. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  225. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +18 -2
  226. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  227. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +213 -0
  228. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -0
  229. package/build/dist/UI/Utils/TeamMembersByUser.js +195 -0
  230. package/build/dist/UI/Utils/TeamMembersByUser.js.map +1 -0
  231. package/build/dist/Utils/StatusPage/ReportPeriod.js +131 -0
  232. package/build/dist/Utils/StatusPage/ReportPeriod.js.map +1 -0
  233. package/build/dist/Utils/Telemetry/HostIpAddresses.js +82 -0
  234. package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -0
  235. package/jest.config.json +2 -0
  236. package/package.json +1 -1
  237. package/tsconfig.json +12 -1
@@ -13,6 +13,7 @@ import AlertStateTimelineService from "../../../Server/Services/AlertStateTimeli
13
13
  import MonitorStatusService from "../../../Server/Services/MonitorStatusService";
14
14
  import ServiceLevelObjectiveService from "../../../Server/Services/ServiceLevelObjectiveService";
15
15
  import ServiceLevelObjectiveBurnRateRuleService from "../../../Server/Services/ServiceLevelObjectiveBurnRateRuleService";
16
+ import ServiceLevelObjectiveMonitorRuleEngineService from "../../../Server/Services/ServiceLevelObjectiveMonitorRuleEngineService";
16
17
  import ServiceLevelObjectiveOwnerTeamService from "../../../Server/Services/ServiceLevelObjectiveOwnerTeamService";
17
18
  import ServiceLevelObjectiveOwnerUserService from "../../../Server/Services/ServiceLevelObjectiveOwnerUserService";
18
19
  import TeamMemberService from "../../../Server/Services/TeamMemberService";
@@ -1986,3 +1987,132 @@ describe("ServiceLevelObjectiveService.getSloLinkInDashboard", () => {
1986
1987
  expect(path).toContain("/slos/");
1987
1988
  });
1988
1989
  });
1990
+
1991
+ /*
1992
+ * The label rule has to be applied by the service that owns the column, not
1993
+ * only by the dashboard that sets it: an SLO created or edited over the API
1994
+ * must come out carrying the monitors its rule implies. Both hooks are
1995
+ * best-effort - a rule that cannot be applied is logged, never allowed to
1996
+ * fail the write that triggered it.
1997
+ */
1998
+ describe("ServiceLevelObjectiveService - applying the monitor label rule", () => {
1999
+ let syncMonitorsForSloSpy: jest.SpyInstance;
2000
+
2001
+ beforeEach(() => {
2002
+ syncMonitorsForSloSpy = jest
2003
+ .spyOn(
2004
+ ServiceLevelObjectiveMonitorRuleEngineService,
2005
+ "syncMonitorsForSlo",
2006
+ )
2007
+ .mockResolvedValue({ monitorIdsAdded: [], monitorIdsRemoved: [] });
2008
+
2009
+ jest
2010
+ .spyOn(ServiceLevelObjectiveService, "updateOneById")
2011
+ .mockResolvedValue(1);
2012
+ jest
2013
+ .spyOn(ServiceLevelObjectiveService, "findOneById")
2014
+ .mockResolvedValue(makeSlo({ projectId: PROJECT_ID }));
2015
+ jest
2016
+ .spyOn(ServiceLevelObjectiveService, "resolveOpenBurnRateAlertsForSlo")
2017
+ .mockResolvedValue(undefined);
2018
+ jest.spyOn(AlertSeverityService, "findOneBy").mockResolvedValue(null);
2019
+ jest
2020
+ .spyOn(ServiceLevelObjectiveBurnRateRuleService, "create")
2021
+ .mockResolvedValue(makeBurnRateRule(RULE_ID));
2022
+ });
2023
+
2024
+ afterEach(() => {
2025
+ jest.restoreAllMocks();
2026
+ });
2027
+
2028
+ it("runs the rule for a newly created SLO so it is not born with an empty monitor list", async () => {
2029
+ const createdItem: ServiceLevelObjective = makeSlo({
2030
+ _id: SLO_ID.toString(),
2031
+ id: SLO_ID,
2032
+ projectId: PROJECT_ID,
2033
+ targetPercentage: 99.9,
2034
+ });
2035
+
2036
+ await callHook(
2037
+ "onCreateSuccess",
2038
+ {
2039
+ createBy: makeCreateBy({
2040
+ projectId: PROJECT_ID,
2041
+ targetPercentage: 99.9,
2042
+ }),
2043
+ carryForward: null,
2044
+ },
2045
+ createdItem,
2046
+ );
2047
+
2048
+ expect(syncMonitorsForSloSpy).toHaveBeenCalledTimes(1);
2049
+ expect(syncMonitorsForSloSpy).toHaveBeenCalledWith({
2050
+ serviceLevelObjectiveId: SLO_ID,
2051
+ });
2052
+ });
2053
+
2054
+ it("does not fail the create when the rule cannot be applied", async () => {
2055
+ syncMonitorsForSloSpy.mockRejectedValue(new Error("db down"));
2056
+
2057
+ const createdItem: ServiceLevelObjective = makeSlo({
2058
+ _id: SLO_ID.toString(),
2059
+ id: SLO_ID,
2060
+ projectId: PROJECT_ID,
2061
+ });
2062
+
2063
+ await expect(
2064
+ callHook(
2065
+ "onCreateSuccess",
2066
+ {
2067
+ createBy: makeCreateBy({ projectId: PROJECT_ID }),
2068
+ carryForward: null,
2069
+ },
2070
+ createdItem,
2071
+ ),
2072
+ ).resolves.toBe(createdItem);
2073
+ });
2074
+
2075
+ it("re-runs the rule for every SLO whose rule was just edited", async () => {
2076
+ await callHook(
2077
+ "onUpdateSuccess",
2078
+ makeOnUpdate({ monitorLabels: [{ _id: RULE_ID.toString() }] }),
2079
+ [SLO_ID, OTHER_RULE_ID],
2080
+ );
2081
+
2082
+ expect(syncMonitorsForSloSpy).toHaveBeenCalledTimes(2);
2083
+ expect(syncMonitorsForSloSpy).toHaveBeenNthCalledWith(1, {
2084
+ serviceLevelObjectiveId: SLO_ID,
2085
+ });
2086
+ expect(syncMonitorsForSloSpy).toHaveBeenNthCalledWith(2, {
2087
+ serviceLevelObjectiveId: OTHER_RULE_ID,
2088
+ });
2089
+ });
2090
+
2091
+ it("re-runs the rule when it is cleared, which is what gives the monitors back", async () => {
2092
+ await callHook("onUpdateSuccess", makeOnUpdate({ monitorLabels: [] }), [
2093
+ SLO_ID,
2094
+ ]);
2095
+
2096
+ expect(syncMonitorsForSloSpy).toHaveBeenCalledTimes(1);
2097
+ });
2098
+
2099
+ it("leaves the rule alone for an edit that does not touch it", async () => {
2100
+ await callHook(
2101
+ "onUpdateSuccess",
2102
+ makeOnUpdate({ targetPercentage: 99.5 }),
2103
+ [SLO_ID],
2104
+ );
2105
+
2106
+ expect(syncMonitorsForSloSpy).not.toHaveBeenCalled();
2107
+ });
2108
+
2109
+ it("does not fail the update when the rule cannot be applied", async () => {
2110
+ syncMonitorsForSloSpy.mockRejectedValue(new Error("db down"));
2111
+
2112
+ await expect(
2113
+ callHook("onUpdateSuccess", makeOnUpdate({ monitorLabels: [] }), [
2114
+ SLO_ID,
2115
+ ]),
2116
+ ).resolves.toBeDefined();
2117
+ });
2118
+ });
@@ -0,0 +1,352 @@
1
+ import Label from "../../../Models/DatabaseModels/Label";
2
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
3
+ import ServiceLevelObjective from "../../../Models/DatabaseModels/ServiceLevelObjective";
4
+ import DatabaseConfig from "../../../Server/DatabaseConfig";
5
+ import LabelService from "../../../Server/Services/LabelService";
6
+ import MonitorFeedService from "../../../Server/Services/MonitorFeedService";
7
+ import MonitorService from "../../../Server/Services/MonitorService";
8
+ import ServiceLevelObjectiveMonitorRuleEngineService from "../../../Server/Services/ServiceLevelObjectiveMonitorRuleEngineService";
9
+ import ServiceLevelObjectiveService from "../../../Server/Services/ServiceLevelObjectiveService";
10
+ import DeleteBy from "../../../Server/Types/Database/DeleteBy";
11
+ import { OnDelete, OnUpdate } from "../../../Server/Types/Database/Hooks";
12
+ import UpdateBy from "../../../Server/Types/Database/UpdateBy";
13
+ import URL from "../../../Types/API/URL";
14
+ import ObjectID from "../../../Types/ObjectID";
15
+ import { describe, expect, it, beforeEach, afterEach } from "@jest/globals";
16
+
17
+ /*
18
+ * Contract under test - the three places that have to notice a label rule
19
+ * became stale. The engine itself is covered separately; what matters here is
20
+ * that something actually calls it.
21
+ *
22
+ * - MonitorService, when a monitor's labels change. Keyed on the field
23
+ * being present rather than non-empty, because clearing every label
24
+ * arrives as `[]` and is exactly the edit that should detach the monitor
25
+ * from its rule-driven SLOs.
26
+ *
27
+ * - LabelService, when a label is deleted. Postgres cascades the rule's
28
+ * join rows away without any service hook firing, so the SLOs that used
29
+ * that label would otherwise keep monitors nothing explains.
30
+ *
31
+ * Every one of them is best-effort: the sync must never fail the write that
32
+ * triggered it.
33
+ */
34
+
35
+ const PROJECT_ID: ObjectID = new ObjectID(
36
+ "22222222-2222-4222-8222-222222222222",
37
+ );
38
+ const MONITOR_ID: ObjectID = new ObjectID(
39
+ "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
40
+ );
41
+ const OTHER_MONITOR_ID: ObjectID = new ObjectID(
42
+ "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
43
+ );
44
+ const LABEL_ID: ObjectID = new ObjectID("dddddddd-dddd-4ddd-8ddd-dddddddddddd");
45
+ const SLO_ID: ObjectID = new ObjectID("11111111-1111-4111-8111-111111111111");
46
+ const OTHER_SLO_ID: ObjectID = new ObjectID(
47
+ "1a1a1a1a-1a1a-4a1a-8a1a-1a1a1a1a1a1a",
48
+ );
49
+
50
+ // Calls a protected hook without widening the service's public surface.
51
+ function callHook(
52
+ service: unknown,
53
+ name: string,
54
+ ...args: Array<unknown>
55
+ ): Promise<unknown> {
56
+ const hooks: Record<
57
+ string,
58
+ (...hookArgs: Array<unknown>) => Promise<unknown>
59
+ > = service as Record<
60
+ string,
61
+ (...hookArgs: Array<unknown>) => Promise<unknown>
62
+ >;
63
+
64
+ return hooks[name]!.apply(service, args);
65
+ }
66
+
67
+ function monitorUpdate(data: Record<string, unknown>): OnUpdate<Monitor> {
68
+ return {
69
+ updateBy: {
70
+ query: { _id: MONITOR_ID.toString() },
71
+ data: data,
72
+ props: { isRoot: true, tenantId: PROJECT_ID },
73
+ limit: 1,
74
+ skip: 0,
75
+ } as unknown as UpdateBy<Monitor>,
76
+ carryForward: null,
77
+ };
78
+ }
79
+
80
+ function fakeMonitorRow(): Monitor {
81
+ return {
82
+ id: MONITOR_ID,
83
+ _id: MONITOR_ID.toString(),
84
+ projectId: PROJECT_ID,
85
+ name: "API",
86
+ } as unknown as Monitor;
87
+ }
88
+
89
+ function fakeLabelRow(id: ObjectID): Label {
90
+ return { id: id, _id: id.toString(), name: "Production" } as unknown as Label;
91
+ }
92
+
93
+ function fakeSloRow(id: ObjectID): ServiceLevelObjective {
94
+ return {
95
+ id: id,
96
+ _id: id.toString(),
97
+ projectId: PROJECT_ID,
98
+ } as unknown as ServiceLevelObjective;
99
+ }
100
+
101
+ describe("MonitorService.onUpdateSuccess - keeping SLO label rules honest", () => {
102
+ let syncSlosForMonitorSpy: jest.SpyInstance;
103
+
104
+ beforeEach(() => {
105
+ syncSlosForMonitorSpy = jest
106
+ .spyOn(
107
+ ServiceLevelObjectiveMonitorRuleEngineService,
108
+ "syncSlosForMonitor",
109
+ )
110
+ .mockResolvedValue([]);
111
+
112
+ jest
113
+ .spyOn(MonitorService, "findOneById")
114
+ .mockResolvedValue(fakeMonitorRow());
115
+ jest
116
+ .spyOn(DatabaseConfig, "getDashboardUrl")
117
+ .mockResolvedValue(URL.fromString("https://oneuptime.test/dashboard"));
118
+ jest.spyOn(LabelService, "findBy").mockResolvedValue([]);
119
+ jest
120
+ .spyOn(MonitorFeedService, "createMonitorFeedItem")
121
+ .mockResolvedValue(undefined as never);
122
+ });
123
+
124
+ afterEach(() => {
125
+ jest.restoreAllMocks();
126
+ });
127
+
128
+ it("re-runs the SLO label rules when a monitor gains a label", async () => {
129
+ await callHook(
130
+ MonitorService,
131
+ "onUpdateSuccess",
132
+ monitorUpdate({ labels: [{ _id: LABEL_ID.toString() }] }),
133
+ [MONITOR_ID],
134
+ );
135
+
136
+ expect(syncSlosForMonitorSpy).toHaveBeenCalledTimes(1);
137
+ expect(syncSlosForMonitorSpy).toHaveBeenCalledWith({
138
+ monitorId: MONITOR_ID,
139
+ projectId: PROJECT_ID,
140
+ });
141
+ });
142
+
143
+ it("re-runs them when the last label is removed - `[]` is an edit, not an absence", async () => {
144
+ await callHook(
145
+ MonitorService,
146
+ "onUpdateSuccess",
147
+ monitorUpdate({ labels: [] }),
148
+ [MONITOR_ID],
149
+ );
150
+
151
+ expect(syncSlosForMonitorSpy).toHaveBeenCalledTimes(1);
152
+ });
153
+
154
+ it("re-runs them for every monitor a bulk label edit touched", async () => {
155
+ await callHook(
156
+ MonitorService,
157
+ "onUpdateSuccess",
158
+ monitorUpdate({ labels: [{ _id: LABEL_ID.toString() }] }),
159
+ [MONITOR_ID, OTHER_MONITOR_ID],
160
+ );
161
+
162
+ expect(syncSlosForMonitorSpy).toHaveBeenCalledTimes(2);
163
+ expect(syncSlosForMonitorSpy).toHaveBeenNthCalledWith(2, {
164
+ monitorId: OTHER_MONITOR_ID,
165
+ projectId: PROJECT_ID,
166
+ });
167
+ });
168
+
169
+ it("does not run them for an edit that cannot change SLO membership", async () => {
170
+ await callHook(
171
+ MonitorService,
172
+ "onUpdateSuccess",
173
+ monitorUpdate({ description: "just a doc tweak" }),
174
+ [MONITOR_ID],
175
+ );
176
+
177
+ expect(syncSlosForMonitorSpy).not.toHaveBeenCalled();
178
+ });
179
+
180
+ it("does not fail the monitor update when the sync throws", async () => {
181
+ syncSlosForMonitorSpy.mockRejectedValue(new Error("db down"));
182
+
183
+ await expect(
184
+ callHook(
185
+ MonitorService,
186
+ "onUpdateSuccess",
187
+ monitorUpdate({ labels: [] }),
188
+ [MONITOR_ID],
189
+ ),
190
+ ).resolves.toBeDefined();
191
+ });
192
+
193
+ it("keeps syncing the remaining monitors after one of them fails", async () => {
194
+ syncSlosForMonitorSpy
195
+ .mockRejectedValueOnce(new Error("db down"))
196
+ .mockResolvedValueOnce([]);
197
+
198
+ await callHook(
199
+ MonitorService,
200
+ "onUpdateSuccess",
201
+ monitorUpdate({ labels: [] }),
202
+ [MONITOR_ID, OTHER_MONITOR_ID],
203
+ );
204
+
205
+ expect(syncSlosForMonitorSpy).toHaveBeenCalledTimes(2);
206
+ });
207
+ });
208
+
209
+ describe("LabelService - a deleted label takes its SLO rule with it", () => {
210
+ let syncMonitorsForSloSpy: jest.SpyInstance;
211
+ let sloFindBySpy: jest.SpyInstance;
212
+ let labelFindBySpy: jest.SpyInstance;
213
+
214
+ beforeEach(() => {
215
+ syncMonitorsForSloSpy = jest
216
+ .spyOn(
217
+ ServiceLevelObjectiveMonitorRuleEngineService,
218
+ "syncMonitorsForSlo",
219
+ )
220
+ .mockResolvedValue({ monitorIdsAdded: [], monitorIdsRemoved: [] });
221
+ sloFindBySpy = jest.spyOn(ServiceLevelObjectiveService, "findBy");
222
+ labelFindBySpy = jest.spyOn(LabelService, "findBy");
223
+ });
224
+
225
+ afterEach(() => {
226
+ jest.restoreAllMocks();
227
+ });
228
+
229
+ function deleteBy(): DeleteBy<Label> {
230
+ return {
231
+ query: { _id: LABEL_ID.toString() },
232
+ props: { isRoot: true },
233
+ limit: 1,
234
+ skip: 0,
235
+ } as unknown as DeleteBy<Label>;
236
+ }
237
+
238
+ it("notes down the SLOs whose rule uses the label, while the label still exists", async () => {
239
+ labelFindBySpy.mockResolvedValue([fakeLabelRow(LABEL_ID)]);
240
+ sloFindBySpy.mockResolvedValue([
241
+ fakeSloRow(SLO_ID),
242
+ fakeSloRow(OTHER_SLO_ID),
243
+ ]);
244
+
245
+ const onDelete: OnDelete<Label> = (await callHook(
246
+ LabelService,
247
+ "onBeforeDelete",
248
+ deleteBy(),
249
+ )) as OnDelete<Label>;
250
+
251
+ const query: { monitorLabels: Array<ObjectID> } = sloFindBySpy.mock
252
+ .calls[0]![0]!.query as { monitorLabels: Array<ObjectID> };
253
+
254
+ expect(
255
+ query.monitorLabels.map((id: ObjectID) => {
256
+ return id.toString();
257
+ }),
258
+ ).toEqual([LABEL_ID.toString()]);
259
+
260
+ const carried: { serviceLevelObjectiveIds: Array<ObjectID> } =
261
+ onDelete.carryForward as { serviceLevelObjectiveIds: Array<ObjectID> };
262
+
263
+ expect(
264
+ carried.serviceLevelObjectiveIds.map((id: ObjectID) => {
265
+ return id.toString();
266
+ }),
267
+ ).toEqual([SLO_ID.toString(), OTHER_SLO_ID.toString()]);
268
+ });
269
+
270
+ it("does not go looking for SLOs when the delete matched no labels", async () => {
271
+ labelFindBySpy.mockResolvedValue([]);
272
+
273
+ await callHook(LabelService, "onBeforeDelete", deleteBy());
274
+
275
+ expect(sloFindBySpy).not.toHaveBeenCalled();
276
+ });
277
+
278
+ it("re-runs the now-smaller rule for each noted SLO once the label is gone", async () => {
279
+ await callHook(
280
+ LabelService,
281
+ "onDeleteSuccess",
282
+ {
283
+ deleteBy: deleteBy(),
284
+ carryForward: { serviceLevelObjectiveIds: [SLO_ID, OTHER_SLO_ID] },
285
+ },
286
+ [LABEL_ID],
287
+ );
288
+
289
+ expect(syncMonitorsForSloSpy).toHaveBeenCalledTimes(2);
290
+ expect(syncMonitorsForSloSpy).toHaveBeenNthCalledWith(1, {
291
+ serviceLevelObjectiveId: SLO_ID,
292
+ });
293
+ expect(syncMonitorsForSloSpy).toHaveBeenNthCalledWith(2, {
294
+ serviceLevelObjectiveId: OTHER_SLO_ID,
295
+ });
296
+ });
297
+
298
+ it("does nothing after deleting a label no SLO rule referenced", async () => {
299
+ await callHook(
300
+ LabelService,
301
+ "onDeleteSuccess",
302
+ { deleteBy: deleteBy(), carryForward: { serviceLevelObjectiveIds: [] } },
303
+ [LABEL_ID],
304
+ );
305
+
306
+ expect(syncMonitorsForSloSpy).not.toHaveBeenCalled();
307
+ });
308
+
309
+ it("tolerates a delete that carried nothing forward", async () => {
310
+ await expect(
311
+ callHook(
312
+ LabelService,
313
+ "onDeleteSuccess",
314
+ { deleteBy: deleteBy(), carryForward: null },
315
+ [LABEL_ID],
316
+ ),
317
+ ).resolves.toBeDefined();
318
+
319
+ expect(syncMonitorsForSloSpy).not.toHaveBeenCalled();
320
+ });
321
+
322
+ it("still deletes the label when the SLO lookup fails", async () => {
323
+ labelFindBySpy.mockRejectedValue(new Error("db down"));
324
+
325
+ const onDelete: OnDelete<Label> = (await callHook(
326
+ LabelService,
327
+ "onBeforeDelete",
328
+ deleteBy(),
329
+ )) as OnDelete<Label>;
330
+
331
+ expect(
332
+ (onDelete.carryForward as { serviceLevelObjectiveIds: Array<ObjectID> })
333
+ .serviceLevelObjectiveIds,
334
+ ).toEqual([]);
335
+ });
336
+
337
+ it("still reports the delete as done when re-running a rule fails", async () => {
338
+ syncMonitorsForSloSpy.mockRejectedValue(new Error("db down"));
339
+
340
+ await expect(
341
+ callHook(
342
+ LabelService,
343
+ "onDeleteSuccess",
344
+ {
345
+ deleteBy: deleteBy(),
346
+ carryForward: { serviceLevelObjectiveIds: [SLO_ID] },
347
+ },
348
+ [LABEL_ID],
349
+ ),
350
+ ).resolves.toBeDefined();
351
+ });
352
+ });
@@ -28,6 +28,10 @@ import StatusPageResource from "../../../Models/DatabaseModels/StatusPageResourc
28
28
  import Dictionary from "../../../Types/Dictionary";
29
29
  import ObjectID from "../../../Types/ObjectID";
30
30
  import OneUptimeDate from "../../../Types/Date";
31
+ import StatusPageReportPeriodType from "../../../Types/StatusPage/StatusPageReportPeriodType";
32
+ import StatusPageReportPeriodUtil, {
33
+ StatusPageReportPeriod,
34
+ } from "../../../Utils/StatusPage/ReportPeriod";
31
35
  import PositiveNumber from "../../../Types/PositiveNumber";
32
36
  import { Green, Red } from "../../../Types/BrandColors";
33
37
  import {
@@ -74,6 +78,18 @@ const WEBSITE_MONITOR: ObjectID = new ObjectID(
74
78
 
75
79
  const HISTORY_DAYS: number = 14;
76
80
 
81
+ /*
82
+ * The window under test: the rolling fourteen days the timeline below is built
83
+ * around. Resolved through the same util the worker uses so these tests keep
84
+ * exercising the real string formatting as well as the boundaries.
85
+ */
86
+ function reportPeriod(): StatusPageReportPeriod {
87
+ return StatusPageReportPeriodUtil.getReportPeriod({
88
+ periodType: StatusPageReportPeriodType.Rolling,
89
+ reportDataInDays: HISTORY_DAYS,
90
+ });
91
+ }
92
+
77
93
  const OPERATIONAL: MonitorStatus = new MonitorStatus();
78
94
  OPERATIONAL.id = new ObjectID("bb000000-0000-4000-8000-000000000001");
79
95
  OPERATIONAL.name = "Operational";
@@ -284,7 +300,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
284
300
  const report: StatusPageReport =
285
301
  await StatusPageService.getReportByStatusPage({
286
302
  statusPageId: STATUS_PAGE_ID,
287
- historyDays: HISTORY_DAYS,
303
+ reportPeriod: reportPeriod(),
288
304
  });
289
305
 
290
306
  expect(report.hasGroups).toBe(true);
@@ -303,7 +319,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
303
319
  const report: StatusPageReport =
304
320
  await StatusPageService.getReportByStatusPage({
305
321
  statusPageId: STATUS_PAGE_ID,
306
- historyDays: HISTORY_DAYS,
322
+ reportPeriod: reportPeriod(),
307
323
  });
308
324
 
309
325
  expect(rowNames(report.rows)).toEqual([
@@ -321,7 +337,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
321
337
  const report: StatusPageReport =
322
338
  await StatusPageService.getReportByStatusPage({
323
339
  statusPageId: STATUS_PAGE_ID,
324
- historyDays: HISTORY_DAYS,
340
+ reportPeriod: reportPeriod(),
325
341
  });
326
342
 
327
343
  const unit: StatusPageReportGroup =
@@ -344,7 +360,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
344
360
  const report: StatusPageReport =
345
361
  await StatusPageService.getReportByStatusPage({
346
362
  statusPageId: STATUS_PAGE_ID,
347
- historyDays: HISTORY_DAYS,
363
+ reportPeriod: reportPeriod(),
348
364
  });
349
365
 
350
366
  const pathByName: Dictionary<string> = {};
@@ -368,7 +384,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
368
384
  const report: StatusPageReport =
369
385
  await StatusPageService.getReportByStatusPage({
370
386
  statusPageId: STATUS_PAGE_ID,
371
- historyDays: HISTORY_DAYS,
387
+ reportPeriod: reportPeriod(),
372
388
  });
373
389
 
374
390
  const uptimeByName: Dictionary<string> = {};
@@ -387,7 +403,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
387
403
  const report: StatusPageReport =
388
404
  await StatusPageService.getReportByStatusPage({
389
405
  statusPageId: STATUS_PAGE_ID,
390
- historyDays: HISTORY_DAYS,
406
+ reportPeriod: reportPeriod(),
391
407
  });
392
408
 
393
409
  const corporate: StatusPageReportGroup = report.groups[0]!;
@@ -405,7 +421,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
405
421
  const report: StatusPageReport =
406
422
  await StatusPageService.getReportByStatusPage({
407
423
  statusPageId: STATUS_PAGE_ID,
408
- historyDays: HISTORY_DAYS,
424
+ reportPeriod: reportPeriod(),
409
425
  });
410
426
 
411
427
  const corporate: StatusPageReportGroup = report.groups[0]!;
@@ -418,7 +434,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
418
434
  const report: StatusPageReport =
419
435
  await StatusPageService.getReportByStatusPage({
420
436
  statusPageId: STATUS_PAGE_ID,
421
- historyDays: HISTORY_DAYS,
437
+ reportPeriod: reportPeriod(),
422
438
  });
423
439
 
424
440
  const corporate: StatusPageReportGroup = report.groups[0]!;
@@ -441,7 +457,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
441
457
 
442
458
  await StatusPageService.getReportByStatusPage({
443
459
  statusPageId: STATUS_PAGE_ID,
444
- historyDays: HISTORY_DAYS,
460
+ reportPeriod: reportPeriod(),
445
461
  });
446
462
 
447
463
  /*
@@ -456,7 +472,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
456
472
  const report: StatusPageReport =
457
473
  await StatusPageService.getReportByStatusPage({
458
474
  statusPageId: STATUS_PAGE_ID,
459
- historyDays: HISTORY_DAYS,
475
+ reportPeriod: reportPeriod(),
460
476
  });
461
477
 
462
478
  expect(report.totalResources).toBe(3);
@@ -471,7 +487,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
471
487
  const report: StatusPageReport =
472
488
  await StatusPageService.getReportByStatusPage({
473
489
  statusPageId: STATUS_PAGE_ID,
474
- historyDays: HISTORY_DAYS,
490
+ reportPeriod: reportPeriod(),
475
491
  });
476
492
 
477
493
  expect(
@@ -513,7 +529,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
513
529
  const report: StatusPageReport =
514
530
  await StatusPageService.getReportByStatusPage({
515
531
  statusPageId: STATUS_PAGE_ID,
516
- historyDays: HISTORY_DAYS,
532
+ reportPeriod: reportPeriod(),
517
533
  });
518
534
 
519
535
  expect(report.hasGroups).toBe(false);
@@ -541,7 +557,7 @@ describe("StatusPageService.getReportByStatusPage", () => {
541
557
  const report: StatusPageReport =
542
558
  await StatusPageService.getReportByStatusPage({
543
559
  statusPageId: STATUS_PAGE_ID,
544
- historyDays: HISTORY_DAYS,
560
+ reportPeriod: reportPeriod(),
545
561
  });
546
562
 
547
563
  expect(report.totalResources).toBe(0);