@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,459 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Probe agreement re-runs the criteria evaluator over every OTHER probe's
|
|
3
|
+
* stored result before a status change is allowed. Two things about that
|
|
4
|
+
* pass used to make it disagree with the evaluation summary shown to the
|
|
5
|
+
* user on the very same incident:
|
|
6
|
+
*
|
|
7
|
+
* 1. The stored result is `JSON.parse(JSON.stringify(response))`, so its
|
|
8
|
+
* ObjectIDs come back as plain `{ _type, value }` objects. Those are
|
|
9
|
+
* not `instanceof ObjectID`, so an "evaluate over time" lookup bound
|
|
10
|
+
* them as-is and matched zero rows - indistinguishable from "this
|
|
11
|
+
* monitor has no history", which sent the evaluator down its
|
|
12
|
+
* instantaneous-value fallback.
|
|
13
|
+
* 2. The probe whose result triggered this whole pass was re-evaluated
|
|
14
|
+
* from that stored copy rather than reusing the verdict the caller had
|
|
15
|
+
* just computed, so its "over time" window was a different window.
|
|
16
|
+
*
|
|
17
|
+
* Together those produced incidents whose stored root cause quoted a single
|
|
18
|
+
* sample while the summary beside it reported the full window and said the
|
|
19
|
+
* criteria was not met. See
|
|
20
|
+
* https://github.com/OneUptime/oneuptime/issues/2321.
|
|
21
|
+
*
|
|
22
|
+
* As in the sibling reuse suite, everything heavy is mocked BEFORE
|
|
23
|
+
* importing MonitorResource.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* MonitorResource's import chain reaches the native isolated-vm addon
|
|
28
|
+
* through the sandbox runner. Nothing here touches the sandbox and the
|
|
29
|
+
* prebuilt binary cannot always dlopen in the test environment, so stub it
|
|
30
|
+
* out before anything imports it.
|
|
31
|
+
*/
|
|
32
|
+
jest.mock("isolated-vm", () => {
|
|
33
|
+
return {};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
jest.mock("../../../Server/Utils/Monitor/MonitorCriteriaEvaluator", () => {
|
|
37
|
+
return {
|
|
38
|
+
__esModule: true,
|
|
39
|
+
default: {
|
|
40
|
+
processMonitorStep: jest.fn(),
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
jest.mock("../../../Server/Services/MonitorProbeService", () => {
|
|
46
|
+
return {
|
|
47
|
+
__esModule: true,
|
|
48
|
+
default: {
|
|
49
|
+
findBy: jest.fn(),
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
jest.mock("../../../Server/Utils/Logger", () => {
|
|
55
|
+
return {
|
|
56
|
+
__esModule: true,
|
|
57
|
+
default: {
|
|
58
|
+
debug: jest.fn(),
|
|
59
|
+
info: jest.fn(),
|
|
60
|
+
warn: jest.fn(),
|
|
61
|
+
error: jest.fn(),
|
|
62
|
+
trace: jest.fn(),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
import MonitorResourceUtil from "../../../Server/Utils/Monitor/MonitorResource";
|
|
68
|
+
import MonitorCriteriaEvaluator from "../../../Server/Utils/Monitor/MonitorCriteriaEvaluator";
|
|
69
|
+
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
70
|
+
import MonitorProbe, {
|
|
71
|
+
MonitorStepProbeResponse,
|
|
72
|
+
} from "../../../Models/DatabaseModels/MonitorProbe";
|
|
73
|
+
import Probe, {
|
|
74
|
+
ProbeConnectionStatus,
|
|
75
|
+
} from "../../../Models/DatabaseModels/Probe";
|
|
76
|
+
import MonitorStep from "../../../Types/Monitor/MonitorStep";
|
|
77
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
78
|
+
import ProbeApiIngestResponse from "../../../Types/Probe/ProbeApiIngestResponse";
|
|
79
|
+
import ProbeMonitorResponse from "../../../Types/Probe/ProbeMonitorResponse";
|
|
80
|
+
import DataToProcess from "../../../Server/Utils/Monitor/DataToProcess";
|
|
81
|
+
import { describe, expect, test, beforeEach } from "@jest/globals";
|
|
82
|
+
|
|
83
|
+
interface ProbeAgreementResult {
|
|
84
|
+
hasAgreement: boolean;
|
|
85
|
+
agreementCount: number;
|
|
86
|
+
requiredCount: number;
|
|
87
|
+
totalActiveProbes: number;
|
|
88
|
+
agreedCriteriaId: string | null;
|
|
89
|
+
agreedRootCause: string | null;
|
|
90
|
+
agreedProbeNames: Array<string>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const processMonitorStepMock: jest.Mock =
|
|
94
|
+
MonitorCriteriaEvaluator.processMonitorStep as unknown as jest.Mock;
|
|
95
|
+
|
|
96
|
+
type CheckProbeAgreementFunction = (input: {
|
|
97
|
+
monitor: Monitor;
|
|
98
|
+
monitorStep: MonitorStep;
|
|
99
|
+
currentCriteriaMetId: string | null;
|
|
100
|
+
currentRootCause: string | null;
|
|
101
|
+
currentProbeId?: ObjectID | undefined;
|
|
102
|
+
monitorProbes?: Array<MonitorProbe> | undefined;
|
|
103
|
+
}) => Promise<ProbeAgreementResult>;
|
|
104
|
+
|
|
105
|
+
const checkProbeAgreement: CheckProbeAgreementFunction = (
|
|
106
|
+
MonitorResourceUtil as any
|
|
107
|
+
)["checkProbeAgreement"].bind(MonitorResourceUtil);
|
|
108
|
+
|
|
109
|
+
const PROJECT_ID: string = "11111111-1111-4111-8111-111111111111";
|
|
110
|
+
const MONITOR_ID: string = "22222222-2222-4222-8222-222222222222";
|
|
111
|
+
const STEP_ID: string = "33333333-3333-4333-8333-333333333333";
|
|
112
|
+
const PROBE_ID: string = "44444444-4444-4444-8444-444444444444";
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A probe response exactly as it comes back out of the lastMonitoringLog
|
|
116
|
+
* jsonb column - every ObjectID flattened to `{ _type, value }` and every
|
|
117
|
+
* Date to an ISO string.
|
|
118
|
+
*/
|
|
119
|
+
function storedProbeResponse(
|
|
120
|
+
overrides: Record<string, unknown> = {},
|
|
121
|
+
): ProbeMonitorResponse {
|
|
122
|
+
return {
|
|
123
|
+
projectId: { _type: "ObjectID", value: PROJECT_ID },
|
|
124
|
+
monitorId: { _type: "ObjectID", value: MONITOR_ID },
|
|
125
|
+
monitorStepId: { _type: "ObjectID", value: STEP_ID },
|
|
126
|
+
probeId: { _type: "ObjectID", value: PROBE_ID },
|
|
127
|
+
monitoredAt: "2026-08-20T12:00:00.000Z",
|
|
128
|
+
ingestedAt: "2026-08-20T12:00:01.000Z",
|
|
129
|
+
isOnline: false,
|
|
130
|
+
responseCode: 404,
|
|
131
|
+
failureCause: "",
|
|
132
|
+
...overrides,
|
|
133
|
+
} as unknown as ProbeMonitorResponse;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function makeMonitorProbe(input: {
|
|
137
|
+
probeName: string;
|
|
138
|
+
probeId?: ObjectID | undefined;
|
|
139
|
+
lastMonitoringLog?: MonitorStepProbeResponse | undefined;
|
|
140
|
+
}): MonitorProbe {
|
|
141
|
+
const probe: Probe = new Probe();
|
|
142
|
+
probe.name = input.probeName;
|
|
143
|
+
probe.connectionStatus = ProbeConnectionStatus.Connected;
|
|
144
|
+
|
|
145
|
+
const monitorProbe: MonitorProbe = new MonitorProbe();
|
|
146
|
+
monitorProbe.id = ObjectID.generate();
|
|
147
|
+
monitorProbe.probeId = input.probeId || ObjectID.generate();
|
|
148
|
+
monitorProbe.isEnabled = true;
|
|
149
|
+
monitorProbe.probe = probe;
|
|
150
|
+
|
|
151
|
+
if (input.lastMonitoringLog !== undefined) {
|
|
152
|
+
monitorProbe.lastMonitoringLog = input.lastMonitoringLog;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return monitorProbe;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function makeMonitor(): Monitor {
|
|
159
|
+
const monitor: Monitor = new Monitor();
|
|
160
|
+
monitor.id = ObjectID.generate();
|
|
161
|
+
return monitor;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
describe("MonitorResourceUtil.hydrateStoredProbeResponse", () => {
|
|
165
|
+
const hydrate: (response: ProbeMonitorResponse) => ProbeMonitorResponse = (
|
|
166
|
+
response: ProbeMonitorResponse,
|
|
167
|
+
): ProbeMonitorResponse => {
|
|
168
|
+
return MonitorResourceUtil.hydrateStoredProbeResponse(response);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
test("turns serialized ObjectIDs back into ObjectID instances", () => {
|
|
172
|
+
const hydrated: ProbeMonitorResponse = hydrate(storedProbeResponse());
|
|
173
|
+
|
|
174
|
+
expect(hydrated.projectId).toBeInstanceOf(ObjectID);
|
|
175
|
+
expect(hydrated.monitorId).toBeInstanceOf(ObjectID);
|
|
176
|
+
expect(hydrated.monitorStepId).toBeInstanceOf(ObjectID);
|
|
177
|
+
expect(hydrated.probeId).toBeInstanceOf(ObjectID);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("preserves the id values themselves", () => {
|
|
181
|
+
const hydrated: ProbeMonitorResponse = hydrate(storedProbeResponse());
|
|
182
|
+
|
|
183
|
+
expect(hydrated.projectId.toString()).toBe(PROJECT_ID);
|
|
184
|
+
expect(hydrated.monitorId.toString()).toBe(MONITOR_ID);
|
|
185
|
+
expect(hydrated.monitorStepId.toString()).toBe(STEP_ID);
|
|
186
|
+
expect(hydrated.probeId.toString()).toBe(PROBE_ID);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("accepts a bare id string too", () => {
|
|
190
|
+
const hydrated: ProbeMonitorResponse = hydrate(
|
|
191
|
+
storedProbeResponse({ projectId: PROJECT_ID }),
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
expect(hydrated.projectId).toBeInstanceOf(ObjectID);
|
|
195
|
+
expect(hydrated.projectId.toString()).toBe(PROJECT_ID);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("leaves a real ObjectID alone", () => {
|
|
199
|
+
const projectId: ObjectID = new ObjectID(PROJECT_ID);
|
|
200
|
+
|
|
201
|
+
const hydrated: ProbeMonitorResponse = hydrate(
|
|
202
|
+
storedProbeResponse({ projectId: projectId }),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
expect(hydrated.projectId).toBe(projectId);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("turns serialized dates back into Date instances", () => {
|
|
209
|
+
const hydrated: ProbeMonitorResponse = hydrate(storedProbeResponse());
|
|
210
|
+
|
|
211
|
+
expect(hydrated.monitoredAt).toBeInstanceOf(Date);
|
|
212
|
+
expect(hydrated.monitoredAt.toISOString()).toBe("2026-08-20T12:00:00.000Z");
|
|
213
|
+
expect(hydrated.ingestedAt).toBeInstanceOf(Date);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("leaves a real Date alone", () => {
|
|
217
|
+
const monitoredAt: Date = new Date("2026-08-20T12:00:00.000Z");
|
|
218
|
+
|
|
219
|
+
const hydrated: ProbeMonitorResponse = hydrate(
|
|
220
|
+
storedProbeResponse({ monitoredAt: monitoredAt }),
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect(hydrated.monitoredAt).toBe(monitoredAt);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("carries every other field through untouched", () => {
|
|
227
|
+
const hydrated: ProbeMonitorResponse = hydrate(
|
|
228
|
+
storedProbeResponse({
|
|
229
|
+
responseCode: 503,
|
|
230
|
+
responseTimeInMs: 1234,
|
|
231
|
+
isOnline: false,
|
|
232
|
+
}),
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
expect(hydrated.responseCode).toBe(503);
|
|
236
|
+
expect(hydrated.responseTimeInMs).toBe(1234);
|
|
237
|
+
expect(hydrated.isOnline).toBe(false);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("does not mutate the stored row", () => {
|
|
241
|
+
const stored: ProbeMonitorResponse = storedProbeResponse();
|
|
242
|
+
|
|
243
|
+
hydrate(stored);
|
|
244
|
+
|
|
245
|
+
expect(stored.projectId).not.toBeInstanceOf(ObjectID);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("tolerates missing ids and dates", () => {
|
|
249
|
+
const sparse: ProbeMonitorResponse = {
|
|
250
|
+
isOnline: true,
|
|
251
|
+
} as unknown as ProbeMonitorResponse;
|
|
252
|
+
|
|
253
|
+
expect(() => {
|
|
254
|
+
hydrate(sparse);
|
|
255
|
+
}).not.toThrow();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("tolerates an unparseable date", () => {
|
|
259
|
+
const hydrated: ProbeMonitorResponse = hydrate(
|
|
260
|
+
storedProbeResponse({ monitoredAt: "not-a-date" }),
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
// Left as-is rather than replaced with an Invalid Date.
|
|
264
|
+
expect(hydrated.monitoredAt).toBe("not-a-date" as unknown as Date);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
describe("MonitorResourceUtil.checkProbeAgreement stored-payload handling", () => {
|
|
269
|
+
let monitorStep: MonitorStep;
|
|
270
|
+
let stepId: string;
|
|
271
|
+
|
|
272
|
+
beforeEach(() => {
|
|
273
|
+
jest.clearAllMocks();
|
|
274
|
+
|
|
275
|
+
monitorStep = new MonitorStep();
|
|
276
|
+
stepId = monitorStep.id.toString();
|
|
277
|
+
|
|
278
|
+
processMonitorStepMock.mockImplementation(
|
|
279
|
+
(input: {
|
|
280
|
+
dataToProcess: DataToProcess;
|
|
281
|
+
monitor: Monitor;
|
|
282
|
+
}): Promise<ProbeApiIngestResponse> => {
|
|
283
|
+
return Promise.resolve({
|
|
284
|
+
monitorId: input.monitor.id!,
|
|
285
|
+
criteriaMetId: (input.dataToProcess as any).testCriteriaId,
|
|
286
|
+
rootCause: (input.dataToProcess as any).testRootCause || null,
|
|
287
|
+
});
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("hydrates a stored payload before handing it to the evaluator", async () => {
|
|
293
|
+
const monitor: Monitor = makeMonitor();
|
|
294
|
+
|
|
295
|
+
const otherProbe: MonitorProbe = makeMonitorProbe({
|
|
296
|
+
probeName: "Probe B",
|
|
297
|
+
lastMonitoringLog: {
|
|
298
|
+
[stepId]: storedProbeResponse({ testCriteriaId: "criteria-c" }),
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
await checkProbeAgreement({
|
|
303
|
+
monitor: monitor,
|
|
304
|
+
monitorStep: monitorStep,
|
|
305
|
+
currentCriteriaMetId: "criteria-c",
|
|
306
|
+
currentRootCause: "root cause C",
|
|
307
|
+
monitorProbes: [otherProbe],
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
expect(processMonitorStepMock).toHaveBeenCalledTimes(1);
|
|
311
|
+
|
|
312
|
+
const handed: ProbeMonitorResponse = (
|
|
313
|
+
processMonitorStepMock.mock.calls[0]![0] as {
|
|
314
|
+
dataToProcess: ProbeMonitorResponse;
|
|
315
|
+
}
|
|
316
|
+
).dataToProcess;
|
|
317
|
+
|
|
318
|
+
expect(handed.projectId).toBeInstanceOf(ObjectID);
|
|
319
|
+
expect(handed.projectId.toString()).toBe(PROJECT_ID);
|
|
320
|
+
expect(handed.monitorId).toBeInstanceOf(ObjectID);
|
|
321
|
+
expect(handed.monitoredAt).toBeInstanceOf(Date);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
/*
|
|
325
|
+
* The caller has already evaluated the probe whose result triggered this
|
|
326
|
+
* pass, and that evaluation is the one the user sees. Re-running it here
|
|
327
|
+
* would read a different "over time" window seconds later.
|
|
328
|
+
*/
|
|
329
|
+
test("reuses the caller's verdict for the probe being handled", async () => {
|
|
330
|
+
const monitor: Monitor = makeMonitor();
|
|
331
|
+
const currentProbeId: ObjectID = new ObjectID(PROBE_ID);
|
|
332
|
+
|
|
333
|
+
const currentProbe: MonitorProbe = makeMonitorProbe({
|
|
334
|
+
probeName: "Probe A",
|
|
335
|
+
probeId: currentProbeId,
|
|
336
|
+
lastMonitoringLog: {
|
|
337
|
+
// Deliberately different: it must NOT be what the result reports.
|
|
338
|
+
[stepId]: storedProbeResponse({ testCriteriaId: "stale-criteria" }),
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const result: ProbeAgreementResult = await checkProbeAgreement({
|
|
343
|
+
monitor: monitor,
|
|
344
|
+
monitorStep: monitorStep,
|
|
345
|
+
currentCriteriaMetId: "criteria-live",
|
|
346
|
+
currentRootCause: "root cause live",
|
|
347
|
+
currentProbeId: currentProbeId,
|
|
348
|
+
monitorProbes: [currentProbe],
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
expect(processMonitorStepMock).not.toHaveBeenCalled();
|
|
352
|
+
expect(result.agreedCriteriaId).toBe("criteria-live");
|
|
353
|
+
expect(result.agreedRootCause).toBe("root cause live");
|
|
354
|
+
expect(result.agreementCount).toBe(1);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("still evaluates the other probes", async () => {
|
|
358
|
+
const monitor: Monitor = makeMonitor();
|
|
359
|
+
const currentProbeId: ObjectID = new ObjectID(PROBE_ID);
|
|
360
|
+
|
|
361
|
+
const currentProbe: MonitorProbe = makeMonitorProbe({
|
|
362
|
+
probeName: "Probe A",
|
|
363
|
+
probeId: currentProbeId,
|
|
364
|
+
lastMonitoringLog: {
|
|
365
|
+
[stepId]: storedProbeResponse({ testCriteriaId: "criteria-live" }),
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const otherProbe: MonitorProbe = makeMonitorProbe({
|
|
370
|
+
probeName: "Probe B",
|
|
371
|
+
lastMonitoringLog: {
|
|
372
|
+
[stepId]: storedProbeResponse({ testCriteriaId: "criteria-live" }),
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const result: ProbeAgreementResult = await checkProbeAgreement({
|
|
377
|
+
monitor: monitor,
|
|
378
|
+
monitorStep: monitorStep,
|
|
379
|
+
currentCriteriaMetId: "criteria-live",
|
|
380
|
+
currentRootCause: "root cause live",
|
|
381
|
+
currentProbeId: currentProbeId,
|
|
382
|
+
monitorProbes: [currentProbe, otherProbe],
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
expect(processMonitorStepMock).toHaveBeenCalledTimes(1);
|
|
386
|
+
expect(result.agreementCount).toBe(2);
|
|
387
|
+
expect(result.hasAgreement).toBe(true);
|
|
388
|
+
expect(result.agreedProbeNames).toEqual(["Probe A", "Probe B"]);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
/*
|
|
392
|
+
* The current probe seeing nothing while the others see a breach must
|
|
393
|
+
* still count as a disagreement, not be quietly folded into the majority.
|
|
394
|
+
*/
|
|
395
|
+
test("a caller verdict of 'no criteria met' is counted as its own outcome", async () => {
|
|
396
|
+
const monitor: Monitor = makeMonitor();
|
|
397
|
+
const currentProbeId: ObjectID = new ObjectID(PROBE_ID);
|
|
398
|
+
|
|
399
|
+
const currentProbe: MonitorProbe = makeMonitorProbe({
|
|
400
|
+
probeName: "Probe A",
|
|
401
|
+
probeId: currentProbeId,
|
|
402
|
+
lastMonitoringLog: {
|
|
403
|
+
[stepId]: storedProbeResponse({ testCriteriaId: "criteria-c" }),
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const otherProbe: MonitorProbe = makeMonitorProbe({
|
|
408
|
+
probeName: "Probe B",
|
|
409
|
+
lastMonitoringLog: {
|
|
410
|
+
[stepId]: storedProbeResponse({ testCriteriaId: "criteria-c" }),
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
const result: ProbeAgreementResult = await checkProbeAgreement({
|
|
415
|
+
monitor: monitor,
|
|
416
|
+
monitorStep: monitorStep,
|
|
417
|
+
currentCriteriaMetId: null,
|
|
418
|
+
currentRootCause: null,
|
|
419
|
+
currentProbeId: currentProbeId,
|
|
420
|
+
monitorProbes: [currentProbe, otherProbe],
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// Two probes, both required, but they disagree - no status change.
|
|
424
|
+
expect(result.hasAgreement).toBe(false);
|
|
425
|
+
expect(result.agreementCount).toBe(1);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
/*
|
|
429
|
+
* Without currentProbeId (the incoming-request caller, for instance) the
|
|
430
|
+
* behaviour is unchanged: every stored row is evaluated.
|
|
431
|
+
*/
|
|
432
|
+
test("evaluates every probe when no current probe is named", async () => {
|
|
433
|
+
const monitor: Monitor = makeMonitor();
|
|
434
|
+
|
|
435
|
+
const probeA: MonitorProbe = makeMonitorProbe({
|
|
436
|
+
probeName: "Probe A",
|
|
437
|
+
lastMonitoringLog: {
|
|
438
|
+
[stepId]: storedProbeResponse({ testCriteriaId: "criteria-c" }),
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
const probeB: MonitorProbe = makeMonitorProbe({
|
|
442
|
+
probeName: "Probe B",
|
|
443
|
+
lastMonitoringLog: {
|
|
444
|
+
[stepId]: storedProbeResponse({ testCriteriaId: "criteria-c" }),
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const result: ProbeAgreementResult = await checkProbeAgreement({
|
|
449
|
+
monitor: monitor,
|
|
450
|
+
monitorStep: monitorStep,
|
|
451
|
+
currentCriteriaMetId: "criteria-c",
|
|
452
|
+
currentRootCause: "root cause C",
|
|
453
|
+
monitorProbes: [probeA, probeB],
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
expect(processMonitorStepMock).toHaveBeenCalledTimes(2);
|
|
457
|
+
expect(result.agreementCount).toBe(2);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
@@ -28,6 +28,16 @@
|
|
|
28
28
|
* Postgres, no Redis, no isolated-vm.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
+
/*
|
|
32
|
+
* MonitorResource's import chain reaches the native isolated-vm addon
|
|
33
|
+
* through the sandbox runner. Nothing here touches the sandbox and the
|
|
34
|
+
* prebuilt binary cannot always dlopen in the test environment, so stub it
|
|
35
|
+
* out before anything imports it.
|
|
36
|
+
*/
|
|
37
|
+
jest.mock("isolated-vm", () => {
|
|
38
|
+
return {};
|
|
39
|
+
});
|
|
40
|
+
|
|
31
41
|
jest.mock("../../../Server/Utils/Monitor/MonitorCriteriaEvaluator", () => {
|
|
32
42
|
return {
|
|
33
43
|
__esModule: true,
|