@oneuptime/common 14.0.2 → 14.0.3
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/AlertFeed.ts +2 -0
- package/Models/DatabaseModels/IncidentAlert.ts +540 -0
- package/Models/DatabaseModels/IncidentFeed.ts +2 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/Project.ts +60 -0
- package/Models/DatabaseModels/RumApplication.ts +52 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/IncidentAlertService.ts +1811 -0
- package/Server/Services/IncidentService.ts +210 -10
- package/Server/Services/IncidentStateTimelineService.ts +24 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +24 -2
- package/Tests/App/Dashboard/BulkIncidentLinkActions.test.tsx +869 -0
- package/Tests/App/Dashboard/CephMetricTooltips.test.tsx +1777 -0
- package/Tests/App/Dashboard/CloudFleetSummary.test.ts +8 -0
- package/Tests/App/Dashboard/CloudMetricTooltips.test.tsx +887 -0
- package/Tests/App/Dashboard/ContainerHostMetricTooltips.test.tsx +1309 -0
- package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +39 -0
- package/Tests/App/Dashboard/DockerSwarmClustersListTooltips.test.tsx +414 -0
- package/Tests/App/Dashboard/DockerSwarmMetricTooltips.test.ts +796 -0
- package/Tests/App/Dashboard/DockerSwarmOverviewTooltips.test.tsx +747 -0
- package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +38 -18
- package/Tests/App/Dashboard/HostDetailViewTooltips.test.tsx +725 -0
- package/Tests/App/Dashboard/HostListColumnTooltips.test.tsx +472 -0
- package/Tests/App/Dashboard/HostMetricTooltips.test.ts +709 -0
- package/Tests/App/Dashboard/HostOverviewTooltips.test.tsx +698 -0
- package/Tests/App/Dashboard/HostTooltipHarness.ts +124 -0
- package/Tests/App/Dashboard/HostsListTooltips.test.tsx +395 -0
- package/Tests/App/Dashboard/IncidentAlertLinkDialog.test.tsx +691 -0
- package/Tests/App/Dashboard/IncidentAlertLinkHelpers.test.ts +458 -0
- package/Tests/App/Dashboard/IncidentAlertLinkPages.test.tsx +828 -0
- package/Tests/App/Dashboard/IncidentAlertLinkSideMenus.test.tsx +231 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +689 -0
- package/Tests/App/Dashboard/IoTMetricTooltips.test.tsx +1123 -0
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +837 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +699 -0
- package/Tests/App/Dashboard/KubernetesContainersTabTooltips.test.tsx +320 -0
- package/Tests/App/Dashboard/KubernetesCostTooltips.test.tsx +502 -0
- package/Tests/App/Dashboard/KubernetesInsightsTooltips.test.tsx +222 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricTooltips.test.ts +561 -0
- package/Tests/App/Dashboard/KubernetesResourcePagesTooltips.test.tsx +1168 -0
- package/Tests/App/Dashboard/LinkIncidentAlertModal.test.tsx +154 -0
- package/Tests/App/Dashboard/MetricDescriptionRules.ts +170 -0
- package/Tests/App/Dashboard/MetricDescriptionsCatalog.test.ts +206 -0
- package/Tests/App/Dashboard/MetricDescriptionsConsistency.test.ts +275 -0
- package/Tests/App/Dashboard/NetworkDeviceTooltips.test.tsx +852 -0
- package/Tests/App/Dashboard/NetworkDiagnosticsTooltips.test.tsx +556 -0
- package/Tests/App/Dashboard/NetworkMetricTooltips.test.ts +1348 -0
- package/Tests/App/Dashboard/NetworkSiteTooltips.test.tsx +679 -0
- package/Tests/App/Dashboard/ProxmoxMetricTooltips.test.ts +592 -0
- package/Tests/App/Dashboard/ProxmoxOverviewTooltips.test.tsx +1002 -0
- package/Tests/App/Dashboard/ProxmoxResourcePageTooltips.test.tsx +923 -0
- package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +1066 -0
- package/Tests/App/Dashboard/RumOverviewPage.test.tsx +1195 -0
- package/Tests/App/Dashboard/ServerlessMetricTooltips.test.tsx +474 -0
- package/Tests/App/Dashboard/ServiceMetricTooltips.test.tsx +772 -0
- package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +56 -1
- package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +1207 -0
- package/Tests/App/Dashboard/TelemetryResourceTabTooltips.test.tsx +437 -0
- package/Tests/App/Dashboard/VMwareMetricTooltips.test.ts +801 -0
- package/Tests/App/Dashboard/VMwareOverviewTooltips.test.tsx +601 -0
- package/Tests/App/Dashboard/VMwareResourcePagesTooltips.test.tsx +646 -0
- package/Tests/App/Dashboard/VMwareTooltipHarness.ts +145 -0
- package/Tests/Models/DatabaseModels/IncidentAlertModel.test.ts +554 -0
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +117 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentAlertMigration.test.ts +341 -0
- package/Tests/Server/Infrastructure/Postgres/AddSessionReplaySameOriginTracePropagationMigration.test.ts +247 -0
- package/Tests/Server/Services/IncidentAlertPostgres.test.ts +903 -0
- package/Tests/Server/Services/IncidentAlertRegistration.test.ts +96 -0
- package/Tests/Server/Services/IncidentAlertService.test.ts +2225 -0
- package/Tests/Server/Services/IncidentAlertStateCascade.test.ts +970 -0
- package/Tests/Server/Services/IncidentCreateFromAlerts.test.ts +1284 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +103 -0
- package/Tests/Types/Rum/WebVitals.test.ts +173 -0
- package/Tests/UI/Components/FieldLabelInfoCardTooltip.test.tsx +413 -0
- package/Tests/UI/Components/InfoTooltip.test.tsx +242 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +207 -3
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDeleteVerb.test.tsx +367 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableCreateButtonTitle.test.tsx +275 -0
- package/Tests/UI/Components/TableHeaderTooltip.test.tsx +662 -0
- package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +166 -9
- package/Tests/UI/Rum/RecordingHealthTooltips.test.tsx +447 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +164 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +6750 -0
- package/Tests/UI/Rum/ReplayRail.test.tsx +723 -12
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +86 -0
- package/Tests/UI/Rum/ReplayScreenshot.test.ts +2124 -0
- package/Tests/UI/Rum/ReplayScreenshotActions.test.tsx +3288 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +62 -0
- package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +983 -2
- package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +149 -0
- package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +107 -6
- package/Tests/UI/Rum/SessionReplayUsersTableTooltips.test.tsx +330 -0
- package/Tests/UI/Rum/UserFlowTooltips.test.tsx +886 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/UI/Utils/PermissionGate.test.ts +76 -0
- package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +696 -0
- package/Tests/Utils/Rum/SessionTraceState.test.ts +239 -0
- package/Types/Incident/IncidentAlertLink.ts +38 -0
- package/Types/Permission.ts +48 -0
- package/Types/Rum/SessionReplay.ts +25 -6
- package/Types/Rum/WebVitals.ts +52 -0
- package/UI/Components/Detail/FieldLabel.tsx +8 -0
- package/UI/Components/InfoCard/InfoCard.tsx +47 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +95 -17
- package/UI/Components/ModelTable/Column.ts +5 -0
- package/UI/Components/Table/TableHeader.tsx +47 -2
- package/UI/Components/Table/Types/Column.ts +7 -0
- package/UI/Components/Tooltip/InfoTooltip.tsx +78 -0
- package/UI/Utils/PermissionGate.ts +11 -2
- package/Utils/Incident/IncidentFromAlerts.ts +516 -0
- package/Utils/Rum/SessionTraceState.ts +212 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentAlert.js +553 -0
- package/build/dist/Models/DatabaseModels/IncidentAlert.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +62 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +53 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.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/Services/IncidentAlertService.js +1370 -0
- package/build/dist/Server/Services/IncidentAlertService.js.map +1 -0
- package/build/dist/Server/Services/IncidentService.js +164 -12
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +21 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +8 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Types/Incident/IncidentAlertLink.js +34 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rum/WebVitals.js +25 -0
- package/build/dist/Types/Rum/WebVitals.js.map +1 -1
- package/build/dist/UI/Components/Detail/FieldLabel.js +2 -0
- package/build/dist/UI/Components/Detail/FieldLabel.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +22 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +66 -14
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +18 -2
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js +28 -0
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js.map +1 -0
- package/build/dist/UI/Utils/PermissionGate.js +4 -2
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentFromAlerts.js +306 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -0
- package/build/dist/Utils/Rum/SessionTraceState.js +154 -0
- package/build/dist/Utils/Rum/SessionTraceState.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import IncidentFromAlerts, {
|
|
3
|
+
AlertForIncidentPrefill,
|
|
4
|
+
INCIDENT_PREFILL_RESOURCE_KEYS,
|
|
5
|
+
IncidentPrefillFromAlerts,
|
|
6
|
+
NamedResource,
|
|
7
|
+
ParsedAlertIds,
|
|
8
|
+
SeverityForMapping,
|
|
9
|
+
} from "../../../Utils/Incident/IncidentFromAlerts";
|
|
10
|
+
import { MAX_ALERTS_PER_INCIDENT_LINK_ACTION } from "../../../Types/Incident/IncidentAlertLink";
|
|
11
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
12
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* The declare-incident-from-alerts prefill. Everything the create page puts
|
|
16
|
+
* into the form comes out of these functions, so the rules the product
|
|
17
|
+
* promises - which alert names the incident, how severities map, what gets
|
|
18
|
+
* unioned, and that on-call policies are never copied - are pinned here.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/* The project defaults: alerts get High / Low, incidents get three. */
|
|
22
|
+
const ALERT_HIGH: SeverityForMapping = {
|
|
23
|
+
id: "alert-high",
|
|
24
|
+
name: "High",
|
|
25
|
+
order: 1,
|
|
26
|
+
};
|
|
27
|
+
const ALERT_LOW: SeverityForMapping = {
|
|
28
|
+
id: "alert-low",
|
|
29
|
+
name: "Low",
|
|
30
|
+
order: 2,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const INCIDENT_CRITICAL: SeverityForMapping = {
|
|
34
|
+
id: "incident-critical",
|
|
35
|
+
name: "Critical Incident",
|
|
36
|
+
order: 1,
|
|
37
|
+
};
|
|
38
|
+
const INCIDENT_MAJOR: SeverityForMapping = {
|
|
39
|
+
id: "incident-major",
|
|
40
|
+
name: "Major Incident",
|
|
41
|
+
order: 2,
|
|
42
|
+
};
|
|
43
|
+
const INCIDENT_MINOR: SeverityForMapping = {
|
|
44
|
+
id: "incident-minor",
|
|
45
|
+
name: "Minor Incident",
|
|
46
|
+
order: 3,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const DEFAULT_ALERT_SEVERITIES: Array<SeverityForMapping> = [
|
|
50
|
+
ALERT_LOW,
|
|
51
|
+
ALERT_HIGH,
|
|
52
|
+
];
|
|
53
|
+
const DEFAULT_INCIDENT_SEVERITIES: Array<SeverityForMapping> = [
|
|
54
|
+
INCIDENT_MINOR,
|
|
55
|
+
INCIDENT_CRITICAL,
|
|
56
|
+
INCIDENT_MAJOR,
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
type ResourceFunction = (id: string, name?: string) => NamedResource;
|
|
60
|
+
|
|
61
|
+
const resource: ResourceFunction = (
|
|
62
|
+
id: string,
|
|
63
|
+
name?: string,
|
|
64
|
+
): NamedResource => {
|
|
65
|
+
return { _id: id, name: name || `Resource ${id}` };
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
type AlertFunction = (
|
|
69
|
+
id: string,
|
|
70
|
+
overrides?: Partial<AlertForIncidentPrefill>,
|
|
71
|
+
) => AlertForIncidentPrefill;
|
|
72
|
+
|
|
73
|
+
const alert: AlertFunction = (
|
|
74
|
+
id: string,
|
|
75
|
+
overrides?: Partial<AlertForIncidentPrefill>,
|
|
76
|
+
): AlertForIncidentPrefill => {
|
|
77
|
+
return {
|
|
78
|
+
id: id,
|
|
79
|
+
title: `Alert ${id} title`,
|
|
80
|
+
description: `Alert ${id} description`,
|
|
81
|
+
...overrides,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
type BuildFunction = (
|
|
86
|
+
alerts: Array<AlertForIncidentPrefill>,
|
|
87
|
+
alertSeverities?: Array<SeverityForMapping>,
|
|
88
|
+
incidentSeverities?: Array<SeverityForMapping>,
|
|
89
|
+
) => IncidentPrefillFromAlerts;
|
|
90
|
+
|
|
91
|
+
const build: BuildFunction = (
|
|
92
|
+
alerts: Array<AlertForIncidentPrefill>,
|
|
93
|
+
alertSeverities?: Array<SeverityForMapping>,
|
|
94
|
+
incidentSeverities?: Array<SeverityForMapping>,
|
|
95
|
+
): IncidentPrefillFromAlerts => {
|
|
96
|
+
return IncidentFromAlerts.buildIncidentPrefill({
|
|
97
|
+
alerts: alerts,
|
|
98
|
+
alertSeverities: alertSeverities || DEFAULT_ALERT_SEVERITIES,
|
|
99
|
+
incidentSeverities: incidentSeverities || DEFAULT_INCIDENT_SEVERITIES,
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type MapFunction = (
|
|
104
|
+
alertSeverityId: string | undefined,
|
|
105
|
+
alertSeverities: Array<SeverityForMapping>,
|
|
106
|
+
incidentSeverities: Array<SeverityForMapping>,
|
|
107
|
+
) => string | null;
|
|
108
|
+
|
|
109
|
+
const map: MapFunction = (
|
|
110
|
+
alertSeverityId: string | undefined,
|
|
111
|
+
alertSeverities: Array<SeverityForMapping>,
|
|
112
|
+
incidentSeverities: Array<SeverityForMapping>,
|
|
113
|
+
): string | null => {
|
|
114
|
+
return IncidentFromAlerts.mapAlertSeverityToIncidentSeverity({
|
|
115
|
+
alertSeverityId: alertSeverityId,
|
|
116
|
+
alertSeverities: alertSeverities,
|
|
117
|
+
incidentSeverities: incidentSeverities,
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
type IdsFunction = (resources: Array<NamedResource>) => Array<string>;
|
|
122
|
+
|
|
123
|
+
const idsOf: IdsFunction = (resources: Array<NamedResource>): Array<string> => {
|
|
124
|
+
return resources.map((item: NamedResource): string => {
|
|
125
|
+
return item._id;
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
describe("IncidentFromAlerts", () => {
|
|
130
|
+
describe("parseAlertIdsQueryParam", () => {
|
|
131
|
+
test("reads nothing from a missing or empty parameter", () => {
|
|
132
|
+
for (const value of [null, undefined, "", ",", " , "]) {
|
|
133
|
+
expect(IncidentFromAlerts.parseAlertIdsQueryParam(value)).toEqual({
|
|
134
|
+
alertIds: [],
|
|
135
|
+
wasTruncated: false,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("splits on commas, trims, and drops invalid and repeated ids", () => {
|
|
141
|
+
const first: string = ObjectID.generate().toString();
|
|
142
|
+
const second: string = ObjectID.generate().toString();
|
|
143
|
+
|
|
144
|
+
const parsed: ParsedAlertIds = IncidentFromAlerts.parseAlertIdsQueryParam(
|
|
145
|
+
` ${first} ,not-an-id,${second},${first},,<script>`,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
expect(parsed.alertIds).toEqual([first, second]);
|
|
149
|
+
expect(parsed.wasTruncated).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("caps the ids at the per-action maximum and says it did", () => {
|
|
153
|
+
const ids: Array<string> = Array.from(
|
|
154
|
+
{ length: MAX_ALERTS_PER_INCIDENT_LINK_ACTION + 3 },
|
|
155
|
+
() => {
|
|
156
|
+
return ObjectID.generate().toString();
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const parsed: ParsedAlertIds = IncidentFromAlerts.parseAlertIdsQueryParam(
|
|
161
|
+
ids.join(","),
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(parsed.alertIds).toEqual(
|
|
165
|
+
ids.slice(0, MAX_ALERTS_PER_INCIDENT_LINK_ACTION),
|
|
166
|
+
);
|
|
167
|
+
expect(parsed.wasTruncated).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("is not truncated at exactly the maximum", () => {
|
|
171
|
+
const ids: Array<string> = Array.from(
|
|
172
|
+
{ length: MAX_ALERTS_PER_INCIDENT_LINK_ACTION },
|
|
173
|
+
() => {
|
|
174
|
+
return ObjectID.generate().toString();
|
|
175
|
+
},
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const parsed: ParsedAlertIds = IncidentFromAlerts.parseAlertIdsQueryParam(
|
|
179
|
+
ids.join(","),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
expect(parsed.alertIds).toHaveLength(MAX_ALERTS_PER_INCIDENT_LINK_ACTION);
|
|
183
|
+
expect(parsed.wasTruncated).toBe(false);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("sortSeveritiesByOrder", () => {
|
|
188
|
+
test("puts the most severe (lowest order) first", () => {
|
|
189
|
+
expect(
|
|
190
|
+
IncidentFromAlerts.sortSeveritiesByOrder(DEFAULT_INCIDENT_SEVERITIES),
|
|
191
|
+
).toEqual([INCIDENT_CRITICAL, INCIDENT_MAJOR, INCIDENT_MINOR]);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("sorts severities without an order after every ordered one, keeping their input order", () => {
|
|
195
|
+
const unorderedA: SeverityForMapping = { id: "a", name: "A" };
|
|
196
|
+
const unorderedB: SeverityForMapping = { id: "b", name: "B" };
|
|
197
|
+
|
|
198
|
+
expect(
|
|
199
|
+
IncidentFromAlerts.sortSeveritiesByOrder([
|
|
200
|
+
unorderedA,
|
|
201
|
+
INCIDENT_MINOR,
|
|
202
|
+
unorderedB,
|
|
203
|
+
INCIDENT_CRITICAL,
|
|
204
|
+
]),
|
|
205
|
+
).toEqual([INCIDENT_CRITICAL, INCIDENT_MINOR, unorderedA, unorderedB]);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("keeps input order for equal orders and does not mutate the input", () => {
|
|
209
|
+
const first: SeverityForMapping = { id: "first", order: 1 };
|
|
210
|
+
const second: SeverityForMapping = { id: "second", order: 1 };
|
|
211
|
+
const input: Array<SeverityForMapping> = [second, first];
|
|
212
|
+
|
|
213
|
+
expect(IncidentFromAlerts.sortSeveritiesByOrder(input)).toEqual([
|
|
214
|
+
second,
|
|
215
|
+
first,
|
|
216
|
+
]);
|
|
217
|
+
expect(input).toEqual([second, first]);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("handles an empty list", () => {
|
|
221
|
+
expect(IncidentFromAlerts.sortSeveritiesByOrder([])).toEqual([]);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe("getSeverityRank", () => {
|
|
226
|
+
test("ranks by order, most severe as 0", () => {
|
|
227
|
+
expect(
|
|
228
|
+
IncidentFromAlerts.getSeverityRank(
|
|
229
|
+
"alert-high",
|
|
230
|
+
DEFAULT_ALERT_SEVERITIES,
|
|
231
|
+
),
|
|
232
|
+
).toBe(0);
|
|
233
|
+
expect(
|
|
234
|
+
IncidentFromAlerts.getSeverityRank(
|
|
235
|
+
"alert-low",
|
|
236
|
+
DEFAULT_ALERT_SEVERITIES,
|
|
237
|
+
),
|
|
238
|
+
).toBe(1);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("is null for an unknown or missing severity", () => {
|
|
242
|
+
expect(
|
|
243
|
+
IncidentFromAlerts.getSeverityRank("nope", DEFAULT_ALERT_SEVERITIES),
|
|
244
|
+
).toBeNull();
|
|
245
|
+
expect(
|
|
246
|
+
IncidentFromAlerts.getSeverityRank(undefined, DEFAULT_ALERT_SEVERITIES),
|
|
247
|
+
).toBeNull();
|
|
248
|
+
expect(IncidentFromAlerts.getSeverityRank("alert-high", [])).toBeNull();
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe("mapAlertSeverityToIncidentSeverity", () => {
|
|
253
|
+
test("maps by rank: the most severe alert severity to the most severe incident severity", () => {
|
|
254
|
+
expect(
|
|
255
|
+
map(
|
|
256
|
+
"alert-high",
|
|
257
|
+
DEFAULT_ALERT_SEVERITIES,
|
|
258
|
+
DEFAULT_INCIDENT_SEVERITIES,
|
|
259
|
+
),
|
|
260
|
+
).toBe("incident-critical");
|
|
261
|
+
expect(
|
|
262
|
+
map("alert-low", DEFAULT_ALERT_SEVERITIES, DEFAULT_INCIDENT_SEVERITIES),
|
|
263
|
+
).toBe("incident-major");
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("clamps to the least severe incident severity when there are fewer of them", () => {
|
|
267
|
+
const alertSeverities: Array<SeverityForMapping> = [
|
|
268
|
+
{ id: "a1", name: "P1", order: 1 },
|
|
269
|
+
{ id: "a2", name: "P2", order: 2 },
|
|
270
|
+
{ id: "a3", name: "P3", order: 3 },
|
|
271
|
+
{ id: "a4", name: "P4", order: 4 },
|
|
272
|
+
];
|
|
273
|
+
const incidentSeverities: Array<SeverityForMapping> = [
|
|
274
|
+
{ id: "i1", name: "Sev 1", order: 10 },
|
|
275
|
+
{ id: "i2", name: "Sev 2", order: 20 },
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
expect(map("a1", alertSeverities, incidentSeverities)).toBe("i1");
|
|
279
|
+
expect(map("a2", alertSeverities, incidentSeverities)).toBe("i2");
|
|
280
|
+
expect(map("a3", alertSeverities, incidentSeverities)).toBe("i2");
|
|
281
|
+
expect(map("a4", alertSeverities, incidentSeverities)).toBe("i2");
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("an exact name match wins over rank, ignoring case and surrounding spaces", () => {
|
|
285
|
+
const alertSeverities: Array<SeverityForMapping> = [
|
|
286
|
+
{ id: "a-critical", name: "Critical", order: 1 },
|
|
287
|
+
{ id: "a-warning", name: "Warning", order: 2 },
|
|
288
|
+
];
|
|
289
|
+
const incidentSeverities: Array<SeverityForMapping> = [
|
|
290
|
+
{ id: "i-sev1", name: "SEV1", order: 1 },
|
|
291
|
+
{ id: "i-warning", name: " warning ", order: 3 },
|
|
292
|
+
{ id: "i-critical", name: "CRITICAL", order: 2 },
|
|
293
|
+
];
|
|
294
|
+
|
|
295
|
+
// By rank these would be SEV1 and CRITICAL.
|
|
296
|
+
expect(map("a-critical", alertSeverities, incidentSeverities)).toBe(
|
|
297
|
+
"i-critical",
|
|
298
|
+
);
|
|
299
|
+
expect(map("a-warning", alertSeverities, incidentSeverities)).toBe(
|
|
300
|
+
"i-warning",
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("does not treat two unnamed severities as a name match", () => {
|
|
305
|
+
const alertSeverities: Array<SeverityForMapping> = [
|
|
306
|
+
{ id: "a1", order: 1 },
|
|
307
|
+
{ id: "a2", order: 2 },
|
|
308
|
+
];
|
|
309
|
+
const incidentSeverities: Array<SeverityForMapping> = [
|
|
310
|
+
{ id: "i1", order: 1 },
|
|
311
|
+
{ id: "i2", order: 2 },
|
|
312
|
+
];
|
|
313
|
+
|
|
314
|
+
expect(map("a2", alertSeverities, incidentSeverities)).toBe("i2");
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("ranks severities without an order after the ordered ones", () => {
|
|
318
|
+
const alertSeverities: Array<SeverityForMapping> = [
|
|
319
|
+
{ id: "a-unordered", name: "Other" },
|
|
320
|
+
{ id: "a1", name: "P1", order: 1 },
|
|
321
|
+
];
|
|
322
|
+
const incidentSeverities: Array<SeverityForMapping> = [
|
|
323
|
+
{ id: "i-unordered", name: "Something" },
|
|
324
|
+
{ id: "i1", name: "Sev 1", order: 5 },
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
expect(map("a1", alertSeverities, incidentSeverities)).toBe("i1");
|
|
328
|
+
expect(map("a-unordered", alertSeverities, incidentSeverities)).toBe(
|
|
329
|
+
"i-unordered",
|
|
330
|
+
);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test("is null without a known alert severity or without incident severities", () => {
|
|
334
|
+
expect(
|
|
335
|
+
map(undefined, DEFAULT_ALERT_SEVERITIES, DEFAULT_INCIDENT_SEVERITIES),
|
|
336
|
+
).toBeNull();
|
|
337
|
+
expect(
|
|
338
|
+
map("unknown", DEFAULT_ALERT_SEVERITIES, DEFAULT_INCIDENT_SEVERITIES),
|
|
339
|
+
).toBeNull();
|
|
340
|
+
expect(map("alert-high", DEFAULT_ALERT_SEVERITIES, [])).toBeNull();
|
|
341
|
+
expect(map("alert-high", [], DEFAULT_INCIDENT_SEVERITIES)).toBeNull();
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe("getMostSevereAlert", () => {
|
|
346
|
+
test("picks the alert whose severity ranks highest", () => {
|
|
347
|
+
const low: AlertForIncidentPrefill = alert("1", {
|
|
348
|
+
alertSeverityId: "alert-low",
|
|
349
|
+
});
|
|
350
|
+
const high: AlertForIncidentPrefill = alert("2", {
|
|
351
|
+
alertSeverityId: "alert-high",
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
expect(
|
|
355
|
+
IncidentFromAlerts.getMostSevereAlert(
|
|
356
|
+
[low, high],
|
|
357
|
+
DEFAULT_ALERT_SEVERITIES,
|
|
358
|
+
),
|
|
359
|
+
).toBe(high);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test("keeps the first alert on a tie", () => {
|
|
363
|
+
const first: AlertForIncidentPrefill = alert("1", {
|
|
364
|
+
alertSeverityId: "alert-high",
|
|
365
|
+
});
|
|
366
|
+
const second: AlertForIncidentPrefill = alert("2", {
|
|
367
|
+
alertSeverityId: "alert-high",
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
expect(
|
|
371
|
+
IncidentFromAlerts.getMostSevereAlert(
|
|
372
|
+
[first, second],
|
|
373
|
+
DEFAULT_ALERT_SEVERITIES,
|
|
374
|
+
),
|
|
375
|
+
).toBe(first);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test("never lets an alert without a known severity win over one with a severity", () => {
|
|
379
|
+
const unknown: AlertForIncidentPrefill = alert("1");
|
|
380
|
+
const stale: AlertForIncidentPrefill = alert("2", {
|
|
381
|
+
alertSeverityId: "deleted-severity",
|
|
382
|
+
});
|
|
383
|
+
const low: AlertForIncidentPrefill = alert("3", {
|
|
384
|
+
alertSeverityId: "alert-low",
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
expect(
|
|
388
|
+
IncidentFromAlerts.getMostSevereAlert(
|
|
389
|
+
[unknown, stale, low],
|
|
390
|
+
DEFAULT_ALERT_SEVERITIES,
|
|
391
|
+
),
|
|
392
|
+
).toBe(low);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test("falls back to the first alert when none has a known severity", () => {
|
|
396
|
+
const first: AlertForIncidentPrefill = alert("1");
|
|
397
|
+
const second: AlertForIncidentPrefill = alert("2");
|
|
398
|
+
|
|
399
|
+
expect(IncidentFromAlerts.getMostSevereAlert([first, second], [])).toBe(
|
|
400
|
+
first,
|
|
401
|
+
);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test("is null for no alerts", () => {
|
|
405
|
+
expect(
|
|
406
|
+
IncidentFromAlerts.getMostSevereAlert([], DEFAULT_ALERT_SEVERITIES),
|
|
407
|
+
).toBeNull();
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
describe("getAlertReference", () => {
|
|
412
|
+
test("prefers the prefixed number, then the plain number", () => {
|
|
413
|
+
expect(
|
|
414
|
+
IncidentFromAlerts.getAlertReference(
|
|
415
|
+
alert("1", { alertNumber: 7, alertNumberWithPrefix: "ALT-7" }),
|
|
416
|
+
),
|
|
417
|
+
).toBe("Alert ALT-7");
|
|
418
|
+
expect(
|
|
419
|
+
IncidentFromAlerts.getAlertReference(alert("1", { alertNumber: 7 })),
|
|
420
|
+
).toBe("Alert #7");
|
|
421
|
+
expect(IncidentFromAlerts.getAlertReference(alert("1"))).toBe("Alert");
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
describe("buildIncidentPrefill", () => {
|
|
426
|
+
test("returns an empty prefill for no alerts", () => {
|
|
427
|
+
expect(build([])).toEqual({
|
|
428
|
+
title: "",
|
|
429
|
+
description: "",
|
|
430
|
+
incidentSeverityId: undefined,
|
|
431
|
+
monitors: [],
|
|
432
|
+
hosts: [],
|
|
433
|
+
kubernetesClusters: [],
|
|
434
|
+
dockerHosts: [],
|
|
435
|
+
podmanHosts: [],
|
|
436
|
+
services: [],
|
|
437
|
+
labelIds: [],
|
|
438
|
+
isPrivate: false,
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test("a single alert gives its own title, description and mapped severity", () => {
|
|
443
|
+
const prefill: IncidentPrefillFromAlerts = build([
|
|
444
|
+
alert("1", {
|
|
445
|
+
title: "Checkout latency is high",
|
|
446
|
+
description: "p99 above 2s for 10 minutes.",
|
|
447
|
+
alertSeverityId: "alert-low",
|
|
448
|
+
}),
|
|
449
|
+
]);
|
|
450
|
+
|
|
451
|
+
expect(prefill.title).toBe("Checkout latency is high");
|
|
452
|
+
expect(prefill.description).toBe("p99 above 2s for 10 minutes.");
|
|
453
|
+
expect(prefill.incidentSeverityId).toBe("incident-major");
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test("a single alert without a description leaves the description empty", () => {
|
|
457
|
+
const prefill: IncidentPrefillFromAlerts = build([
|
|
458
|
+
alert("1", { description: undefined }),
|
|
459
|
+
]);
|
|
460
|
+
|
|
461
|
+
expect(prefill.description).toBe("");
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
test("several alerts take the most severe alert's title and list every alert in the description", () => {
|
|
465
|
+
const prefill: IncidentPrefillFromAlerts = build([
|
|
466
|
+
alert("1", {
|
|
467
|
+
title: "Disk filling up",
|
|
468
|
+
alertNumber: 11,
|
|
469
|
+
alertSeverityId: "alert-low",
|
|
470
|
+
}),
|
|
471
|
+
alert("2", {
|
|
472
|
+
title: "Database is down",
|
|
473
|
+
alertNumber: 12,
|
|
474
|
+
alertNumberWithPrefix: "ALT-12",
|
|
475
|
+
alertSeverityId: "alert-high",
|
|
476
|
+
}),
|
|
477
|
+
alert("3", { title: "Queue backing up" }),
|
|
478
|
+
]);
|
|
479
|
+
|
|
480
|
+
expect(prefill.title).toBe("Database is down");
|
|
481
|
+
expect(prefill.description).toBe(
|
|
482
|
+
[
|
|
483
|
+
"- Alert #11: Disk filling up",
|
|
484
|
+
"- Alert ALT-12: Database is down",
|
|
485
|
+
"- Alert: Queue backing up",
|
|
486
|
+
].join("\n"),
|
|
487
|
+
);
|
|
488
|
+
expect(prefill.incidentSeverityId).toBe("incident-critical");
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
test("several alerts with no severity take the first alert's title and no severity", () => {
|
|
492
|
+
const prefill: IncidentPrefillFromAlerts = build([
|
|
493
|
+
alert("1", { title: "First" }),
|
|
494
|
+
alert("2", { title: "Second" }),
|
|
495
|
+
]);
|
|
496
|
+
|
|
497
|
+
expect(prefill.title).toBe("First");
|
|
498
|
+
expect(prefill.incidentSeverityId).toBeUndefined();
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
test("leaves the severity unset when the project has no incident severities", () => {
|
|
502
|
+
const prefill: IncidentPrefillFromAlerts = build(
|
|
503
|
+
[alert("1", { alertSeverityId: "alert-high" })],
|
|
504
|
+
DEFAULT_ALERT_SEVERITIES,
|
|
505
|
+
[],
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
expect(prefill.incidentSeverityId).toBeUndefined();
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test("unions monitors across alerts without duplicates, in first-seen order", () => {
|
|
512
|
+
const prefill: IncidentPrefillFromAlerts = build([
|
|
513
|
+
alert("1", { monitor: resource("m1", "API") }),
|
|
514
|
+
alert("2", { monitor: resource("m2", "Website") }),
|
|
515
|
+
alert("3", { monitor: resource("m1", "API") }),
|
|
516
|
+
alert("4"),
|
|
517
|
+
]);
|
|
518
|
+
|
|
519
|
+
expect(prefill.monitors).toEqual([
|
|
520
|
+
{ _id: "m1", name: "API" },
|
|
521
|
+
{ _id: "m2", name: "Website" },
|
|
522
|
+
]);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
test.each(INCIDENT_PREFILL_RESOURCE_KEYS)(
|
|
526
|
+
"unions %s across alerts without duplicates",
|
|
527
|
+
(
|
|
528
|
+
key:
|
|
529
|
+
| "hosts"
|
|
530
|
+
| "kubernetesClusters"
|
|
531
|
+
| "dockerHosts"
|
|
532
|
+
| "podmanHosts"
|
|
533
|
+
| "services",
|
|
534
|
+
) => {
|
|
535
|
+
const prefill: IncidentPrefillFromAlerts = build([
|
|
536
|
+
alert("1", { [key]: [resource("r1"), resource("r2")] }),
|
|
537
|
+
alert("2", { [key]: [resource("r2"), resource("r3")] }),
|
|
538
|
+
alert("3", { [key]: [] }),
|
|
539
|
+
]);
|
|
540
|
+
|
|
541
|
+
expect(idsOf(prefill[key])).toEqual(["r1", "r2", "r3"]);
|
|
542
|
+
|
|
543
|
+
// Nothing leaks into the other lists.
|
|
544
|
+
for (const otherKey of INCIDENT_PREFILL_RESOURCE_KEYS) {
|
|
545
|
+
if (otherKey !== key) {
|
|
546
|
+
expect(prefill[otherKey]).toEqual([]);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
expect(prefill.monitors).toEqual([]);
|
|
550
|
+
},
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
test("skips resources without an id", () => {
|
|
554
|
+
const prefill: IncidentPrefillFromAlerts = build([
|
|
555
|
+
alert("1", {
|
|
556
|
+
monitor: resource(""),
|
|
557
|
+
hosts: [resource(""), resource("h1")],
|
|
558
|
+
}),
|
|
559
|
+
]);
|
|
560
|
+
|
|
561
|
+
expect(prefill.monitors).toEqual([]);
|
|
562
|
+
expect(idsOf(prefill.hosts)).toEqual(["h1"]);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
test("unions labels without duplicates or blanks", () => {
|
|
566
|
+
const prefill: IncidentPrefillFromAlerts = build([
|
|
567
|
+
alert("1", { labelIds: ["l1", "l2"] }),
|
|
568
|
+
alert("2", { labelIds: ["l2", "", "l3"] }),
|
|
569
|
+
]);
|
|
570
|
+
|
|
571
|
+
expect(prefill.labelIds).toEqual(["l1", "l2", "l3"]);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
test("is private when any alert is private", () => {
|
|
575
|
+
expect(
|
|
576
|
+
build([alert("1"), alert("2", { isPrivate: true })]).isPrivate,
|
|
577
|
+
).toBe(true);
|
|
578
|
+
expect(
|
|
579
|
+
build([alert("1", { isPrivate: false }), alert("2")]).isPrivate,
|
|
580
|
+
).toBe(false);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
test("never carries on-call policies", () => {
|
|
584
|
+
const prefill: IncidentPrefillFromAlerts = build([
|
|
585
|
+
alert("1", {
|
|
586
|
+
alertSeverityId: "alert-high",
|
|
587
|
+
monitor: resource("m1"),
|
|
588
|
+
labelIds: ["l1"],
|
|
589
|
+
}),
|
|
590
|
+
]);
|
|
591
|
+
|
|
592
|
+
expect(Object.keys(prefill)).not.toContain("onCallDutyPolicies");
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
describe("applyPrefillToInitialValues", () => {
|
|
597
|
+
const prefill: IncidentPrefillFromAlerts = build([
|
|
598
|
+
alert("1", {
|
|
599
|
+
title: "Database is down",
|
|
600
|
+
description: "Primary is unreachable.",
|
|
601
|
+
alertSeverityId: "alert-high",
|
|
602
|
+
monitor: resource("m1", "DB"),
|
|
603
|
+
hosts: [resource("h1", "db-1")],
|
|
604
|
+
services: [resource("s1", "Checkout")],
|
|
605
|
+
labelIds: ["l1"],
|
|
606
|
+
isPrivate: true,
|
|
607
|
+
}),
|
|
608
|
+
]);
|
|
609
|
+
|
|
610
|
+
test("fills an empty form with the whole prefill", () => {
|
|
611
|
+
expect(
|
|
612
|
+
IncidentFromAlerts.applyPrefillToInitialValues(
|
|
613
|
+
{ currentIncidentState: "state-1" },
|
|
614
|
+
prefill,
|
|
615
|
+
),
|
|
616
|
+
).toEqual({
|
|
617
|
+
currentIncidentState: "state-1",
|
|
618
|
+
title: "Database is down",
|
|
619
|
+
description: "Primary is unreachable.",
|
|
620
|
+
incidentSeverity: "incident-critical",
|
|
621
|
+
monitors: [{ _id: "m1", name: "DB" }],
|
|
622
|
+
hosts: [{ _id: "h1", name: "db-1" }],
|
|
623
|
+
services: [{ _id: "s1", name: "Checkout" }],
|
|
624
|
+
labels: ["l1"],
|
|
625
|
+
isPrivate: true,
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
test("keeps what a template already set, and unions resources and labels with it", () => {
|
|
630
|
+
const templateValues: JSONObject = {
|
|
631
|
+
title: "Template title",
|
|
632
|
+
description: "Template description",
|
|
633
|
+
incidentSeverity: "incident-minor",
|
|
634
|
+
monitors: [
|
|
635
|
+
{ _id: "m0", name: "Template monitor" },
|
|
636
|
+
{ _id: "m1", name: "DB" },
|
|
637
|
+
],
|
|
638
|
+
hosts: [{ _id: "h0", name: "Template host" }],
|
|
639
|
+
labels: ["l0", "l1"],
|
|
640
|
+
onCallDutyPolicies: ["policy-1"],
|
|
641
|
+
isPrivate: false,
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
const merged: JSONObject = IncidentFromAlerts.applyPrefillToInitialValues(
|
|
645
|
+
templateValues,
|
|
646
|
+
prefill,
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
expect(merged["title"]).toBe("Template title");
|
|
650
|
+
expect(merged["description"]).toBe("Template description");
|
|
651
|
+
expect(merged["incidentSeverity"]).toBe("incident-minor");
|
|
652
|
+
expect(merged["monitors"]).toEqual([
|
|
653
|
+
{ _id: "m0", name: "Template monitor" },
|
|
654
|
+
{ _id: "m1", name: "DB" },
|
|
655
|
+
]);
|
|
656
|
+
expect(merged["hosts"]).toEqual([
|
|
657
|
+
{ _id: "h0", name: "Template host" },
|
|
658
|
+
{ _id: "h1", name: "db-1" },
|
|
659
|
+
]);
|
|
660
|
+
expect(merged["labels"]).toEqual(["l0", "l1"]);
|
|
661
|
+
// The template's own on-call policies stay; none come from the alerts.
|
|
662
|
+
expect(merged["onCallDutyPolicies"]).toEqual(["policy-1"]);
|
|
663
|
+
expect(merged["isPrivate"]).toBe(true);
|
|
664
|
+
// The input is not mutated.
|
|
665
|
+
expect(templateValues["title"]).toBe("Template title");
|
|
666
|
+
expect(templateValues["isPrivate"]).toBe(false);
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
test("fills a blank template title", () => {
|
|
670
|
+
const merged: JSONObject = IncidentFromAlerts.applyPrefillToInitialValues(
|
|
671
|
+
{ title: " " },
|
|
672
|
+
prefill,
|
|
673
|
+
);
|
|
674
|
+
|
|
675
|
+
expect(merged["title"]).toBe("Database is down");
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
test("does not add empty lists or a false isPrivate", () => {
|
|
679
|
+
const merged: JSONObject = IncidentFromAlerts.applyPrefillToInitialValues(
|
|
680
|
+
{},
|
|
681
|
+
build([alert("1", { title: "Only a title", description: "" })]),
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
expect(merged).toEqual({ title: "Only a title" });
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
test("never adds on-call policies", () => {
|
|
688
|
+
const merged: JSONObject = IncidentFromAlerts.applyPrefillToInitialValues(
|
|
689
|
+
{},
|
|
690
|
+
prefill,
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
expect(merged["onCallDutyPolicies"]).toBeUndefined();
|
|
694
|
+
});
|
|
695
|
+
});
|
|
696
|
+
});
|