@oneuptime/common 12.0.17 → 12.0.19
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/EnterpriseLicense.ts +23 -0
- package/Models/DatabaseModels/Index.ts +4 -2
- package/Models/DatabaseModels/Monitor.ts +39 -9
- package/Models/DatabaseModels/UserMicrosoftTeams.ts +344 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +144 -8
- package/Models/DatabaseModels/UserNotificationSetting.ts +34 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +95 -0
- package/Models/DatabaseModels/UserSlack.ts +341 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +9 -0
- package/Server/API/UserMicrosoftTeamsAPI.ts +127 -0
- package/Server/API/UserSlackAPI.ts +125 -0
- package/Server/EnvironmentConfig.ts +12 -107
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.ts +117 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.ts +197 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Infrastructure/Queue.ts +7 -0
- package/Server/Services/BillingService.ts +8 -0
- package/Server/Services/EnterpriseLicenseService.ts +60 -0
- package/Server/Services/Index.ts +6 -2
- package/Server/Services/LlmLogService.ts +25 -9
- package/Server/Services/OnCallReadinessService.ts +86 -2
- package/Server/Services/ProjectService.ts +67 -0
- package/Server/Services/UserMicrosoftTeamsService.ts +208 -0
- package/Server/Services/UserNotificationMethodAdminService.ts +167 -5
- package/Server/Services/UserNotificationRuleAdminService.ts +40 -6
- package/Server/Services/UserNotificationRuleService.ts +589 -11
- package/Server/Services/UserNotificationSettingService.ts +138 -0
- package/Server/Services/UserService.ts +28 -0
- package/Server/Services/UserSlackService.ts +218 -0
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +67 -1
- package/Server/Services/WorkspaceUserAuthTokenService.ts +73 -0
- package/Server/Services/WorkspaceUserNotificationService.ts +190 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +145 -0
- package/Server/Utils/Marketing/MarketingEventWebhook.ts +114 -0
- package/Server/Utils/Monitor/MonitorLogUtil.ts +14 -1
- package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +321 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +191 -4
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +22 -14
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +22 -11
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +37 -1
- package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +18 -10
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +17 -0
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +27 -1
- package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +214 -0
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +145 -26
- package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +2 -2
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +11 -1
- package/Tests/Server/Services/AdminRuleEditGuards.test.ts +22 -8
- package/Tests/Server/Services/BillingService.test.ts +20 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +18 -2
- package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +5 -1
- package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +640 -0
- package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +246 -22
- package/Tests/Server/Services/NotificationDeletionImpact.test.ts +136 -8
- package/Tests/Server/Services/OnCallNotificationFallback.test.ts +191 -8
- package/Tests/Server/Services/OnCallReadinessService.test.ts +343 -18
- package/Tests/Server/Services/SeverityRuleBackfill.test.ts +5 -1
- package/Tests/Server/Services/UserMicrosoftTeamsService.test.ts +343 -0
- package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +209 -8
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +22 -6
- package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +12 -6
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +37 -3
- package/Tests/Server/Services/UserNotificationRuleWorkspaceDelivery.test.ts +669 -0
- package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +410 -0
- package/Tests/Server/Services/UserSlackService.test.ts +407 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenDisconnectCascade.test.ts +224 -0
- package/Tests/Server/Services/WorkspaceUserAuthTokenNotificationMethodCascade.test.ts +189 -0
- package/Tests/Server/Services/WorkspaceUserNotificationService.test.ts +496 -0
- package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +30 -17
- package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +8 -3
- package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +72 -3
- package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +22 -18
- package/Tests/Server/Types/Database/Permissions/WorkspaceMethodStampedColumnCreate.test.ts +178 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +259 -0
- package/Tests/Server/Utils/Marketing/MarketingEventWebhook.test.ts +257 -0
- package/Tests/Server/Utils/Monitor/MonitorLogUtilRedaction.test.ts +300 -0
- package/Tests/Server/Utils/Monitor/MonitorPayloadRedaction.test.ts +381 -0
- package/Tests/Server/Utils/Runbook/RunbookExecutePermission.test.ts +228 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayErasureTombstone.test.ts +189 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +205 -0
- package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +275 -0
- package/Tests/Server/Utils/Telemetry/TelemetryContext.test.ts +279 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +454 -0
- package/Tests/Types/Kubernetes/KubernetesObjectParsers.test.ts +794 -0
- package/Tests/Types/Kubernetes/KubernetesRightSizing.test.ts +53 -0
- package/Tests/Types/Measurement/MeasurementAggregationType.test.ts +100 -0
- package/Tests/Types/Monitor/MonitorStepConfigHelpers.test.ts +114 -0
- package/Tests/Types/Workspace/WorkspaceType.test.ts +53 -0
- package/Tests/UI/Utils/NotificationMethodUtil.test.ts +137 -9
- package/Tests/UI/Utils/PermissionGate.test.ts +105 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentDefaults.test.ts +257 -0
- package/Types/Billing/PayAsYouGoPricing.ts +1 -1
- package/Types/Marketing/MarketingEvent.ts +97 -0
- package/UI/Utils/NotificationMethodUtil.ts +41 -1
- package/UI/Utils/PermissionGate.ts +56 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +24 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -2
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +39 -9
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js +363 -0
- package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +145 -8
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +38 -0
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +96 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSlack.js +361 -0
- package/build/dist/Models/DatabaseModels/UserSlack.js.map +1 -0
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +82 -0
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -0
- package/build/dist/Server/API/UserSlackAPI.js +81 -0
- package/build/dist/Server/API/UserSlackAPI.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +10 -70
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js +37 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js +106 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js +78 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +7 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +6 -0
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseService.js +65 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +6 -2
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LlmLogService.js +25 -9
- package/build/dist/Server/Services/LlmLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallReadinessService.js +74 -2
- package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +56 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/UserMicrosoftTeamsService.js +183 -0
- package/build/dist/Server/Services/UserMicrosoftTeamsService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js +138 -2
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +32 -4
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +499 -67
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +118 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +26 -1
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSlackService.js +195 -0
- package/build/dist/Server/Services/UserSlackService.js.map +1 -0
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +64 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js +70 -0
- package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceUserNotificationService.js +129 -0
- package/build/dist/Server/Services/WorkspaceUserNotificationService.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +94 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js +93 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +12 -1
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +233 -0
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +141 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -1
- package/build/dist/Types/Marketing/MarketingEvent.js +43 -0
- package/build/dist/Types/Marketing/MarketingEvent.js.map +1 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js +33 -1
- package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -1
- package/build/dist/UI/Utils/PermissionGate.js +32 -0
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/package.json +1 -1
- package/Models/DatabaseModels/MarketingConversion.ts +0 -410
- package/Server/Services/MarketingConversionService.ts +0 -10
- package/Server/Utils/Marketing/ConversionUploadProvider.ts +0 -215
- package/Server/Utils/Marketing/ConversionUploadProviders.ts +0 -22
- package/Server/Utils/Marketing/Providers/GoogleAds.ts +0 -333
- package/Server/Utils/Marketing/Providers/LinkedIn.ts +0 -185
- package/Server/Utils/Marketing/Providers/Meta.ts +0 -182
- package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +0 -223
- package/Server/Utils/Marketing/Providers/Reddit.ts +0 -208
- package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +0 -315
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +0 -300
- package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +0 -592
- package/Tests/Server/Utils/Marketing/LinkedIn.test.ts +0 -282
- package/Tests/Server/Utils/Marketing/Meta.test.ts +0 -304
- package/Tests/Server/Utils/Marketing/MicrosoftAds.test.ts +0 -263
- package/Tests/Server/Utils/Marketing/Reddit.test.ts +0 -259
- package/Types/Marketing/MarketingConversion.ts +0 -53
- package/build/dist/Models/DatabaseModels/MarketingConversion.js +0 -445
- package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +0 -1
- package/build/dist/Server/Services/MarketingConversionService.js +0 -9
- package/build/dist/Server/Services/MarketingConversionService.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +0 -93
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js +0 -20
- package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +0 -221
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +0 -137
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js +0 -133
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +0 -143
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +0 -145
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +0 -1
- package/build/dist/Types/Marketing/MarketingConversion.js +0 -52
- package/build/dist/Types/Marketing/MarketingConversion.js.map +0 -1
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
buildRightSizingRecommendation,
|
|
8
8
|
formatCpuCores,
|
|
9
9
|
formatMemoryBytes,
|
|
10
|
+
getVerdictLabel,
|
|
10
11
|
hasActionableRecommendation,
|
|
11
12
|
} from "../../../Types/Kubernetes/KubernetesRightSizing";
|
|
12
13
|
import { describe, expect, test } from "@jest/globals";
|
|
@@ -321,3 +322,55 @@ describe("formatting", () => {
|
|
|
321
322
|
expect(formatMemoryBytes(null)).toBe("-");
|
|
322
323
|
});
|
|
323
324
|
});
|
|
325
|
+
|
|
326
|
+
/*
|
|
327
|
+
* getVerdictLabel turns the internal verdict enum into the human string the
|
|
328
|
+
* right-sizing UI renders. It is a switch with a default arm, so the failure it
|
|
329
|
+
* invites is a new verdict added to the enum without a matching case — it would
|
|
330
|
+
* silently render as "Unavailable". This block pins each label and, crucially,
|
|
331
|
+
* that every enum member has an explicit, distinct, non-"Unavailable" label
|
|
332
|
+
* except the one that is meant to be Unavailable.
|
|
333
|
+
*/
|
|
334
|
+
describe("getVerdictLabel", () => {
|
|
335
|
+
const EXPECTED: Array<[RightSizingVerdict, string]> = [
|
|
336
|
+
[RightSizingVerdict.Overprovisioned, "Over-provisioned"],
|
|
337
|
+
[RightSizingVerdict.Underprovisioned, "Under-provisioned"],
|
|
338
|
+
[RightSizingVerdict.Optimal, "Right-sized"],
|
|
339
|
+
[RightSizingVerdict.NoRequestSet, "No request set"],
|
|
340
|
+
[RightSizingVerdict.Unavailable, "Unavailable"],
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
test.each(EXPECTED)(
|
|
344
|
+
"maps %s to its label",
|
|
345
|
+
(verdict: RightSizingVerdict, label: string) => {
|
|
346
|
+
expect(getVerdictLabel(verdict)).toBe(label);
|
|
347
|
+
},
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
test("every verdict produces a non-empty label", () => {
|
|
351
|
+
for (const verdict of Object.values(RightSizingVerdict)) {
|
|
352
|
+
const label: string = getVerdictLabel(verdict);
|
|
353
|
+
expect(typeof label).toBe("string");
|
|
354
|
+
expect(label.length).toBeGreaterThan(0);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test('only the Unavailable verdict resolves to "Unavailable"', () => {
|
|
359
|
+
/*
|
|
360
|
+
* If a new verdict fell through to the default arm, more than one enum
|
|
361
|
+
* member would carry the "Unavailable" label — catch that here.
|
|
362
|
+
*/
|
|
363
|
+
const unavailableCount: number = Object.values(RightSizingVerdict).filter(
|
|
364
|
+
(verdict: RightSizingVerdict) => {
|
|
365
|
+
return getVerdictLabel(verdict) === "Unavailable";
|
|
366
|
+
},
|
|
367
|
+
).length;
|
|
368
|
+
expect(unavailableCount).toBe(1);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
test("an unknown verdict falls back to Unavailable", () => {
|
|
372
|
+
expect(getVerdictLabel("SomethingElse" as RightSizingVerdict)).toBe(
|
|
373
|
+
"Unavailable",
|
|
374
|
+
);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import AggregationType from "../../../Types/BaseDatabase/AggregationType";
|
|
3
|
+
import MeasurementAggregationType, {
|
|
4
|
+
MeasurementAggregationTypeUtil,
|
|
5
|
+
} from "../../../Types/Measurement/MeasurementAggregationType";
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* MeasurementAggregationType is the deliberately-narrow subset of
|
|
9
|
+
* AggregationType a measurement chart is allowed to default to (Sum is left
|
|
10
|
+
* out on purpose: summing durations across incidents is meaningless). The Util
|
|
11
|
+
* maps each subset member onto the full AggregationType the query layer
|
|
12
|
+
* understands. The mapping is a switch with a default arm, so the failure it
|
|
13
|
+
* invites is a member added to the enum without a matching case — it would
|
|
14
|
+
* silently fall through to Avg. This suite pins the mapping and that
|
|
15
|
+
* exhaustiveness.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
describe("MeasurementAggregationType", () => {
|
|
19
|
+
test("is a strict subset of AggregationType and excludes Sum/Count", () => {
|
|
20
|
+
const full: Array<string> = Object.values(AggregationType);
|
|
21
|
+
for (const member of Object.values(MeasurementAggregationType)) {
|
|
22
|
+
// Every measurement member must be a real AggregationType value.
|
|
23
|
+
expect(full).toContain(member);
|
|
24
|
+
}
|
|
25
|
+
// The two intentionally-omitted members must never appear here.
|
|
26
|
+
expect(Object.values(MeasurementAggregationType)).not.toContain(
|
|
27
|
+
AggregationType.Sum as unknown as MeasurementAggregationType,
|
|
28
|
+
);
|
|
29
|
+
expect(Object.values(MeasurementAggregationType)).not.toContain(
|
|
30
|
+
AggregationType.Count as unknown as MeasurementAggregationType,
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("MeasurementAggregationTypeUtil.toAggregationType", () => {
|
|
36
|
+
const CASES: Array<[MeasurementAggregationType, AggregationType]> = [
|
|
37
|
+
[MeasurementAggregationType.Avg, AggregationType.Avg],
|
|
38
|
+
[MeasurementAggregationType.Max, AggregationType.Max],
|
|
39
|
+
[MeasurementAggregationType.Min, AggregationType.Min],
|
|
40
|
+
[MeasurementAggregationType.P50, AggregationType.P50],
|
|
41
|
+
[MeasurementAggregationType.P90, AggregationType.P90],
|
|
42
|
+
[MeasurementAggregationType.P95, AggregationType.P95],
|
|
43
|
+
[MeasurementAggregationType.P99, AggregationType.P99],
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
test.each(CASES)(
|
|
47
|
+
"maps %s to the matching AggregationType",
|
|
48
|
+
(input: MeasurementAggregationType, expected: AggregationType) => {
|
|
49
|
+
expect(MeasurementAggregationTypeUtil.toAggregationType(input)).toBe(
|
|
50
|
+
expected,
|
|
51
|
+
);
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
test("covers every enum member (no member falls through to the default)", () => {
|
|
56
|
+
/*
|
|
57
|
+
* If a new member is added to MeasurementAggregationType without its own
|
|
58
|
+
* case, it would resolve to Avg here. Compare against an independently
|
|
59
|
+
* built expectation so that fall-through is caught rather than accepted.
|
|
60
|
+
*/
|
|
61
|
+
const mapped: Set<AggregationType> = new Set<AggregationType>(
|
|
62
|
+
Object.values(MeasurementAggregationType).map(
|
|
63
|
+
(m: MeasurementAggregationType) => {
|
|
64
|
+
return MeasurementAggregationTypeUtil.toAggregationType(m);
|
|
65
|
+
},
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
// Avg, Max, Min, P50, P90, P95, P99 => 7 distinct targets.
|
|
69
|
+
expect(mapped.size).toBe(Object.values(MeasurementAggregationType).length);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("defaults undefined to Avg", () => {
|
|
73
|
+
// The picker can be empty; an unset selection must resolve to a safe Avg.
|
|
74
|
+
expect(MeasurementAggregationTypeUtil.toAggregationType(undefined)).toBe(
|
|
75
|
+
AggregationType.Avg,
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("an unknown value falls back to Avg rather than throwing", () => {
|
|
80
|
+
// Defensive: a value outside the enum (bad persisted data) must not crash.
|
|
81
|
+
expect(
|
|
82
|
+
MeasurementAggregationTypeUtil.toAggregationType(
|
|
83
|
+
"NotARealAggregation" as MeasurementAggregationType,
|
|
84
|
+
),
|
|
85
|
+
).toBe(AggregationType.Avg);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("only ever returns AggregationTypes valid for measurements", () => {
|
|
89
|
+
/*
|
|
90
|
+
* The mapping must never emit Sum or Count — those are exactly the
|
|
91
|
+
* aggregations the measurement subset exists to keep out.
|
|
92
|
+
*/
|
|
93
|
+
for (const member of Object.values(MeasurementAggregationType)) {
|
|
94
|
+
const result: AggregationType =
|
|
95
|
+
MeasurementAggregationTypeUtil.toAggregationType(member);
|
|
96
|
+
expect(result).not.toBe(AggregationType.Sum);
|
|
97
|
+
expect(result).not.toBe(AggregationType.Count);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_MONITOR_REQUEST_TIMEOUT_IN_MS,
|
|
3
|
+
DEFAULT_MONITOR_RETRY_COUNT,
|
|
3
4
|
MAX_MONITOR_REQUEST_TIMEOUT_IN_MS,
|
|
5
|
+
MAX_MONITOR_RETRY_COUNT,
|
|
4
6
|
clampMonitorRequestTimeoutInMs,
|
|
7
|
+
clampMonitorRetryCount,
|
|
5
8
|
} from "../../../Types/Monitor/MonitorStep";
|
|
6
9
|
import { describe, expect, test } from "@jest/globals";
|
|
7
10
|
|
|
@@ -163,3 +166,114 @@ describe("clampMonitorRequestTimeoutInMs", () => {
|
|
|
163
166
|
});
|
|
164
167
|
});
|
|
165
168
|
});
|
|
169
|
+
|
|
170
|
+
/*
|
|
171
|
+
* clampMonitorRetryCount constrains how many times a probe retries a step. Its
|
|
172
|
+
* guard differs from the timeout clamp in one behaviour that matters: the lower
|
|
173
|
+
* bound is `value < 0`, NOT `value <= 0`. Zero is therefore a VALID retry count
|
|
174
|
+
* (retry never) and must pass through unchanged, whereas only genuinely invalid
|
|
175
|
+
* inputs (undefined / null / NaN / negative) fall back to the default. These
|
|
176
|
+
* tests pin exactly that zero-is-valid distinction plus the usual boundary and
|
|
177
|
+
* defensive-coercion cases.
|
|
178
|
+
*/
|
|
179
|
+
describe("clampMonitorRetryCount", () => {
|
|
180
|
+
describe("invalid inputs fall back to the default", () => {
|
|
181
|
+
test("NaN returns the default", () => {
|
|
182
|
+
expect(clampMonitorRetryCount(NaN)).toBe(DEFAULT_MONITOR_RETRY_COUNT);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("a negative value returns the default (not a clamp to zero)", () => {
|
|
186
|
+
expect(clampMonitorRetryCount(-1)).toBe(DEFAULT_MONITOR_RETRY_COUNT);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("negative Infinity returns the default", () => {
|
|
190
|
+
expect(clampMonitorRetryCount(-Infinity)).toBe(
|
|
191
|
+
DEFAULT_MONITOR_RETRY_COUNT,
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("undefined coerced through the signature returns the default", () => {
|
|
196
|
+
expect(clampMonitorRetryCount(undefined as unknown as number)).toBe(
|
|
197
|
+
DEFAULT_MONITOR_RETRY_COUNT,
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("null coerced through the signature returns the default", () => {
|
|
202
|
+
expect(clampMonitorRetryCount(null as unknown as number)).toBe(
|
|
203
|
+
DEFAULT_MONITOR_RETRY_COUNT,
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe("zero is a valid retry count", () => {
|
|
209
|
+
test("zero passes through unchanged (retry never)", () => {
|
|
210
|
+
// The whole point of `< 0` rather than `<= 0`: 0 is a real choice.
|
|
211
|
+
expect(clampMonitorRetryCount(0)).toBe(0);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("negative zero also passes through as a numeric zero", () => {
|
|
215
|
+
/*
|
|
216
|
+
* -0 < 0 is false, so -0 is not treated as invalid and passes through.
|
|
217
|
+
* It stays -0 (toBe uses Object.is, under which -0 !== 0), which is still
|
|
218
|
+
* numerically zero — assert that rather than the Object.is identity.
|
|
219
|
+
*/
|
|
220
|
+
const result: number = clampMonitorRetryCount(-0);
|
|
221
|
+
expect(result === 0).toBe(true);
|
|
222
|
+
expect(Math.abs(result)).toBe(0);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe("in-range and boundary values", () => {
|
|
227
|
+
test("a value between 0 and the cap is returned unchanged", () => {
|
|
228
|
+
expect(clampMonitorRetryCount(1)).toBe(1);
|
|
229
|
+
expect(clampMonitorRetryCount(2)).toBe(2);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("a value exactly at the cap is returned, not clamped", () => {
|
|
233
|
+
expect(clampMonitorRetryCount(MAX_MONITOR_RETRY_COUNT)).toBe(
|
|
234
|
+
MAX_MONITOR_RETRY_COUNT,
|
|
235
|
+
);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("one over the cap is clamped down to the cap", () => {
|
|
239
|
+
expect(clampMonitorRetryCount(MAX_MONITOR_RETRY_COUNT + 1)).toBe(
|
|
240
|
+
MAX_MONITOR_RETRY_COUNT,
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("positive Infinity is clamped to the cap", () => {
|
|
245
|
+
expect(clampMonitorRetryCount(Infinity)).toBe(MAX_MONITOR_RETRY_COUNT);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe("stability and invariants", () => {
|
|
250
|
+
test("the exported cap and default are non-negative finite numbers", () => {
|
|
251
|
+
expect(Number.isFinite(MAX_MONITOR_RETRY_COUNT)).toBe(true);
|
|
252
|
+
expect(MAX_MONITOR_RETRY_COUNT).toBeGreaterThanOrEqual(0);
|
|
253
|
+
expect(DEFAULT_MONITOR_RETRY_COUNT).toBeGreaterThanOrEqual(0);
|
|
254
|
+
expect(DEFAULT_MONITOR_RETRY_COUNT).toBeLessThanOrEqual(
|
|
255
|
+
MAX_MONITOR_RETRY_COUNT,
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("across a sweep the result is always within [0, cap]", () => {
|
|
260
|
+
const samples: Array<number> = [
|
|
261
|
+
-100,
|
|
262
|
+
-1,
|
|
263
|
+
0,
|
|
264
|
+
1,
|
|
265
|
+
2,
|
|
266
|
+
3,
|
|
267
|
+
4,
|
|
268
|
+
1000,
|
|
269
|
+
Infinity,
|
|
270
|
+
NaN,
|
|
271
|
+
];
|
|
272
|
+
for (const sample of samples) {
|
|
273
|
+
const result: number = clampMonitorRetryCount(sample);
|
|
274
|
+
expect(result).toBeGreaterThanOrEqual(0);
|
|
275
|
+
expect(result).toBeLessThanOrEqual(MAX_MONITOR_RETRY_COUNT);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import WorkspaceType, {
|
|
3
|
+
getWorkspaceTypeDisplayName,
|
|
4
|
+
} from "../../../Types/Workspace/WorkspaceType";
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* getWorkspaceTypeDisplayName turns the WorkspaceType enum into the label shown
|
|
8
|
+
* wherever a workspace integration is named in the UI. The only member whose
|
|
9
|
+
* display name differs from its enum value is MicrosoftTeams ("Microsoft Teams"
|
|
10
|
+
* with a space); the rest fall through to returning the raw value. The suite
|
|
11
|
+
* pins that spacing and guards the fall-through: a new workspace type added to
|
|
12
|
+
* the enum must still yield a non-empty label rather than undefined.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
describe("getWorkspaceTypeDisplayName", () => {
|
|
16
|
+
test("renders Microsoft Teams with a space", () => {
|
|
17
|
+
// The enum value is "MicrosoftTeams"; the label must be human-spaced.
|
|
18
|
+
expect(getWorkspaceTypeDisplayName(WorkspaceType.MicrosoftTeams)).toBe(
|
|
19
|
+
"Microsoft Teams",
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("renders Slack as-is", () => {
|
|
24
|
+
expect(getWorkspaceTypeDisplayName(WorkspaceType.Slack)).toBe("Slack");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("every workspace type yields a non-empty display name", () => {
|
|
28
|
+
for (const type of Object.values(WorkspaceType)) {
|
|
29
|
+
const label: string = getWorkspaceTypeDisplayName(type);
|
|
30
|
+
expect(typeof label).toBe("string");
|
|
31
|
+
expect(label.length).toBeGreaterThan(0);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("display names are unique across workspace types", () => {
|
|
36
|
+
const labels: Array<string> = Object.values(WorkspaceType).map(
|
|
37
|
+
(type: WorkspaceType) => {
|
|
38
|
+
return getWorkspaceTypeDisplayName(type);
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
expect(new Set<string>(labels).size).toBe(labels.length);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("an unknown value falls back to the raw string it was given", () => {
|
|
45
|
+
/*
|
|
46
|
+
* The function's default arm returns its argument unchanged. Persisted or
|
|
47
|
+
* mistyped data must therefore round-trip rather than become undefined.
|
|
48
|
+
*/
|
|
49
|
+
expect(getWorkspaceTypeDisplayName("Discord" as WorkspaceType)).toBe(
|
|
50
|
+
"Discord",
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -7,8 +7,10 @@ import NotificationMethodUtil, {
|
|
|
7
7
|
import { DropdownOption } from "../../../UI/Components/Dropdown/Dropdown";
|
|
8
8
|
import UserCall from "../../../Models/DatabaseModels/UserCall";
|
|
9
9
|
import UserEmail from "../../../Models/DatabaseModels/UserEmail";
|
|
10
|
+
import UserMicrosoftTeams from "../../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
10
11
|
import UserNotificationRule from "../../../Models/DatabaseModels/UserNotificationRule";
|
|
11
12
|
import UserPush from "../../../Models/DatabaseModels/UserPush";
|
|
13
|
+
import UserSlack from "../../../Models/DatabaseModels/UserSlack";
|
|
12
14
|
import UserSMS from "../../../Models/DatabaseModels/UserSMS";
|
|
13
15
|
import UserTelegram from "../../../Models/DatabaseModels/UserTelegram";
|
|
14
16
|
import UserWebhook from "../../../Models/DatabaseModels/UserWebhook";
|
|
@@ -95,6 +97,34 @@ function push(id: string, deviceName?: string): UserPush {
|
|
|
95
97
|
return model;
|
|
96
98
|
}
|
|
97
99
|
|
|
100
|
+
function slack(id: string, userName?: string, userId?: string): UserSlack {
|
|
101
|
+
const model: UserSlack = new UserSlack();
|
|
102
|
+
model.id = new ObjectID(id);
|
|
103
|
+
if (userName !== undefined) {
|
|
104
|
+
model.slackUserName = userName;
|
|
105
|
+
}
|
|
106
|
+
if (userId !== undefined) {
|
|
107
|
+
model.slackUserId = userId;
|
|
108
|
+
}
|
|
109
|
+
return model;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function microsoftTeams(
|
|
113
|
+
id: string,
|
|
114
|
+
userName?: string,
|
|
115
|
+
userId?: string,
|
|
116
|
+
): UserMicrosoftTeams {
|
|
117
|
+
const model: UserMicrosoftTeams = new UserMicrosoftTeams();
|
|
118
|
+
model.id = new ObjectID(id);
|
|
119
|
+
if (userName !== undefined) {
|
|
120
|
+
model.microsoftTeamsUserName = userName;
|
|
121
|
+
}
|
|
122
|
+
if (userId !== undefined) {
|
|
123
|
+
model.microsoftTeamsUserId = userId;
|
|
124
|
+
}
|
|
125
|
+
return model;
|
|
126
|
+
}
|
|
127
|
+
|
|
98
128
|
function emptyModels(): NotificationMethodModels {
|
|
99
129
|
return {
|
|
100
130
|
userCalls: [],
|
|
@@ -103,20 +133,25 @@ function emptyModels(): NotificationMethodModels {
|
|
|
103
133
|
userPush: [],
|
|
104
134
|
userWhatsApps: [],
|
|
105
135
|
userTelegrams: [],
|
|
136
|
+
userSlacks: [],
|
|
137
|
+
userMicrosoftTeamsAccounts: [],
|
|
106
138
|
userWebhooks: [],
|
|
107
139
|
};
|
|
108
140
|
}
|
|
109
141
|
|
|
110
142
|
// A 24-hex-char string is a valid ObjectID.
|
|
111
|
-
const ID: Record<"a" | "b" | "c" | "d" | "e" | "f" | "g", string> =
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
143
|
+
const ID: Record<"a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i", string> =
|
|
144
|
+
{
|
|
145
|
+
a: "aaaaaaaaaaaaaaaaaaaaaaaa",
|
|
146
|
+
b: "bbbbbbbbbbbbbbbbbbbbbbbb",
|
|
147
|
+
c: "cccccccccccccccccccccccc",
|
|
148
|
+
d: "dddddddddddddddddddddddd",
|
|
149
|
+
e: "eeeeeeeeeeeeeeeeeeeeeeee",
|
|
150
|
+
f: "ffffffffffffffffffffffff",
|
|
151
|
+
g: "abcabcabcabcabcabcabcabc",
|
|
152
|
+
h: "cdecdecdecdecdecdecdecde",
|
|
153
|
+
i: "fabfabfabfabfabfabfabfab",
|
|
154
|
+
};
|
|
120
155
|
|
|
121
156
|
/*
|
|
122
157
|
* A minimal stand-in for a model row: getDisplayItems only needs getColumnValue,
|
|
@@ -144,7 +179,9 @@ describe("NotificationMethodUtil.getSelectForNotificationMethods", () => {
|
|
|
144
179
|
[
|
|
145
180
|
"userCall",
|
|
146
181
|
"userEmail",
|
|
182
|
+
"userMicrosoftTeams",
|
|
147
183
|
"userPush",
|
|
184
|
+
"userSlack",
|
|
148
185
|
"userSms",
|
|
149
186
|
"userTelegram",
|
|
150
187
|
"userWebhook",
|
|
@@ -171,6 +208,14 @@ describe("NotificationMethodUtil.getSelectForNotificationMethods", () => {
|
|
|
171
208
|
telegramUserHandle: true,
|
|
172
209
|
telegramChatId: true,
|
|
173
210
|
});
|
|
211
|
+
expect(select["userSlack"]).toEqual({
|
|
212
|
+
slackUserName: true,
|
|
213
|
+
slackUserId: true,
|
|
214
|
+
});
|
|
215
|
+
expect(select["userMicrosoftTeams"]).toEqual({
|
|
216
|
+
microsoftTeamsUserName: true,
|
|
217
|
+
microsoftTeamsUserId: true,
|
|
218
|
+
});
|
|
174
219
|
});
|
|
175
220
|
|
|
176
221
|
test("selects ONLY name for a webhook — never the credential-bearing URL or secret", () => {
|
|
@@ -284,6 +329,51 @@ describe("NotificationMethodUtil.getDisplayItems", () => {
|
|
|
284
329
|
expect(items).toEqual([{ title: "Telegram", value: "999" }]);
|
|
285
330
|
});
|
|
286
331
|
|
|
332
|
+
test("prefers the Slack user name over the Slack member id", () => {
|
|
333
|
+
const items: Array<NotificationMethodDisplayItem> =
|
|
334
|
+
NotificationMethodUtil.getDisplayItems(
|
|
335
|
+
reader({
|
|
336
|
+
userSlack: { slackUserName: "alice", slackUserId: "U0123ABCD" },
|
|
337
|
+
}),
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
expect(items).toEqual([{ title: "Slack", value: "alice" }]);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test("falls back to the Slack member id when the user name is absent", () => {
|
|
344
|
+
const items: Array<NotificationMethodDisplayItem> =
|
|
345
|
+
NotificationMethodUtil.getDisplayItems(
|
|
346
|
+
reader({ userSlack: { slackUserId: "U0123ABCD" } }),
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
expect(items).toEqual([{ title: "Slack", value: "U0123ABCD" }]);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test("prefers the Microsoft Teams user name over the Teams user id", () => {
|
|
353
|
+
const items: Array<NotificationMethodDisplayItem> =
|
|
354
|
+
NotificationMethodUtil.getDisplayItems(
|
|
355
|
+
reader({
|
|
356
|
+
userMicrosoftTeams: {
|
|
357
|
+
microsoftTeamsUserName: "Alice Example",
|
|
358
|
+
microsoftTeamsUserId: "29:1abc",
|
|
359
|
+
},
|
|
360
|
+
}),
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
expect(items).toEqual([
|
|
364
|
+
{ title: "Microsoft Teams", value: "Alice Example" },
|
|
365
|
+
]);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test("falls back to the Teams user id when the user name is absent", () => {
|
|
369
|
+
const items: Array<NotificationMethodDisplayItem> =
|
|
370
|
+
NotificationMethodUtil.getDisplayItems(
|
|
371
|
+
reader({ userMicrosoftTeams: { microsoftTeamsUserId: "29:1abc" } }),
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
expect(items).toEqual([{ title: "Microsoft Teams", value: "29:1abc" }]);
|
|
375
|
+
});
|
|
376
|
+
|
|
287
377
|
test("ignores a relation whose value is not an object", () => {
|
|
288
378
|
expect(
|
|
289
379
|
NotificationMethodUtil.getDisplayItems(
|
|
@@ -306,12 +396,14 @@ describe("NotificationMethodUtil.getDisplayItems", () => {
|
|
|
306
396
|
reader({
|
|
307
397
|
userPush: { deviceName: "Pixel" },
|
|
308
398
|
userEmail: { email: "jane@example.com" },
|
|
399
|
+
userSlack: { slackUserName: "alice" },
|
|
309
400
|
userWebhook: { name: "Hook" },
|
|
310
401
|
}),
|
|
311
402
|
);
|
|
312
403
|
|
|
313
404
|
expect(items).toEqual([
|
|
314
405
|
{ title: "Email", value: "jane@example.com" },
|
|
406
|
+
{ title: "Slack", value: "alice" },
|
|
315
407
|
{ title: "Webhook", value: "Hook" },
|
|
316
408
|
{ title: "Push", value: "Pixel" },
|
|
317
409
|
]);
|
|
@@ -370,6 +462,32 @@ describe("NotificationMethodUtil.getLabel", () => {
|
|
|
370
462
|
);
|
|
371
463
|
});
|
|
372
464
|
|
|
465
|
+
test("labels a Slack method by user name, then member id, then Unknown Account", () => {
|
|
466
|
+
expect(NotificationMethodUtil.getLabel(slack(ID.a, "alice"))).toBe(
|
|
467
|
+
"Slack: alice",
|
|
468
|
+
);
|
|
469
|
+
expect(
|
|
470
|
+
NotificationMethodUtil.getLabel(slack(ID.b, undefined, "U0123ABCD")),
|
|
471
|
+
).toBe("Slack: U0123ABCD");
|
|
472
|
+
expect(NotificationMethodUtil.getLabel(slack(ID.c))).toBe(
|
|
473
|
+
"Slack: Unknown Account",
|
|
474
|
+
);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
test("labels a Teams method by user name, then user id, then Unknown Account", () => {
|
|
478
|
+
expect(
|
|
479
|
+
NotificationMethodUtil.getLabel(microsoftTeams(ID.a, "Alice Example")),
|
|
480
|
+
).toBe("Microsoft Teams: Alice Example");
|
|
481
|
+
expect(
|
|
482
|
+
NotificationMethodUtil.getLabel(
|
|
483
|
+
microsoftTeams(ID.b, undefined, "29:1abc"),
|
|
484
|
+
),
|
|
485
|
+
).toBe("Microsoft Teams: 29:1abc");
|
|
486
|
+
expect(NotificationMethodUtil.getLabel(microsoftTeams(ID.c))).toBe(
|
|
487
|
+
"Microsoft Teams: Unknown Account",
|
|
488
|
+
);
|
|
489
|
+
});
|
|
490
|
+
|
|
373
491
|
test("labels call, sms, whatsapp with the phone number and their prefix", () => {
|
|
374
492
|
expect(NotificationMethodUtil.getLabel(call(ID.a, "+15555550100"))).toBe(
|
|
375
493
|
"Call: +15555550100",
|
|
@@ -418,6 +536,8 @@ describe("NotificationMethodUtil.getDropdownOptions", () => {
|
|
|
418
536
|
userPush: [push(ID.d, "Pixel")],
|
|
419
537
|
userWhatsApps: [whatsApp(ID.e, "+15555550102")],
|
|
420
538
|
userTelegrams: [telegram(ID.f, "@jane")],
|
|
539
|
+
userSlacks: [slack(ID.h, "alice")],
|
|
540
|
+
userMicrosoftTeamsAccounts: [microsoftTeams(ID.i, "Alice Example")],
|
|
421
541
|
userWebhooks: [webhook(ID.g, "Hook")],
|
|
422
542
|
};
|
|
423
543
|
|
|
@@ -434,6 +554,8 @@ describe("NotificationMethodUtil.getDropdownOptions", () => {
|
|
|
434
554
|
"Push: Pixel",
|
|
435
555
|
"WhatsApp: +15555550102",
|
|
436
556
|
"Telegram: @jane",
|
|
557
|
+
"Slack: alice",
|
|
558
|
+
"Microsoft Teams: Alice Example",
|
|
437
559
|
"Webhook: Hook",
|
|
438
560
|
]);
|
|
439
561
|
});
|
|
@@ -465,6 +587,8 @@ describe("NotificationMethodUtil.setSelectedMethodOnRule", () => {
|
|
|
465
587
|
userPush: [push(ID.d, "Pixel")],
|
|
466
588
|
userWhatsApps: [whatsApp(ID.e, "+15555550102")],
|
|
467
589
|
userTelegrams: [telegram(ID.f, "@jane")],
|
|
590
|
+
userSlacks: [slack(ID.h, "alice")],
|
|
591
|
+
userMicrosoftTeamsAccounts: [microsoftTeams(ID.i, "Alice Example")],
|
|
468
592
|
userWebhooks: [webhook(ID.g, "Hook")],
|
|
469
593
|
};
|
|
470
594
|
}
|
|
@@ -489,6 +613,8 @@ describe("NotificationMethodUtil.setSelectedMethodOnRule", () => {
|
|
|
489
613
|
expect(rule.userPushId).toBeUndefined();
|
|
490
614
|
expect(rule.userWhatsAppId).toBeUndefined();
|
|
491
615
|
expect(rule.userTelegramId).toBeUndefined();
|
|
616
|
+
expect(rule.userSlackId).toBeUndefined();
|
|
617
|
+
expect(rule.userMicrosoftTeamsId).toBeUndefined();
|
|
492
618
|
});
|
|
493
619
|
|
|
494
620
|
test("maps each method type to its own foreign key", () => {
|
|
@@ -499,6 +625,8 @@ describe("NotificationMethodUtil.setSelectedMethodOnRule", () => {
|
|
|
499
625
|
{ id: ID.d, key: "userPushId" },
|
|
500
626
|
{ id: ID.e, key: "userWhatsAppId" },
|
|
501
627
|
{ id: ID.f, key: "userTelegramId" },
|
|
628
|
+
{ id: ID.h, key: "userSlackId" },
|
|
629
|
+
{ id: ID.i, key: "userMicrosoftTeamsId" },
|
|
502
630
|
{ id: ID.g, key: "userWebhookId" },
|
|
503
631
|
];
|
|
504
632
|
|