@oneuptime/common 12.0.14 → 12.0.15
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/GlobalOidc.ts +20 -0
- package/Models/DatabaseModels/GlobalSso.ts +20 -0
- package/Models/DatabaseModels/MarketingConversion.ts +19 -9
- package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +46 -0
- package/Models/DatabaseModels/StatusPage.ts +7 -4
- package/Server/API/StatusPageAPI.ts +29 -4
- package/Server/EnvironmentConfig.ts +7 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/SlackAuthorization.ts +43 -6
- package/Server/Middleware/UserAuthorization.ts +236 -26
- package/Server/Middleware/WhatsAppAuthorization.ts +43 -3
- package/Server/Services/GlobalOidcProjectService.ts +126 -1
- package/Server/Services/GlobalOidcService.ts +132 -0
- package/Server/Services/GlobalSsoProjectService.ts +126 -1
- package/Server/Services/GlobalSsoService.ts +132 -0
- package/Server/Services/UserEmailService.ts +4 -4
- package/Server/Utils/CronTab.ts +73 -0
- package/Server/Utils/GlobalSsoAuthorization.ts +175 -0
- package/Server/Utils/Marketing/ConversionUploadProvider.ts +35 -5
- package/Server/Utils/Marketing/Providers/GoogleAds.ts +1 -1
- package/Server/Utils/Marketing/Providers/LinkedIn.ts +1 -1
- package/Server/Utils/Marketing/Providers/Meta.ts +1 -1
- package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +1 -1
- package/Server/Utils/Marketing/Providers/Reddit.ts +1 -1
- package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +48 -33
- package/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.ts +35 -30
- package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +33 -28
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +475 -47
- package/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.ts +35 -30
- package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +34 -28
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +33 -28
- package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +69 -40
- package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +35 -29
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +8 -0
- package/Server/Utils/Monitor/MonitorResource.ts +161 -21
- package/Server/Utils/StatusPageContentSecurityPolicy.ts +56 -0
- package/Server/Utils/StatusPageCustomizationAccess.ts +117 -0
- package/Tests/Server/API/StatusPageCustomizationOrigin.test.ts +348 -0
- package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +9 -59
- package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +227 -0
- package/Tests/Server/Middleware/SlackAuthorization.test.ts +149 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +27 -13
- package/Tests/Server/Middleware/UserAuthorizationGlobalSsoGovernance.test.ts +1455 -0
- package/Tests/Server/Middleware/UserAuthorizationSSOProvider.test.ts +91 -2
- package/Tests/Server/Middleware/WhatsAppAuthorization.test.ts +233 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +21 -8
- package/Tests/Server/Services/GlobalSsoProviderAuthorization.test.ts +1242 -0
- package/Tests/Server/Services/MonitorResourceProbeAgreementHydration.test.ts +459 -0
- package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +10 -0
- package/Tests/Server/Types/AnalyticsDatabase/AggregateUtilBucketTimestamp.test.ts +416 -0
- package/Tests/Server/Types/Database/QueryHelperManyToManyAndEmptyValues.test.ts +551 -0
- package/Tests/Server/Types/Database/SelectUtil.test.ts +348 -0
- package/Tests/Server/Utils/CronTab.test.ts +116 -0
- package/Tests/Server/Utils/GlobalSSOToken.test.ts +581 -0
- package/Tests/Server/Utils/GlobalSsoAuthorization.test.ts +1401 -0
- package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +254 -0
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaEvaluateOverTime.test.ts +508 -0
- package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +30 -9
- package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +987 -0
- package/Tests/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.test.ts +916 -0
- package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +382 -0
- package/Tests/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.test.ts +1219 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +86 -0
- package/Tests/Server/Utils/StatusPageContentSecurityPolicy.test.ts +136 -0
- package/Tests/Server/Utils/StatusPageCustomizationAccess.test.ts +223 -0
- package/Tests/Types/Monitor/MonitorStepConfigHelpers.test.ts +165 -0
- package/Tests/Types/NetworkDevice/NetworkDeviceLinkRuleScope.test.ts +215 -0
- package/Tests/UI/Components/Forms/FormSubmitAnalytics.test.tsx +206 -0
- package/Tests/UI/Utils/AIChatExport/ConversationMarkdown.test.ts +774 -0
- package/Tests/UI/Utils/AIChatExport/MarkdownBlocks.test.ts +791 -0
- package/Tests/UI/Utils/AIChatExport/MarkdownSafety.test.ts +544 -0
- package/Tests/Utils/Analytics.test.ts +187 -0
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +80 -0
- package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +1291 -0
- package/Tests/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.test.ts +214 -0
- package/Types/Marketing/MarketingConversion.ts +12 -0
- package/Types/Monitor/CriteriaFilter.ts +9 -0
- package/Types/NetworkDevice/NetworkDeviceLinkRuleScope.ts +51 -0
- package/UI/Components/FormModal/BasicFormModal.tsx +12 -0
- package/UI/Components/Forms/BasicForm.tsx +13 -1
- package/UI/Components/Forms/BasicModelForm.tsx +2 -1
- package/UI/Components/Forms/ModelForm.tsx +10 -1
- package/UI/Components/Forms/Utils/FormAnalyticsName.ts +39 -0
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +6 -1
- package/Utils/Analytics.ts +55 -2
- package/Utils/Monitor/MonitorMetricType.ts +40 -0
- package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +587 -37
- package/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.ts +115 -0
- package/build/dist/Models/DatabaseModels/GlobalOidc.js +21 -0
- package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSso.js +21 -0
- package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MarketingConversion.js +20 -10
- package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +47 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +4 -4
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +25 -4
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +6 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js +29 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/SlackAuthorization.js +28 -4
- package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +197 -22
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/WhatsAppAuthorization.js +32 -2
- package/build/dist/Server/Middleware/WhatsAppAuthorization.js.map +1 -1
- package/build/dist/Server/Services/GlobalOidcProjectService.js +105 -0
- package/build/dist/Server/Services/GlobalOidcProjectService.js.map +1 -1
- package/build/dist/Server/Services/GlobalOidcService.js +127 -0
- package/build/dist/Server/Services/GlobalOidcService.js.map +1 -1
- package/build/dist/Server/Services/GlobalSsoProjectService.js +105 -0
- package/build/dist/Server/Services/GlobalSsoProjectService.js.map +1 -1
- package/build/dist/Server/Services/GlobalSsoService.js +127 -0
- package/build/dist/Server/Services/GlobalSsoService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +4 -4
- package/build/dist/Server/Utils/CronTab.js +66 -0
- package/build/dist/Server/Utils/CronTab.js.map +1 -1
- package/build/dist/Server/Utils/GlobalSsoAuthorization.js +112 -0
- package/build/dist/Server/Utils/GlobalSsoAuthorization.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +17 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +37 -35
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +20 -22
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +370 -42
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +64 -52
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +25 -29
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +8 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +123 -23
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js +39 -0
- package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js.map +1 -0
- package/build/dist/Server/Utils/StatusPageCustomizationAccess.js +85 -0
- package/build/dist/Server/Utils/StatusPageCustomizationAccess.js.map +1 -0
- package/build/dist/Types/Marketing/MarketingConversion.js +10 -0
- package/build/dist/Types/Marketing/MarketingConversion.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +1 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js +46 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js.map +1 -0
- package/build/dist/UI/Components/FormModal/BasicFormModal.js +2 -1
- package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +10 -1
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicModelForm.js +2 -1
- package/build/dist/UI/Components/Forms/BasicModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +4 -1
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js +33 -0
- package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js.map +1 -0
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js +2 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/Utils/Analytics.js +50 -2
- package/build/dist/Utils/Analytics.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +37 -0
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +318 -37
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js +87 -0
- package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
import ExternalStatusPageMonitorCriteria from "../../../../../Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria";
|
|
2
|
+
import {
|
|
3
|
+
CheckOn,
|
|
4
|
+
CriteriaFilter,
|
|
5
|
+
FilterType,
|
|
6
|
+
} from "../../../../../Types/Monitor/CriteriaFilter";
|
|
7
|
+
import ExternalStatusPageMonitorResponse, {
|
|
8
|
+
ExternalStatusPageComponentStatus,
|
|
9
|
+
} from "../../../../../Types/Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse";
|
|
10
|
+
import ProbeMonitorResponse from "../../../../../Types/Probe/ProbeMonitorResponse";
|
|
11
|
+
import ObjectID from "../../../../../Types/ObjectID";
|
|
12
|
+
import { describe, expect, test } from "@jest/globals";
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* Build an ExternalStatusPageMonitorResponse with sensible defaults. Every
|
|
16
|
+
* field the type requires is populated so a test only needs to override the
|
|
17
|
+
* single value it exercises. Defaults are chosen so they do NOT accidentally
|
|
18
|
+
* satisfy the criteria under test (e.g. activeIncidentCount defaults to 0).
|
|
19
|
+
*/
|
|
20
|
+
function buildExternalStatusPageResponse(
|
|
21
|
+
overrides: Partial<ExternalStatusPageMonitorResponse>,
|
|
22
|
+
): ExternalStatusPageMonitorResponse {
|
|
23
|
+
return {
|
|
24
|
+
isOnline: true,
|
|
25
|
+
overallStatus: "Operational",
|
|
26
|
+
componentStatuses: [],
|
|
27
|
+
activeIncidentCount: 0,
|
|
28
|
+
responseTimeInMs: 100,
|
|
29
|
+
failureCause: "",
|
|
30
|
+
...overrides,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* Build the ProbeMonitorResponse wrapper the evaluator receives. The
|
|
36
|
+
* top-level `isOnline` / `responseTimeInMs` are deliberately kept distinct
|
|
37
|
+
* from the nested externalStatusPageResponse values so precedence between the
|
|
38
|
+
* two can be asserted.
|
|
39
|
+
*/
|
|
40
|
+
function buildDataToProcess(input: {
|
|
41
|
+
externalStatusPageResponse?: ExternalStatusPageMonitorResponse | undefined;
|
|
42
|
+
isOnline?: boolean | undefined;
|
|
43
|
+
responseTimeInMs?: number | undefined;
|
|
44
|
+
}): ProbeMonitorResponse {
|
|
45
|
+
return {
|
|
46
|
+
projectId: ObjectID.generate(),
|
|
47
|
+
monitorId: ObjectID.generate(),
|
|
48
|
+
monitorStepId: ObjectID.generate(),
|
|
49
|
+
probeId: ObjectID.generate(),
|
|
50
|
+
failureCause: "",
|
|
51
|
+
isOnline: input.isOnline,
|
|
52
|
+
responseTimeInMs: input.responseTimeInMs,
|
|
53
|
+
externalStatusPageResponse: input.externalStatusPageResponse,
|
|
54
|
+
monitoredAt: new Date(),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function evaluate(
|
|
59
|
+
dataToProcess: ProbeMonitorResponse,
|
|
60
|
+
criteriaFilter: CriteriaFilter,
|
|
61
|
+
): Promise<string | null> {
|
|
62
|
+
return ExternalStatusPageMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
|
|
63
|
+
dataToProcess,
|
|
64
|
+
criteriaFilter,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function component(
|
|
69
|
+
name: string,
|
|
70
|
+
status: string,
|
|
71
|
+
): ExternalStatusPageComponentStatus {
|
|
72
|
+
return {
|
|
73
|
+
name,
|
|
74
|
+
status,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
describe("ExternalStatusPageMonitorCriteria.isMonitorInstanceCriteriaFilterMet", () => {
|
|
79
|
+
test("an unrelated CheckOn is not claimed by this evaluator", async () => {
|
|
80
|
+
const result: string | null = await evaluate(
|
|
81
|
+
buildDataToProcess({
|
|
82
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({}),
|
|
83
|
+
responseTimeInMs: 5,
|
|
84
|
+
}),
|
|
85
|
+
{
|
|
86
|
+
checkOn: CheckOn.ResponseTime,
|
|
87
|
+
filterType: FilterType.LessThan,
|
|
88
|
+
value: 1000,
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
expect(result).toBeNull();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("ExternalStatusPageIsOnline", () => {
|
|
96
|
+
test("online + True → met", async () => {
|
|
97
|
+
const result: string | null = await evaluate(
|
|
98
|
+
buildDataToProcess({
|
|
99
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({}),
|
|
100
|
+
isOnline: true,
|
|
101
|
+
}),
|
|
102
|
+
{
|
|
103
|
+
checkOn: CheckOn.ExternalStatusPageIsOnline,
|
|
104
|
+
filterType: FilterType.True,
|
|
105
|
+
value: undefined,
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(result).toBe("External Status Page Is Online is true.");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("offline + False → met", async () => {
|
|
113
|
+
const result: string | null = await evaluate(
|
|
114
|
+
buildDataToProcess({
|
|
115
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({}),
|
|
116
|
+
isOnline: false,
|
|
117
|
+
}),
|
|
118
|
+
{
|
|
119
|
+
checkOn: CheckOn.ExternalStatusPageIsOnline,
|
|
120
|
+
filterType: FilterType.False,
|
|
121
|
+
value: undefined,
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
expect(result).toBe("External Status Page Is Online is false.");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("offline + True → not met", async () => {
|
|
129
|
+
const result: string | null = await evaluate(
|
|
130
|
+
buildDataToProcess({
|
|
131
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({}),
|
|
132
|
+
isOnline: false,
|
|
133
|
+
}),
|
|
134
|
+
{
|
|
135
|
+
checkOn: CheckOn.ExternalStatusPageIsOnline,
|
|
136
|
+
filterType: FilterType.True,
|
|
137
|
+
value: undefined,
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
expect(result).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("online + False → not met", async () => {
|
|
145
|
+
const result: string | null = await evaluate(
|
|
146
|
+
buildDataToProcess({
|
|
147
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({}),
|
|
148
|
+
isOnline: true,
|
|
149
|
+
}),
|
|
150
|
+
{
|
|
151
|
+
checkOn: CheckOn.ExternalStatusPageIsOnline,
|
|
152
|
+
filterType: FilterType.False,
|
|
153
|
+
value: undefined,
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
expect(result).toBeNull();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
/*
|
|
161
|
+
* The online value is read from the top-level ProbeMonitorResponse, not
|
|
162
|
+
* from the nested externalStatusPageResponse. With the top-level flag
|
|
163
|
+
* missing the boolean comparison receives undefined and is undecidable.
|
|
164
|
+
*/
|
|
165
|
+
test("undefined top-level isOnline → undecidable", async () => {
|
|
166
|
+
const result: string | null = await evaluate(
|
|
167
|
+
buildDataToProcess({
|
|
168
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
169
|
+
isOnline: true,
|
|
170
|
+
}),
|
|
171
|
+
isOnline: undefined,
|
|
172
|
+
}),
|
|
173
|
+
{
|
|
174
|
+
checkOn: CheckOn.ExternalStatusPageIsOnline,
|
|
175
|
+
filterType: FilterType.True,
|
|
176
|
+
value: undefined,
|
|
177
|
+
},
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
expect(result).toBeNull();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/*
|
|
184
|
+
* compareCriteriaBoolean only understands True / False. Any other filter
|
|
185
|
+
* type (here EqualTo) is undecidable for a boolean check.
|
|
186
|
+
*/
|
|
187
|
+
test("non-boolean filter type → undecidable", async () => {
|
|
188
|
+
const result: string | null = await evaluate(
|
|
189
|
+
buildDataToProcess({
|
|
190
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({}),
|
|
191
|
+
isOnline: true,
|
|
192
|
+
}),
|
|
193
|
+
{
|
|
194
|
+
checkOn: CheckOn.ExternalStatusPageIsOnline,
|
|
195
|
+
filterType: FilterType.EqualTo,
|
|
196
|
+
value: undefined,
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
expect(result).toBeNull();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
/*
|
|
204
|
+
* The over-time block only runs when BOTH evaluateOverTime and
|
|
205
|
+
* evaluateOverTimeOptions are set. With options missing the guard is
|
|
206
|
+
* skipped entirely (no EvaluateOverTime / database call), and the current
|
|
207
|
+
* top-level isOnline value is used — keeping this deterministic.
|
|
208
|
+
*/
|
|
209
|
+
test("evaluateOverTime without options falls back to current value", async () => {
|
|
210
|
+
const result: string | null = await evaluate(
|
|
211
|
+
buildDataToProcess({
|
|
212
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({}),
|
|
213
|
+
isOnline: true,
|
|
214
|
+
}),
|
|
215
|
+
{
|
|
216
|
+
checkOn: CheckOn.ExternalStatusPageIsOnline,
|
|
217
|
+
filterType: FilterType.True,
|
|
218
|
+
value: undefined,
|
|
219
|
+
evaluateOverTime: true,
|
|
220
|
+
evaluateOverTimeOptions: undefined,
|
|
221
|
+
},
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
expect(result).toBe("External Status Page Is Online is true.");
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe("ExternalStatusPageResponseTime", () => {
|
|
229
|
+
test("response time above threshold + GreaterThan → met", async () => {
|
|
230
|
+
const result: string | null = await evaluate(
|
|
231
|
+
buildDataToProcess({
|
|
232
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
233
|
+
responseTimeInMs: 500,
|
|
234
|
+
}),
|
|
235
|
+
}),
|
|
236
|
+
{
|
|
237
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
238
|
+
filterType: FilterType.GreaterThan,
|
|
239
|
+
value: 100,
|
|
240
|
+
},
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect(result).toContain("External Status Page Response Time");
|
|
244
|
+
expect(result).toContain("500");
|
|
245
|
+
expect(result).toContain("greater than 100");
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("response time below threshold + GreaterThan → not met", async () => {
|
|
249
|
+
const result: string | null = await evaluate(
|
|
250
|
+
buildDataToProcess({
|
|
251
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
252
|
+
responseTimeInMs: 50,
|
|
253
|
+
}),
|
|
254
|
+
}),
|
|
255
|
+
{
|
|
256
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
257
|
+
filterType: FilterType.GreaterThan,
|
|
258
|
+
value: 100,
|
|
259
|
+
},
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
expect(result).toBeNull();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("response time equal to threshold + GreaterThan → not met (boundary)", async () => {
|
|
266
|
+
const result: string | null = await evaluate(
|
|
267
|
+
buildDataToProcess({
|
|
268
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
269
|
+
responseTimeInMs: 100,
|
|
270
|
+
}),
|
|
271
|
+
}),
|
|
272
|
+
{
|
|
273
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
274
|
+
filterType: FilterType.GreaterThan,
|
|
275
|
+
value: 100,
|
|
276
|
+
},
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
expect(result).toBeNull();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("response time equal to threshold + GreaterThanOrEqualTo → met (boundary)", async () => {
|
|
283
|
+
const result: string | null = await evaluate(
|
|
284
|
+
buildDataToProcess({
|
|
285
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
286
|
+
responseTimeInMs: 100,
|
|
287
|
+
}),
|
|
288
|
+
}),
|
|
289
|
+
{
|
|
290
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
291
|
+
filterType: FilterType.GreaterThanOrEqualTo,
|
|
292
|
+
value: 100,
|
|
293
|
+
},
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
expect(result).toContain("External Status Page Response Time");
|
|
297
|
+
expect(result).toContain("greater than or equal to 100");
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("response time below threshold + LessThan → met", async () => {
|
|
301
|
+
const result: string | null = await evaluate(
|
|
302
|
+
buildDataToProcess({
|
|
303
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
304
|
+
responseTimeInMs: 40,
|
|
305
|
+
}),
|
|
306
|
+
}),
|
|
307
|
+
{
|
|
308
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
309
|
+
filterType: FilterType.LessThan,
|
|
310
|
+
value: 100,
|
|
311
|
+
},
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
expect(result).toContain("less than 100");
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("numeric string threshold is accepted", async () => {
|
|
318
|
+
const result: string | null = await evaluate(
|
|
319
|
+
buildDataToProcess({
|
|
320
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
321
|
+
responseTimeInMs: 500,
|
|
322
|
+
}),
|
|
323
|
+
}),
|
|
324
|
+
{
|
|
325
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
326
|
+
filterType: FilterType.GreaterThan,
|
|
327
|
+
value: "100",
|
|
328
|
+
},
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
expect(result).toContain("greater than 100");
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test("undefined threshold → undecidable", async () => {
|
|
335
|
+
const result: string | null = await evaluate(
|
|
336
|
+
buildDataToProcess({
|
|
337
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
338
|
+
responseTimeInMs: 500,
|
|
339
|
+
}),
|
|
340
|
+
}),
|
|
341
|
+
{
|
|
342
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
343
|
+
filterType: FilterType.GreaterThan,
|
|
344
|
+
value: undefined,
|
|
345
|
+
},
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
expect(result).toBeNull();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("non-numeric threshold → undecidable", async () => {
|
|
352
|
+
const result: string | null = await evaluate(
|
|
353
|
+
buildDataToProcess({
|
|
354
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
355
|
+
responseTimeInMs: 500,
|
|
356
|
+
}),
|
|
357
|
+
}),
|
|
358
|
+
{
|
|
359
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
360
|
+
filterType: FilterType.GreaterThan,
|
|
361
|
+
value: "not-a-number",
|
|
362
|
+
},
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
expect(result).toBeNull();
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
/*
|
|
369
|
+
* The nested externalStatusPageResponse.responseTimeInMs takes precedence
|
|
370
|
+
* over the top-level responseTimeInMs. Here the nested value (500) fails a
|
|
371
|
+
* LessThan-100 check while the top-level value (10) would have passed — a
|
|
372
|
+
* null result proves the nested value was the one compared.
|
|
373
|
+
*/
|
|
374
|
+
test("nested response time takes precedence over top-level", async () => {
|
|
375
|
+
const result: string | null = await evaluate(
|
|
376
|
+
buildDataToProcess({
|
|
377
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
378
|
+
responseTimeInMs: 500,
|
|
379
|
+
}),
|
|
380
|
+
responseTimeInMs: 10,
|
|
381
|
+
}),
|
|
382
|
+
{
|
|
383
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
384
|
+
filterType: FilterType.LessThan,
|
|
385
|
+
value: 100,
|
|
386
|
+
},
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
expect(result).toBeNull();
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
/*
|
|
393
|
+
* A nested response time of 0 is falsy, so the `||` chain falls through to
|
|
394
|
+
* the top-level responseTimeInMs. The message must therefore reflect the
|
|
395
|
+
* top-level value (250), not 0.
|
|
396
|
+
*/
|
|
397
|
+
test("nested response time of zero falls through to top-level value", async () => {
|
|
398
|
+
const result: string | null = await evaluate(
|
|
399
|
+
buildDataToProcess({
|
|
400
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
401
|
+
responseTimeInMs: 0,
|
|
402
|
+
}),
|
|
403
|
+
responseTimeInMs: 250,
|
|
404
|
+
}),
|
|
405
|
+
{
|
|
406
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
407
|
+
filterType: FilterType.GreaterThan,
|
|
408
|
+
value: 100,
|
|
409
|
+
},
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
expect(result).toContain("250");
|
|
413
|
+
expect(result).toContain("greater than 100");
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
/*
|
|
417
|
+
* When no externalStatusPageResponse is present the evaluator falls back
|
|
418
|
+
* to the top-level responseTimeInMs.
|
|
419
|
+
*/
|
|
420
|
+
test("falls back to top-level response time when external response is absent", async () => {
|
|
421
|
+
const result: string | null = await evaluate(
|
|
422
|
+
buildDataToProcess({
|
|
423
|
+
externalStatusPageResponse: undefined,
|
|
424
|
+
responseTimeInMs: 250,
|
|
425
|
+
}),
|
|
426
|
+
{
|
|
427
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
428
|
+
filterType: FilterType.GreaterThan,
|
|
429
|
+
value: 100,
|
|
430
|
+
},
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
expect(result).toContain("250");
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test("no response time available → undecidable", async () => {
|
|
437
|
+
const result: string | null = await evaluate(
|
|
438
|
+
buildDataToProcess({
|
|
439
|
+
externalStatusPageResponse: undefined,
|
|
440
|
+
responseTimeInMs: undefined,
|
|
441
|
+
}),
|
|
442
|
+
{
|
|
443
|
+
checkOn: CheckOn.ExternalStatusPageResponseTime,
|
|
444
|
+
filterType: FilterType.GreaterThan,
|
|
445
|
+
value: 100,
|
|
446
|
+
},
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
expect(result).toBeNull();
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
describe("ExternalStatusPageOverallStatus", () => {
|
|
454
|
+
test("no external response → undecidable", async () => {
|
|
455
|
+
const result: string | null = await evaluate(
|
|
456
|
+
buildDataToProcess({ externalStatusPageResponse: undefined }),
|
|
457
|
+
{
|
|
458
|
+
checkOn: CheckOn.ExternalStatusPageOverallStatus,
|
|
459
|
+
filterType: FilterType.EqualTo,
|
|
460
|
+
value: "Operational",
|
|
461
|
+
},
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
expect(result).toBeNull();
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
/*
|
|
468
|
+
* An empty overallStatus string is falsy and short-circuits before any
|
|
469
|
+
* comparison, so the check is undecidable rather than matching an empty
|
|
470
|
+
* threshold.
|
|
471
|
+
*/
|
|
472
|
+
test("empty overall status → undecidable", async () => {
|
|
473
|
+
const result: string | null = await evaluate(
|
|
474
|
+
buildDataToProcess({
|
|
475
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
476
|
+
overallStatus: "",
|
|
477
|
+
}),
|
|
478
|
+
}),
|
|
479
|
+
{
|
|
480
|
+
checkOn: CheckOn.ExternalStatusPageOverallStatus,
|
|
481
|
+
filterType: FilterType.EqualTo,
|
|
482
|
+
value: "",
|
|
483
|
+
},
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
expect(result).toBeNull();
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("EqualTo exact match → met", async () => {
|
|
490
|
+
const result: string | null = await evaluate(
|
|
491
|
+
buildDataToProcess({
|
|
492
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
493
|
+
overallStatus: "Operational",
|
|
494
|
+
}),
|
|
495
|
+
}),
|
|
496
|
+
{
|
|
497
|
+
checkOn: CheckOn.ExternalStatusPageOverallStatus,
|
|
498
|
+
filterType: FilterType.EqualTo,
|
|
499
|
+
value: "Operational",
|
|
500
|
+
},
|
|
501
|
+
);
|
|
502
|
+
|
|
503
|
+
expect(result).toBe(
|
|
504
|
+
"External Status Page Overall Status is equal to Operational.",
|
|
505
|
+
);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
test("EqualTo mismatch → not met", async () => {
|
|
509
|
+
const result: string | null = await evaluate(
|
|
510
|
+
buildDataToProcess({
|
|
511
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
512
|
+
overallStatus: "Operational",
|
|
513
|
+
}),
|
|
514
|
+
}),
|
|
515
|
+
{
|
|
516
|
+
checkOn: CheckOn.ExternalStatusPageOverallStatus,
|
|
517
|
+
filterType: FilterType.EqualTo,
|
|
518
|
+
value: "Major Outage",
|
|
519
|
+
},
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
expect(result).toBeNull();
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
test("Contains substring → met", async () => {
|
|
526
|
+
const result: string | null = await evaluate(
|
|
527
|
+
buildDataToProcess({
|
|
528
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
529
|
+
overallStatus: "Major Outage",
|
|
530
|
+
}),
|
|
531
|
+
}),
|
|
532
|
+
{
|
|
533
|
+
checkOn: CheckOn.ExternalStatusPageOverallStatus,
|
|
534
|
+
filterType: FilterType.Contains,
|
|
535
|
+
value: "Outage",
|
|
536
|
+
},
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
expect(result).toContain("External Status Page Overall Status");
|
|
540
|
+
expect(result).toContain("contains");
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
test("NotContains when substring is absent → met", async () => {
|
|
544
|
+
const result: string | null = await evaluate(
|
|
545
|
+
buildDataToProcess({
|
|
546
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
547
|
+
overallStatus: "Operational",
|
|
548
|
+
}),
|
|
549
|
+
}),
|
|
550
|
+
{
|
|
551
|
+
checkOn: CheckOn.ExternalStatusPageOverallStatus,
|
|
552
|
+
filterType: FilterType.NotContains,
|
|
553
|
+
value: "Outage",
|
|
554
|
+
},
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
expect(result).toContain("does not contain");
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
test("StartsWith prefix → met", async () => {
|
|
561
|
+
const result: string | null = await evaluate(
|
|
562
|
+
buildDataToProcess({
|
|
563
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
564
|
+
overallStatus: "Degraded Performance",
|
|
565
|
+
}),
|
|
566
|
+
}),
|
|
567
|
+
{
|
|
568
|
+
checkOn: CheckOn.ExternalStatusPageOverallStatus,
|
|
569
|
+
filterType: FilterType.StartsWith,
|
|
570
|
+
value: "Degraded",
|
|
571
|
+
},
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
expect(result).toContain("starts with");
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
test("EndsWith suffix → met", async () => {
|
|
578
|
+
const result: string | null = await evaluate(
|
|
579
|
+
buildDataToProcess({
|
|
580
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
581
|
+
overallStatus: "Partial Outage",
|
|
582
|
+
}),
|
|
583
|
+
}),
|
|
584
|
+
{
|
|
585
|
+
checkOn: CheckOn.ExternalStatusPageOverallStatus,
|
|
586
|
+
filterType: FilterType.EndsWith,
|
|
587
|
+
value: "Outage",
|
|
588
|
+
},
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
expect(result).toContain("ends with");
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
/*
|
|
595
|
+
* A numeric filter type is not handled by the string comparator, so even a
|
|
596
|
+
* present overall status is undecidable under GreaterThan.
|
|
597
|
+
*/
|
|
598
|
+
test("numeric filter type on a string status → undecidable", async () => {
|
|
599
|
+
const result: string | null = await evaluate(
|
|
600
|
+
buildDataToProcess({
|
|
601
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
602
|
+
overallStatus: "Operational",
|
|
603
|
+
}),
|
|
604
|
+
}),
|
|
605
|
+
{
|
|
606
|
+
checkOn: CheckOn.ExternalStatusPageOverallStatus,
|
|
607
|
+
filterType: FilterType.GreaterThan,
|
|
608
|
+
value: "5",
|
|
609
|
+
},
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
expect(result).toBeNull();
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
/*
|
|
616
|
+
* An undefined value is coerced to the literal string "undefined" via
|
|
617
|
+
* String(threshold); EqualTo against a real status therefore does not
|
|
618
|
+
* match and the comparison stays deterministic without throwing.
|
|
619
|
+
*/
|
|
620
|
+
test("undefined value coerced to string does not match a real status", async () => {
|
|
621
|
+
const result: string | null = await evaluate(
|
|
622
|
+
buildDataToProcess({
|
|
623
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
624
|
+
overallStatus: "Operational",
|
|
625
|
+
}),
|
|
626
|
+
}),
|
|
627
|
+
{
|
|
628
|
+
checkOn: CheckOn.ExternalStatusPageOverallStatus,
|
|
629
|
+
filterType: FilterType.EqualTo,
|
|
630
|
+
value: undefined,
|
|
631
|
+
},
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
expect(result).toBeNull();
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
describe("ExternalStatusPageComponentStatus", () => {
|
|
639
|
+
test("no external response → undecidable", async () => {
|
|
640
|
+
const result: string | null = await evaluate(
|
|
641
|
+
buildDataToProcess({ externalStatusPageResponse: undefined }),
|
|
642
|
+
{
|
|
643
|
+
checkOn: CheckOn.ExternalStatusPageComponentStatus,
|
|
644
|
+
filterType: FilterType.EqualTo,
|
|
645
|
+
value: "Degraded",
|
|
646
|
+
},
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
expect(result).toBeNull();
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
test("empty component list → undecidable", async () => {
|
|
653
|
+
const result: string | null = await evaluate(
|
|
654
|
+
buildDataToProcess({
|
|
655
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
656
|
+
componentStatuses: [],
|
|
657
|
+
}),
|
|
658
|
+
}),
|
|
659
|
+
{
|
|
660
|
+
checkOn: CheckOn.ExternalStatusPageComponentStatus,
|
|
661
|
+
filterType: FilterType.EqualTo,
|
|
662
|
+
value: "Degraded",
|
|
663
|
+
},
|
|
664
|
+
);
|
|
665
|
+
|
|
666
|
+
expect(result).toBeNull();
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
test("a single matching component → met, message names the component", async () => {
|
|
670
|
+
const result: string | null = await evaluate(
|
|
671
|
+
buildDataToProcess({
|
|
672
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
673
|
+
componentStatuses: [component("API Server", "Degraded")],
|
|
674
|
+
}),
|
|
675
|
+
}),
|
|
676
|
+
{
|
|
677
|
+
checkOn: CheckOn.ExternalStatusPageComponentStatus,
|
|
678
|
+
filterType: FilterType.EqualTo,
|
|
679
|
+
value: "Degraded",
|
|
680
|
+
},
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
expect(result).toContain('Component "API Server"');
|
|
684
|
+
expect(result).toContain("is equal to Degraded");
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
test("no component matches → undecidable", async () => {
|
|
688
|
+
const result: string | null = await evaluate(
|
|
689
|
+
buildDataToProcess({
|
|
690
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
691
|
+
componentStatuses: [
|
|
692
|
+
component("API Server", "Operational"),
|
|
693
|
+
component("Database", "Operational"),
|
|
694
|
+
],
|
|
695
|
+
}),
|
|
696
|
+
}),
|
|
697
|
+
{
|
|
698
|
+
checkOn: CheckOn.ExternalStatusPageComponentStatus,
|
|
699
|
+
filterType: FilterType.EqualTo,
|
|
700
|
+
value: "Degraded",
|
|
701
|
+
},
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
expect(result).toBeNull();
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
/*
|
|
708
|
+
* The match can appear anywhere in the list; a match on a later component
|
|
709
|
+
* is still reported, tagged with that component's name.
|
|
710
|
+
*/
|
|
711
|
+
test("a match on a later component is found", async () => {
|
|
712
|
+
const result: string | null = await evaluate(
|
|
713
|
+
buildDataToProcess({
|
|
714
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
715
|
+
componentStatuses: [
|
|
716
|
+
component("API Server", "Operational"),
|
|
717
|
+
component("Database", "Major Outage"),
|
|
718
|
+
],
|
|
719
|
+
}),
|
|
720
|
+
}),
|
|
721
|
+
{
|
|
722
|
+
checkOn: CheckOn.ExternalStatusPageComponentStatus,
|
|
723
|
+
filterType: FilterType.EqualTo,
|
|
724
|
+
value: "Major Outage",
|
|
725
|
+
},
|
|
726
|
+
);
|
|
727
|
+
|
|
728
|
+
expect(result).toContain('Component "Database"');
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
/*
|
|
732
|
+
* When more than one component satisfies the filter, the FIRST matching
|
|
733
|
+
* component in iteration order is the one reported.
|
|
734
|
+
*/
|
|
735
|
+
test("first matching component wins when several match", async () => {
|
|
736
|
+
const result: string | null = await evaluate(
|
|
737
|
+
buildDataToProcess({
|
|
738
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
739
|
+
componentStatuses: [
|
|
740
|
+
component("Ingest", "Degraded"),
|
|
741
|
+
component("Query", "Degraded"),
|
|
742
|
+
],
|
|
743
|
+
}),
|
|
744
|
+
}),
|
|
745
|
+
{
|
|
746
|
+
checkOn: CheckOn.ExternalStatusPageComponentStatus,
|
|
747
|
+
filterType: FilterType.EqualTo,
|
|
748
|
+
value: "Degraded",
|
|
749
|
+
},
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
expect(result).toContain('Component "Ingest"');
|
|
753
|
+
expect(result).not.toContain('Component "Query"');
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
test("Contains match on a component status → met", async () => {
|
|
757
|
+
const result: string | null = await evaluate(
|
|
758
|
+
buildDataToProcess({
|
|
759
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
760
|
+
componentStatuses: [component("CDN", "Partial Outage")],
|
|
761
|
+
}),
|
|
762
|
+
}),
|
|
763
|
+
{
|
|
764
|
+
checkOn: CheckOn.ExternalStatusPageComponentStatus,
|
|
765
|
+
filterType: FilterType.Contains,
|
|
766
|
+
value: "Outage",
|
|
767
|
+
},
|
|
768
|
+
);
|
|
769
|
+
|
|
770
|
+
expect(result).toContain('Component "CDN"');
|
|
771
|
+
expect(result).toContain("contains");
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
describe("ExternalStatusPageActiveIncidents", () => {
|
|
776
|
+
test("undefined threshold → undecidable", async () => {
|
|
777
|
+
const result: string | null = await evaluate(
|
|
778
|
+
buildDataToProcess({
|
|
779
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
780
|
+
activeIncidentCount: 3,
|
|
781
|
+
}),
|
|
782
|
+
}),
|
|
783
|
+
{
|
|
784
|
+
checkOn: CheckOn.ExternalStatusPageActiveIncidents,
|
|
785
|
+
filterType: FilterType.GreaterThan,
|
|
786
|
+
value: undefined,
|
|
787
|
+
},
|
|
788
|
+
);
|
|
789
|
+
|
|
790
|
+
expect(result).toBeNull();
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
test("non-numeric threshold → undecidable", async () => {
|
|
794
|
+
const result: string | null = await evaluate(
|
|
795
|
+
buildDataToProcess({
|
|
796
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
797
|
+
activeIncidentCount: 3,
|
|
798
|
+
}),
|
|
799
|
+
}),
|
|
800
|
+
{
|
|
801
|
+
checkOn: CheckOn.ExternalStatusPageActiveIncidents,
|
|
802
|
+
filterType: FilterType.GreaterThan,
|
|
803
|
+
value: "lots",
|
|
804
|
+
},
|
|
805
|
+
);
|
|
806
|
+
|
|
807
|
+
expect(result).toBeNull();
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
test("incident count above threshold + GreaterThan → met", async () => {
|
|
811
|
+
const result: string | null = await evaluate(
|
|
812
|
+
buildDataToProcess({
|
|
813
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
814
|
+
activeIncidentCount: 3,
|
|
815
|
+
}),
|
|
816
|
+
}),
|
|
817
|
+
{
|
|
818
|
+
checkOn: CheckOn.ExternalStatusPageActiveIncidents,
|
|
819
|
+
filterType: FilterType.GreaterThan,
|
|
820
|
+
value: 2,
|
|
821
|
+
},
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
expect(result).toContain("External Status Page Active Incidents");
|
|
825
|
+
expect(result).toContain("3");
|
|
826
|
+
expect(result).toContain("greater than 2");
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
test("incident count equal to threshold + GreaterThan → not met (boundary)", async () => {
|
|
830
|
+
const result: string | null = await evaluate(
|
|
831
|
+
buildDataToProcess({
|
|
832
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
833
|
+
activeIncidentCount: 2,
|
|
834
|
+
}),
|
|
835
|
+
}),
|
|
836
|
+
{
|
|
837
|
+
checkOn: CheckOn.ExternalStatusPageActiveIncidents,
|
|
838
|
+
filterType: FilterType.GreaterThan,
|
|
839
|
+
value: 2,
|
|
840
|
+
},
|
|
841
|
+
);
|
|
842
|
+
|
|
843
|
+
expect(result).toBeNull();
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
test("incident count equal to threshold + GreaterThanOrEqualTo → met (boundary)", async () => {
|
|
847
|
+
const result: string | null = await evaluate(
|
|
848
|
+
buildDataToProcess({
|
|
849
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
850
|
+
activeIncidentCount: 2,
|
|
851
|
+
}),
|
|
852
|
+
}),
|
|
853
|
+
{
|
|
854
|
+
checkOn: CheckOn.ExternalStatusPageActiveIncidents,
|
|
855
|
+
filterType: FilterType.GreaterThanOrEqualTo,
|
|
856
|
+
value: 2,
|
|
857
|
+
},
|
|
858
|
+
);
|
|
859
|
+
|
|
860
|
+
expect(result).toContain("greater than or equal to 2");
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
/*
|
|
864
|
+
* A missing externalStatusPageResponse yields an incident count of 0 (the
|
|
865
|
+
* `|| 0` default), which still participates in the numeric comparison.
|
|
866
|
+
* Zero is less than or equal to five, so the criterion fires.
|
|
867
|
+
*/
|
|
868
|
+
test("absent external response defaults incident count to zero", async () => {
|
|
869
|
+
const result: string | null = await evaluate(
|
|
870
|
+
buildDataToProcess({ externalStatusPageResponse: undefined }),
|
|
871
|
+
{
|
|
872
|
+
checkOn: CheckOn.ExternalStatusPageActiveIncidents,
|
|
873
|
+
filterType: FilterType.LessThanOrEqualTo,
|
|
874
|
+
value: 5,
|
|
875
|
+
},
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
expect(result).toContain("0");
|
|
879
|
+
expect(result).toContain("less than or equal to 5");
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
test("zero incidents + GreaterThan zero → not met", async () => {
|
|
883
|
+
const result: string | null = await evaluate(
|
|
884
|
+
buildDataToProcess({
|
|
885
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
886
|
+
activeIncidentCount: 0,
|
|
887
|
+
}),
|
|
888
|
+
}),
|
|
889
|
+
{
|
|
890
|
+
checkOn: CheckOn.ExternalStatusPageActiveIncidents,
|
|
891
|
+
filterType: FilterType.GreaterThan,
|
|
892
|
+
value: 0,
|
|
893
|
+
},
|
|
894
|
+
);
|
|
895
|
+
|
|
896
|
+
expect(result).toBeNull();
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
test("numeric string threshold is accepted", async () => {
|
|
900
|
+
const result: string | null = await evaluate(
|
|
901
|
+
buildDataToProcess({
|
|
902
|
+
externalStatusPageResponse: buildExternalStatusPageResponse({
|
|
903
|
+
activeIncidentCount: 4,
|
|
904
|
+
}),
|
|
905
|
+
}),
|
|
906
|
+
{
|
|
907
|
+
checkOn: CheckOn.ExternalStatusPageActiveIncidents,
|
|
908
|
+
filterType: FilterType.GreaterThan,
|
|
909
|
+
value: "2",
|
|
910
|
+
},
|
|
911
|
+
);
|
|
912
|
+
|
|
913
|
+
expect(result).toContain("greater than 2");
|
|
914
|
+
});
|
|
915
|
+
});
|
|
916
|
+
});
|