@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.
- package/Models/DatabaseModels/Host.ts +12 -4
- package/Models/DatabaseModels/KubernetesContainer.ts +5 -4
- package/Models/DatabaseModels/KubernetesResource.ts +8 -7
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.ts +12 -2
- package/Models/DatabaseModels/PodmanResource.ts +6 -6
- package/Models/DatabaseModels/ServiceLevelObjective.ts +91 -0
- package/Models/DatabaseModels/StatusPage.ts +95 -0
- package/Server/API/TeamMemberAPI.ts +85 -0
- package/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.ts +12 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.ts +84 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.ts +131 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/ProjectAuthorization.ts +66 -4
- package/Server/Services/BillingService.ts +600 -51
- package/Server/Services/CephClusterService.ts +47 -5
- package/Server/Services/CephResourceService.ts +43 -4
- package/Server/Services/CloudResourceService.ts +47 -5
- package/Server/Services/DatabaseService.ts +87 -6
- package/Server/Services/DockerHostService.ts +47 -5
- package/Server/Services/DockerResourceService.ts +62 -8
- package/Server/Services/DockerSwarmClusterService.ts +48 -5
- package/Server/Services/DockerSwarmResourceService.ts +53 -4
- package/Server/Services/HostService.ts +48 -5
- package/Server/Services/IoTDeviceService.ts +1 -17
- package/Server/Services/IoTFleetService.ts +47 -5
- package/Server/Services/KubernetesClusterService.ts +48 -5
- package/Server/Services/KubernetesContainerService.ts +50 -9
- package/Server/Services/KubernetesResourceService.ts +53 -7
- package/Server/Services/LabelService.ts +106 -1
- package/Server/Services/LlmProviderService.ts +69 -9
- package/Server/Services/MonitorService.ts +57 -0
- package/Server/Services/PodmanHostService.ts +47 -5
- package/Server/Services/PodmanResourceService.ts +62 -8
- package/Server/Services/ProjectService.ts +165 -35
- package/Server/Services/ProxmoxClusterService.ts +47 -5
- package/Server/Services/ProxmoxResourceService.ts +43 -4
- package/Server/Services/RumApplicationService.ts +48 -5
- package/Server/Services/RunnerService.ts +1 -7
- package/Server/Services/ServerlessFunctionService.ts +48 -5
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +426 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +39 -0
- package/Server/Services/StatusPageService.ts +95 -36
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +18 -1
- package/Server/Services/StatusPageSubscriberService.ts +3 -0
- package/Server/Types/Database/JSONColumnQuery.ts +621 -0
- package/Server/Types/Database/Permissions/PublicPermission.ts +9 -2
- package/Server/Types/Database/QueryHelper.ts +17 -32
- package/Server/Utils/Database/TruncateColumnValue.ts +84 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +12 -0
- package/Tests/App/Dashboard/RunnerInstallInstructions.test.tsx +119 -0
- package/Tests/App/Dashboard/RunnerStatus.test.tsx +274 -0
- package/Tests/App/Dashboard/UsersTableGroupsByPerson.test.tsx +448 -0
- package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +704 -0
- package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +303 -0
- package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +350 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyHeader.test.ts +362 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +687 -0
- package/Tests/Server/Services/BillingService.test.ts +78 -3
- package/Tests/Server/Services/BillingServiceChangePlanCancel.test.ts +645 -0
- package/Tests/Server/Services/BillingServiceTrialPlanChange.test.ts +1005 -0
- package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +503 -0
- package/Tests/Server/Services/HostIpAddressesColumnWidth.test.ts +198 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +510 -0
- package/Tests/Server/Services/InventoryResourceNameColumnWidth.test.ts +500 -0
- package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +586 -0
- package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +1242 -0
- package/Tests/Server/Services/LlmProviderUsableByProject.test.ts +293 -0
- package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +650 -0
- package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +1 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +387 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +770 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +130 -0
- package/Tests/Server/Services/SloMonitorLabelRuleHooks.test.ts +352 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +29 -13
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +69 -8
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +593 -0
- package/Tests/Server/Types/Database/Permissions/PublicPermission.test.ts +495 -0
- package/Tests/Server/Types/Database/QueryUtil.test.ts +113 -0
- package/Tests/Server/Utils/AI/ToolArgsExtractors.test.ts +141 -0
- package/Tests/Server/Utils/AI/Toolbox/WidgetBuilder.test.ts +205 -0
- package/Tests/Server/Utils/Database/TruncateColumnValue.test.ts +179 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +897 -0
- package/Tests/Types/Events/Recurring.test.ts +157 -0
- package/Tests/Types/Runner/RunnerLiveStatus.test.ts +320 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +285 -0
- package/Tests/UI/Components/Detail/EntityFields.test.tsx +166 -0
- package/Tests/UI/Components/ModelTable/ModelTableGroupsProjectUsers.test.tsx +375 -0
- package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +196 -0
- package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +739 -0
- package/Tests/UI/Utils/TeamMembersByUser.test.ts +633 -0
- package/Tests/Utils/StatusPage/ReportPeriod.test.ts +218 -0
- package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +291 -0
- package/Types/CustomField/CustomFieldDefinition.ts +24 -0
- package/Types/Date.ts +112 -0
- package/Types/Events/Recurring.ts +99 -1
- package/Types/Runbook/RunbookStep.ts +15 -0
- package/Types/Runner/RunnerLiveStatus.ts +114 -0
- package/Types/StatusPage/StatusPageReport.ts +11 -0
- package/Types/StatusPage/StatusPageReportPeriodType.ts +21 -0
- package/UI/Components/Detail/Detail.tsx +155 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +2 -6
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +21 -2
- package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +305 -0
- package/UI/Utils/TeamMembersByUser.ts +302 -0
- package/Utils/StatusPage/ReportPeriod.ts +236 -0
- package/Utils/Telemetry/HostIpAddresses.ts +98 -0
- package/build/dist/Models/DatabaseModels/Host.js +12 -4
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesContainer.js +4 -4
- package/build/dist/Models/DatabaseModels/KubernetesContainer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesResource.js +7 -7
- package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js +16 -4
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/PodmanResource.js +6 -6
- package/build/dist/Models/DatabaseModels/PodmanResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +87 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +98 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +57 -2
- package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js +11 -1
- package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js +45 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js +90 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +59 -4
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +466 -36
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +42 -5
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CephResourceService.js +28 -3
- package/build/dist/Server/Services/CephResourceService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +42 -5
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +69 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +42 -5
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerResourceService.js +34 -4
- package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +43 -5
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmResourceService.js +28 -3
- package/build/dist/Server/Services/DockerSwarmResourceService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +43 -5
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceService.js +1 -8
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +42 -5
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +43 -5
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesContainerService.js +29 -3
- package/build/dist/Server/Services/KubernetesContainerService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesResourceService.js +32 -4
- package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +92 -0
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/LlmProviderService.js +61 -8
- package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +47 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +42 -5
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/PodmanResourceService.js +34 -4
- package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +135 -21
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +42 -5
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +28 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +43 -5
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +1 -6
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +43 -5
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +347 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +69 -46
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Types/Database/JSONColumnQuery.js +402 -0
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -0
- package/build/dist/Server/Types/Database/Permissions/PublicPermission.js +9 -2
- package/build/dist/Server/Types/Database/Permissions/PublicPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +17 -27
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/Database/TruncateColumnValue.js +32 -0
- package/build/dist/Server/Utils/Database/TruncateColumnValue.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +11 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldDefinition.js +2 -0
- package/build/dist/Types/CustomField/CustomFieldDefinition.js.map +1 -0
- package/build/dist/Types/Date.js +68 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +63 -0
- package/build/dist/Types/Events/Recurring.js.map +1 -1
- package/build/dist/Types/Runbook/RunbookStep.js.map +1 -1
- package/build/dist/Types/Runner/RunnerLiveStatus.js +69 -0
- package/build/dist/Types/Runner/RunnerLiveStatus.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js +22 -0
- package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +97 -0
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +18 -2
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +213 -0
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -0
- package/build/dist/UI/Utils/TeamMembersByUser.js +195 -0
- package/build/dist/UI/Utils/TeamMembersByUser.js.map +1 -0
- package/build/dist/Utils/StatusPage/ReportPeriod.js +131 -0
- package/build/dist/Utils/StatusPage/ReportPeriod.js.map +1 -0
- package/build/dist/Utils/Telemetry/HostIpAddresses.js +82 -0
- package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
560
|
+
reportPeriod: reportPeriod(),
|
|
545
561
|
});
|
|
546
562
|
|
|
547
563
|
expect(report.totalResources).toBe(0);
|