@oneuptime/common 11.7.1 → 11.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/DashboardDomain.ts +1 -0
- package/Models/DatabaseModels/Incident.ts +2 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
- package/Models/DatabaseModels/Monitor.ts +1 -0
- package/Models/DatabaseModels/MonitorStatusTimeline.ts +1 -0
- package/Models/DatabaseModels/NetworkSite.ts +1 -0
- package/Models/DatabaseModels/NetworkSiteStatusTimeline.ts +1 -0
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +1 -0
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +2 -0
- package/Models/DatabaseModels/Probe.ts +13 -13
- package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -0
- package/Models/DatabaseModels/StatusPage.ts +1 -0
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
- package/Models/DatabaseModels/StatusPageDomain.ts +1 -0
- package/Models/DatabaseModels/WorkspaceNotificationSummary.ts +1 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +11 -0
- package/Server/API/BaseAPI.ts +17 -1
- package/Server/API/CommonAPI.ts +32 -0
- package/Server/API/MicrosoftTeamsAPI.ts +99 -2
- package/Server/API/ProbeAPI.ts +2 -0
- package/Server/API/SlackAPI.ts +62 -0
- package/Server/API/StatusPageAPI.ts +157 -192
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.ts +61 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.ts +280 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/DatabaseService.ts +11 -3
- package/Server/Services/MonitorGroupService.ts +145 -0
- package/Server/Services/MonitorService.ts +231 -30
- package/Server/Services/ProbeService.ts +319 -16
- package/Server/Services/StatusPageService.ts +72 -42
- package/Server/Services/WorkspaceNotificationRuleService.ts +232 -2
- package/Server/Utils/Database/PostgresErrorTranslator.ts +131 -0
- package/Server/Utils/Monitor/MonitorResource.ts +88 -35
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +21 -1
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +158 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +706 -6
- package/Tests/Models/DatabaseModels/ProbeColumnAccessControl.test.ts +103 -0
- package/Tests/Server/API/BaseAPIUpdateNoOp.test.ts +135 -0
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +317 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +228 -0
- package/Tests/Server/API/ProbeAPI.test.ts +6 -0
- package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +167 -0
- package/Tests/Server/Services/AddHotQueryIndexesMigration.test.ts +255 -0
- package/Tests/Server/Services/AddHotQueryIndexesSecondPassMigration.test.ts +258 -0
- package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +1 -1
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +121 -0
- package/Tests/Server/Services/HeartbeatWriteFastPathSafety.test.ts +200 -0
- package/Tests/Server/Services/HookFreeWriteSafetySecondPass.test.ts +181 -0
- package/Tests/Server/Services/MonitorGroupServiceBatchedStatuses.test.ts +513 -0
- package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +492 -0
- package/Tests/Server/Services/MonitorServiceProbeSelection.test.ts +401 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +453 -0
- package/Tests/Server/Services/ProbeAuthKeyCacheAndHeartbeat.test.ts +679 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +3 -3
- package/Tests/Server/Services/StatusPageDomainResolutionCache.test.ts +245 -0
- package/Tests/Server/Services/StatusPageOverviewTimelineWindow.test.ts +283 -0
- package/Tests/Server/Services/StatusPageServiceMcp.test.ts +17 -1
- package/Tests/Server/Services/StatusPageServiceMonitorGroupBatching.test.ts +331 -0
- package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +12 -12
- package/Tests/Server/Services/WorkspaceNotificationRuleChats.test.ts +1390 -0
- package/Tests/Server/Utils/AI/Insights/InsightFixRouting.test.ts +4 -4
- package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +1 -3
- package/Tests/Server/Utils/AI/Insights/InsightStoreHardening.test.ts +1 -3
- package/Tests/Server/Utils/AI/Insights/InsightTriage.test.ts +1 -1
- package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +5 -5
- package/Tests/Server/Utils/Alert/AlertPrivacyFilter.test.ts +187 -0
- package/Tests/Server/Utils/AlertEpisode/AlertEpisodePrivacyFilter.test.ts +170 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +125 -0
- package/Tests/Server/Utils/Incident/IncidentPrivacyFilter.test.ts +264 -0
- package/Tests/Server/Utils/IncidentEpisode/IncidentEpisodePrivacyFilter.test.ts +175 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +181 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +2 -2
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +341 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +2175 -0
- package/Tests/Server/Utils/Workspace/SlackChannelsList.test.ts +585 -0
- package/Tests/Types/Billing/MeteredPlan.test.ts +46 -0
- package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +219 -1
- package/Tests/UI/Components/BulkUpdateForm.test.tsx +174 -0
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +315 -0
- package/Tests/UI/Components/ModelDetailSelect.test.tsx +229 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1258 -0
- package/Tests/UI/Components/ModelTableExportFromColumns.test.ts +153 -0
- package/Tests/UI/Components/MonacoLoader.test.ts +67 -0
- package/Tests/UI/Components/MonacoRuntime.test.ts +121 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +212 -3
- package/Tests/UI/Utils/Project.test.ts +809 -0
- package/Tests/UI/Utils/TableColumnsToCsv.test.ts +360 -0
- package/Tests/Utils/Alerts/AlertMetricType.test.ts +143 -0
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +219 -0
- package/Tests/Utils/Incident/IncidentMetricType.test.ts +174 -0
- package/Tests/Utils/Monitor/MonitorProbeSelectionUtil.test.ts +145 -0
- package/Tests/Utils/StatusPage/ResourceUptime.test.ts +263 -0
- package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +4 -0
- package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +25 -10
- package/Types/Workspace/WorkspaceMessagePayload.ts +1 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +63 -8
- package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
- package/UI/Components/CodeEditor/MonacoLoader.ts +36 -0
- package/UI/Components/Forms/ModelForm.tsx +41 -18
- package/UI/Components/ModelDetail/CardModelDetail.tsx +23 -1
- package/UI/Components/ModelDetail/ModelDetail.tsx +46 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +300 -30
- package/UI/Components/ModelTable/Column.ts +12 -0
- package/UI/Components/ModelTable/ExportFromColumns.ts +57 -0
- package/UI/Components/Navbar/NavBar.tsx +66 -1
- package/UI/Components/Table/Table.tsx +52 -13
- package/UI/Components/Table/Types/Column.ts +18 -0
- package/UI/Utils/Project.ts +214 -18
- package/UI/Utils/TableColumnsToCsv.ts +86 -6
- package/UI/esbuild-config.js +34 -0
- package/Utils/Monitor/MonitorProbeSelectionUtil.ts +72 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js +1 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +2 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +1 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSite.js +1 -0
- package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +2 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Probe.js +13 -13
- package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js +1 -0
- package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +12 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +21 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +72 -2
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProbeAPI.js +2 -0
- package/build/dist/Server/API/ProbeAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +44 -0
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +98 -152
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js +254 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +10 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/MonitorGroupService.js +118 -0
- package/build/dist/Server/Services/MonitorGroupService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +198 -24
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +238 -9
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +46 -39
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +178 -3
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +80 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +67 -29
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +31 -14
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +117 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +549 -7
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +17 -7
- package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +37 -11
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +2 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +34 -0
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +1 -0
- package/build/dist/UI/Components/Forms/ModelForm.js +37 -17
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +22 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js +31 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +234 -21
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ExportFromColumns.js +30 -0
- package/build/dist/UI/Components/ModelTable/ExportFromColumns.js.map +1 -0
- package/build/dist/UI/Components/Navbar/NavBar.js +39 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +31 -11
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Utils/Project.js +161 -15
- package/build/dist/UI/Utils/Project.js.map +1 -1
- package/build/dist/UI/Utils/TableColumnsToCsv.js +67 -6
- package/build/dist/UI/Utils/TableColumnsToCsv.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js +52 -0
- package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,1390 @@
|
|
|
1
|
+
import WorkspaceNotificationRuleService, {
|
|
2
|
+
MessageBlocksByWorkspaceType,
|
|
3
|
+
NotificationFor,
|
|
4
|
+
} from "../../../Server/Services/WorkspaceNotificationRuleService";
|
|
5
|
+
import WorkspaceProjectAuthTokenService from "../../../Server/Services/WorkspaceProjectAuthTokenService";
|
|
6
|
+
import WorkspaceUserAuthTokenService from "../../../Server/Services/WorkspaceUserAuthTokenService";
|
|
7
|
+
import WorkspaceNotificationLogService from "../../../Server/Services/WorkspaceNotificationLogService";
|
|
8
|
+
import WorkspaceUtil from "../../../Server/Utils/Workspace/Workspace";
|
|
9
|
+
import {
|
|
10
|
+
WorkspaceChannel,
|
|
11
|
+
WorkspaceSendMessageResponse,
|
|
12
|
+
} from "../../../Server/Utils/Workspace/WorkspaceBase";
|
|
13
|
+
import WorkspaceNotificationRule from "../../../Models/DatabaseModels/WorkspaceNotificationRule";
|
|
14
|
+
import WorkspaceProjectAuthToken, {
|
|
15
|
+
MicrosoftTeamsChat,
|
|
16
|
+
MicrosoftTeamsMiscData,
|
|
17
|
+
} from "../../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
|
18
|
+
import WorkspaceUserAuthToken from "../../../Models/DatabaseModels/WorkspaceUserAuthToken";
|
|
19
|
+
import WorkspaceNotificationLog from "../../../Models/DatabaseModels/WorkspaceNotificationLog";
|
|
20
|
+
import BaseNotificationRule from "../../../Types/Workspace/NotificationRules/BaseNotificationRule";
|
|
21
|
+
import NotificationRuleEventType from "../../../Types/Workspace/NotificationRules/EventType";
|
|
22
|
+
import FilterCondition from "../../../Types/Filter/FilterCondition";
|
|
23
|
+
import WorkspaceType from "../../../Types/Workspace/WorkspaceType";
|
|
24
|
+
import WorkspaceNotificationStatus from "../../../Types/Workspace/WorkspaceNotificationStatus";
|
|
25
|
+
import WorkspaceNotificationActionType from "../../../Types/Workspace/WorkspaceNotificationActionType";
|
|
26
|
+
import WorkspaceMessagePayload, {
|
|
27
|
+
WorkspacePayloadMarkdown,
|
|
28
|
+
} from "../../../Types/Workspace/WorkspaceMessagePayload";
|
|
29
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
30
|
+
import { describe, expect, test, beforeEach, afterEach } from "@jest/globals";
|
|
31
|
+
|
|
32
|
+
/*
|
|
33
|
+
* Tests for the Microsoft Teams CHAT (group chat / personal 1:1 chat) support
|
|
34
|
+
* in WorkspaceNotificationRuleService:
|
|
35
|
+
*
|
|
36
|
+
* - getExistingChatIdsFromNotificationRules: pure extractor for chat ids from
|
|
37
|
+
* notification rules (toggle + dedupe + empty-id filtering).
|
|
38
|
+
* - getExistingChatIdsBasedOnEventType: Teams-only guard + rule matching.
|
|
39
|
+
* - getConnectedMicrosoftTeamsChats: reads miscData.availableChats off the
|
|
40
|
+
* project auth token.
|
|
41
|
+
* - sendWorkspaceMarkdownNotification: builds one payload per chat id with
|
|
42
|
+
* channelNames/channelIds empty and chatIds=[id] — Teams only.
|
|
43
|
+
* - testRule: the chat block at the end (validation, payload, logging and
|
|
44
|
+
* error wrapping).
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
type BaseRuleOverrides = Partial<BaseNotificationRule> & {
|
|
48
|
+
shouldCreateNewChannel?: boolean;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function makeBaseRule(overrides: BaseRuleOverrides): BaseNotificationRule {
|
|
52
|
+
return {
|
|
53
|
+
_type: "NotificationRule",
|
|
54
|
+
filterCondition: FilterCondition.Any,
|
|
55
|
+
filters: [],
|
|
56
|
+
shouldPostToExistingChannel: false,
|
|
57
|
+
existingChannelNames: "",
|
|
58
|
+
shouldCreateNewChannel: false,
|
|
59
|
+
...overrides,
|
|
60
|
+
} as BaseNotificationRule;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function makeWorkspaceRule(data: {
|
|
64
|
+
workspaceType: WorkspaceType;
|
|
65
|
+
notificationRule: BaseNotificationRule;
|
|
66
|
+
name?: string | undefined;
|
|
67
|
+
}): WorkspaceNotificationRule {
|
|
68
|
+
const rule: WorkspaceNotificationRule = new WorkspaceNotificationRule();
|
|
69
|
+
rule.id = ObjectID.generate();
|
|
70
|
+
rule.projectId = ObjectID.generate();
|
|
71
|
+
rule.workspaceType = data.workspaceType;
|
|
72
|
+
rule.eventType = NotificationRuleEventType.Incident;
|
|
73
|
+
rule.name = data.name || "Chat Rule";
|
|
74
|
+
rule.notificationRule = data.notificationRule;
|
|
75
|
+
return rule;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function makeChat(id: string): MicrosoftTeamsChat {
|
|
79
|
+
return {
|
|
80
|
+
id: id,
|
|
81
|
+
name: `Chat ${id}`,
|
|
82
|
+
chatType: "groupChat",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function makeTeamsMiscData(
|
|
87
|
+
availableChats: Record<string, MicrosoftTeamsChat> | undefined,
|
|
88
|
+
): MicrosoftTeamsMiscData {
|
|
89
|
+
const miscData: MicrosoftTeamsMiscData = {
|
|
90
|
+
tenantId: "tenant-1",
|
|
91
|
+
teamId: "team-1",
|
|
92
|
+
teamName: "Team One",
|
|
93
|
+
botId: "bot-1",
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (availableChats) {
|
|
97
|
+
miscData.availableChats = availableChats;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return miscData;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function makeChannel(data: {
|
|
104
|
+
id: string;
|
|
105
|
+
name: string;
|
|
106
|
+
workspaceType: WorkspaceType;
|
|
107
|
+
teamId?: string | undefined;
|
|
108
|
+
}): WorkspaceChannel {
|
|
109
|
+
const channel: WorkspaceChannel = {
|
|
110
|
+
id: data.id,
|
|
111
|
+
name: data.name,
|
|
112
|
+
workspaceType: data.workspaceType,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
if (data.teamId) {
|
|
116
|
+
channel.teamId = data.teamId;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return channel;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function makeSendResponse(data: {
|
|
123
|
+
workspaceType: WorkspaceType;
|
|
124
|
+
threads: Array<{ channelId: string; channelName: string; threadId: string }>;
|
|
125
|
+
errors?:
|
|
126
|
+
| Array<{ channelId: string; channelName: string; error: string }>
|
|
127
|
+
| undefined;
|
|
128
|
+
}): WorkspaceSendMessageResponse {
|
|
129
|
+
const response: WorkspaceSendMessageResponse = {
|
|
130
|
+
workspaceType: data.workspaceType,
|
|
131
|
+
threads: data.threads.map(
|
|
132
|
+
(thread: {
|
|
133
|
+
channelId: string;
|
|
134
|
+
channelName: string;
|
|
135
|
+
threadId: string;
|
|
136
|
+
}) => {
|
|
137
|
+
return {
|
|
138
|
+
channel: makeChannel({
|
|
139
|
+
id: thread.channelId,
|
|
140
|
+
name: thread.channelName,
|
|
141
|
+
workspaceType: data.workspaceType,
|
|
142
|
+
}),
|
|
143
|
+
threadId: thread.threadId,
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
),
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
if (data.errors) {
|
|
150
|
+
response.errors = data.errors.map(
|
|
151
|
+
(error: { channelId: string; channelName: string; error: string }) => {
|
|
152
|
+
return {
|
|
153
|
+
channel: makeChannel({
|
|
154
|
+
id: error.channelId,
|
|
155
|
+
name: error.channelName,
|
|
156
|
+
workspaceType: data.workspaceType,
|
|
157
|
+
}),
|
|
158
|
+
error: error.error,
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return response;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function markdownBlock(text: string): WorkspacePayloadMarkdown {
|
|
168
|
+
return {
|
|
169
|
+
_type: "WorkspacePayloadMarkdown",
|
|
170
|
+
text: text,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
afterEach(() => {
|
|
175
|
+
jest.restoreAllMocks();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe("WorkspaceNotificationRuleService.getExistingChatIdsFromNotificationRules", () => {
|
|
179
|
+
test("collects chat ids from a rule with the chat toggle on", () => {
|
|
180
|
+
const result: Array<string> =
|
|
181
|
+
WorkspaceNotificationRuleService.getExistingChatIdsFromNotificationRules({
|
|
182
|
+
notificationRules: [
|
|
183
|
+
makeBaseRule({
|
|
184
|
+
shouldPostToExistingChat: true,
|
|
185
|
+
existingChatIds: ["chat-1", "chat-2"],
|
|
186
|
+
}),
|
|
187
|
+
],
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(result).toEqual(["chat-1", "chat-2"]);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("skips rules where the chat toggle is explicitly false", () => {
|
|
194
|
+
const result: Array<string> =
|
|
195
|
+
WorkspaceNotificationRuleService.getExistingChatIdsFromNotificationRules({
|
|
196
|
+
notificationRules: [
|
|
197
|
+
makeBaseRule({
|
|
198
|
+
shouldPostToExistingChat: false,
|
|
199
|
+
existingChatIds: ["chat-1"],
|
|
200
|
+
}),
|
|
201
|
+
],
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(result).toEqual([]);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("skips rules where the chat toggle is undefined (legacy rules)", () => {
|
|
208
|
+
const result: Array<string> =
|
|
209
|
+
WorkspaceNotificationRuleService.getExistingChatIdsFromNotificationRules({
|
|
210
|
+
notificationRules: [
|
|
211
|
+
makeBaseRule({
|
|
212
|
+
existingChatIds: ["chat-1"],
|
|
213
|
+
}),
|
|
214
|
+
],
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
expect(result).toEqual([]);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("dedupes chat ids across multiple rules", () => {
|
|
221
|
+
const result: Array<string> =
|
|
222
|
+
WorkspaceNotificationRuleService.getExistingChatIdsFromNotificationRules({
|
|
223
|
+
notificationRules: [
|
|
224
|
+
makeBaseRule({
|
|
225
|
+
shouldPostToExistingChat: true,
|
|
226
|
+
existingChatIds: ["chat-1", "chat-2"],
|
|
227
|
+
}),
|
|
228
|
+
makeBaseRule({
|
|
229
|
+
shouldPostToExistingChat: true,
|
|
230
|
+
existingChatIds: ["chat-2", "chat-3"],
|
|
231
|
+
}),
|
|
232
|
+
],
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
expect(result).toEqual(["chat-1", "chat-2", "chat-3"]);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("dedupes chat ids repeated within a single rule", () => {
|
|
239
|
+
const result: Array<string> =
|
|
240
|
+
WorkspaceNotificationRuleService.getExistingChatIdsFromNotificationRules({
|
|
241
|
+
notificationRules: [
|
|
242
|
+
makeBaseRule({
|
|
243
|
+
shouldPostToExistingChat: true,
|
|
244
|
+
existingChatIds: ["chat-1", "chat-1", "chat-2"],
|
|
245
|
+
}),
|
|
246
|
+
],
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
expect(result).toEqual(["chat-1", "chat-2"]);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("skips empty-string chat ids", () => {
|
|
253
|
+
const result: Array<string> =
|
|
254
|
+
WorkspaceNotificationRuleService.getExistingChatIdsFromNotificationRules({
|
|
255
|
+
notificationRules: [
|
|
256
|
+
makeBaseRule({
|
|
257
|
+
shouldPostToExistingChat: true,
|
|
258
|
+
existingChatIds: ["", "chat-1", ""],
|
|
259
|
+
}),
|
|
260
|
+
],
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
expect(result).toEqual(["chat-1"]);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("handles a toggled-on rule with undefined existingChatIds", () => {
|
|
267
|
+
const result: Array<string> =
|
|
268
|
+
WorkspaceNotificationRuleService.getExistingChatIdsFromNotificationRules({
|
|
269
|
+
notificationRules: [
|
|
270
|
+
makeBaseRule({
|
|
271
|
+
shouldPostToExistingChat: true,
|
|
272
|
+
}),
|
|
273
|
+
],
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
expect(result).toEqual([]);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("preserves first-seen order across rules", () => {
|
|
280
|
+
const result: Array<string> =
|
|
281
|
+
WorkspaceNotificationRuleService.getExistingChatIdsFromNotificationRules({
|
|
282
|
+
notificationRules: [
|
|
283
|
+
makeBaseRule({
|
|
284
|
+
shouldPostToExistingChat: true,
|
|
285
|
+
existingChatIds: ["z-chat", "a-chat"],
|
|
286
|
+
}),
|
|
287
|
+
makeBaseRule({
|
|
288
|
+
shouldPostToExistingChat: true,
|
|
289
|
+
existingChatIds: ["m-chat", "a-chat"],
|
|
290
|
+
}),
|
|
291
|
+
],
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
expect(result).toEqual(["z-chat", "a-chat", "m-chat"]);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test("returns an empty array for an empty rules array", () => {
|
|
298
|
+
const result: Array<string> =
|
|
299
|
+
WorkspaceNotificationRuleService.getExistingChatIdsFromNotificationRules({
|
|
300
|
+
notificationRules: [],
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
expect(result).toEqual([]);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe("WorkspaceNotificationRuleService.getExistingChatIdsBasedOnEventType", () => {
|
|
308
|
+
test("returns [] for Slack and never evaluates rules (short-circuit)", async () => {
|
|
309
|
+
const matchSpy: jest.SpyInstance = jest
|
|
310
|
+
.spyOn(WorkspaceNotificationRuleService, "getMatchingNotificationRules")
|
|
311
|
+
.mockResolvedValue([]);
|
|
312
|
+
|
|
313
|
+
const notificationFor: NotificationFor = {
|
|
314
|
+
incidentId: ObjectID.generate(),
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const result: Array<string> =
|
|
318
|
+
await WorkspaceNotificationRuleService.getExistingChatIdsBasedOnEventType(
|
|
319
|
+
{
|
|
320
|
+
projectId: ObjectID.generate(),
|
|
321
|
+
workspaceType: WorkspaceType.Slack,
|
|
322
|
+
notificationRuleEventType: NotificationRuleEventType.Incident,
|
|
323
|
+
notificationFor: notificationFor,
|
|
324
|
+
},
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
expect(result).toEqual([]);
|
|
328
|
+
expect(matchSpy).not.toHaveBeenCalled();
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("for Microsoft Teams, calls getMatchingNotificationRules with the exact arguments", async () => {
|
|
332
|
+
const matchSpy: jest.SpyInstance = jest
|
|
333
|
+
.spyOn(WorkspaceNotificationRuleService, "getMatchingNotificationRules")
|
|
334
|
+
.mockResolvedValue([]);
|
|
335
|
+
|
|
336
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
337
|
+
const notificationFor: NotificationFor = {
|
|
338
|
+
incidentId: ObjectID.generate(),
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
await WorkspaceNotificationRuleService.getExistingChatIdsBasedOnEventType({
|
|
342
|
+
projectId: projectId,
|
|
343
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
344
|
+
notificationRuleEventType: NotificationRuleEventType.Incident,
|
|
345
|
+
notificationFor: notificationFor,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
expect(matchSpy).toHaveBeenCalledTimes(1);
|
|
349
|
+
expect(matchSpy).toHaveBeenCalledWith({
|
|
350
|
+
projectId: projectId,
|
|
351
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
352
|
+
notificationRuleEventType: NotificationRuleEventType.Incident,
|
|
353
|
+
notificationFor: notificationFor,
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("maps matching rules through the extractor and merges + dedupes chat ids", async () => {
|
|
358
|
+
const ruleA: WorkspaceNotificationRule = makeWorkspaceRule({
|
|
359
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
360
|
+
notificationRule: makeBaseRule({
|
|
361
|
+
shouldPostToExistingChat: true,
|
|
362
|
+
existingChatIds: ["chat-1", "chat-2"],
|
|
363
|
+
}),
|
|
364
|
+
});
|
|
365
|
+
const ruleB: WorkspaceNotificationRule = makeWorkspaceRule({
|
|
366
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
367
|
+
notificationRule: makeBaseRule({
|
|
368
|
+
shouldPostToExistingChat: true,
|
|
369
|
+
existingChatIds: ["chat-2", "chat-3"],
|
|
370
|
+
}),
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
jest
|
|
374
|
+
.spyOn(WorkspaceNotificationRuleService, "getMatchingNotificationRules")
|
|
375
|
+
.mockResolvedValue([ruleA, ruleB]);
|
|
376
|
+
|
|
377
|
+
const result: Array<string> =
|
|
378
|
+
await WorkspaceNotificationRuleService.getExistingChatIdsBasedOnEventType(
|
|
379
|
+
{
|
|
380
|
+
projectId: ObjectID.generate(),
|
|
381
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
382
|
+
notificationRuleEventType: NotificationRuleEventType.Incident,
|
|
383
|
+
notificationFor: { incidentId: ObjectID.generate() },
|
|
384
|
+
},
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
expect(result).toEqual(["chat-1", "chat-2", "chat-3"]);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("matching rules with the chat toggle off contribute no chat ids", async () => {
|
|
391
|
+
const ruleOn: WorkspaceNotificationRule = makeWorkspaceRule({
|
|
392
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
393
|
+
notificationRule: makeBaseRule({
|
|
394
|
+
shouldPostToExistingChat: true,
|
|
395
|
+
existingChatIds: ["chat-1"],
|
|
396
|
+
}),
|
|
397
|
+
});
|
|
398
|
+
const ruleOff: WorkspaceNotificationRule = makeWorkspaceRule({
|
|
399
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
400
|
+
notificationRule: makeBaseRule({
|
|
401
|
+
shouldPostToExistingChat: false,
|
|
402
|
+
existingChatIds: ["chat-9"],
|
|
403
|
+
}),
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
jest
|
|
407
|
+
.spyOn(WorkspaceNotificationRuleService, "getMatchingNotificationRules")
|
|
408
|
+
.mockResolvedValue([ruleOn, ruleOff]);
|
|
409
|
+
|
|
410
|
+
const result: Array<string> =
|
|
411
|
+
await WorkspaceNotificationRuleService.getExistingChatIdsBasedOnEventType(
|
|
412
|
+
{
|
|
413
|
+
projectId: ObjectID.generate(),
|
|
414
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
415
|
+
notificationRuleEventType: NotificationRuleEventType.Incident,
|
|
416
|
+
notificationFor: { incidentId: ObjectID.generate() },
|
|
417
|
+
},
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
expect(result).toEqual(["chat-1"]);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
test("returns [] for Microsoft Teams when no rules match", async () => {
|
|
424
|
+
jest
|
|
425
|
+
.spyOn(WorkspaceNotificationRuleService, "getMatchingNotificationRules")
|
|
426
|
+
.mockResolvedValue([]);
|
|
427
|
+
|
|
428
|
+
const result: Array<string> =
|
|
429
|
+
await WorkspaceNotificationRuleService.getExistingChatIdsBasedOnEventType(
|
|
430
|
+
{
|
|
431
|
+
projectId: ObjectID.generate(),
|
|
432
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
433
|
+
notificationRuleEventType: NotificationRuleEventType.Incident,
|
|
434
|
+
notificationFor: { incidentId: ObjectID.generate() },
|
|
435
|
+
},
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
expect(result).toEqual([]);
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
describe("WorkspaceNotificationRuleService.getConnectedMicrosoftTeamsChats", () => {
|
|
443
|
+
test("returns {} when no project auth token exists", async () => {
|
|
444
|
+
jest
|
|
445
|
+
.spyOn(WorkspaceProjectAuthTokenService, "getProjectAuth")
|
|
446
|
+
.mockResolvedValue(null);
|
|
447
|
+
|
|
448
|
+
const result: Record<string, MicrosoftTeamsChat> =
|
|
449
|
+
await WorkspaceNotificationRuleService.getConnectedMicrosoftTeamsChats({
|
|
450
|
+
projectId: ObjectID.generate(),
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
expect(result).toEqual({});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test("returns {} when the project auth token has no miscData", async () => {
|
|
457
|
+
const projectAuth: WorkspaceProjectAuthToken =
|
|
458
|
+
new WorkspaceProjectAuthToken();
|
|
459
|
+
|
|
460
|
+
jest
|
|
461
|
+
.spyOn(WorkspaceProjectAuthTokenService, "getProjectAuth")
|
|
462
|
+
.mockResolvedValue(projectAuth);
|
|
463
|
+
|
|
464
|
+
const result: Record<string, MicrosoftTeamsChat> =
|
|
465
|
+
await WorkspaceNotificationRuleService.getConnectedMicrosoftTeamsChats({
|
|
466
|
+
projectId: ObjectID.generate(),
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
expect(result).toEqual({});
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
test("returns {} when miscData exists but has no availableChats", async () => {
|
|
473
|
+
const projectAuth: WorkspaceProjectAuthToken =
|
|
474
|
+
new WorkspaceProjectAuthToken();
|
|
475
|
+
projectAuth.miscData = makeTeamsMiscData(undefined);
|
|
476
|
+
|
|
477
|
+
jest
|
|
478
|
+
.spyOn(WorkspaceProjectAuthTokenService, "getProjectAuth")
|
|
479
|
+
.mockResolvedValue(projectAuth);
|
|
480
|
+
|
|
481
|
+
const result: Record<string, MicrosoftTeamsChat> =
|
|
482
|
+
await WorkspaceNotificationRuleService.getConnectedMicrosoftTeamsChats({
|
|
483
|
+
projectId: ObjectID.generate(),
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
expect(result).toEqual({});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("returns the availableChats map and queries auth for Microsoft Teams", async () => {
|
|
490
|
+
const availableChats: Record<string, MicrosoftTeamsChat> = {
|
|
491
|
+
"chat-1": makeChat("chat-1"),
|
|
492
|
+
"chat-2": {
|
|
493
|
+
id: "chat-2",
|
|
494
|
+
name: "Personal chat",
|
|
495
|
+
chatType: "personal",
|
|
496
|
+
serviceUrl: "https://smba.trafficmanager.net/amer/",
|
|
497
|
+
addedAt: "2026-07-27T00:00:00.000Z",
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
const projectAuth: WorkspaceProjectAuthToken =
|
|
502
|
+
new WorkspaceProjectAuthToken();
|
|
503
|
+
projectAuth.miscData = makeTeamsMiscData(availableChats);
|
|
504
|
+
|
|
505
|
+
const getProjectAuthSpy: jest.SpyInstance = jest
|
|
506
|
+
.spyOn(WorkspaceProjectAuthTokenService, "getProjectAuth")
|
|
507
|
+
.mockResolvedValue(projectAuth);
|
|
508
|
+
|
|
509
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
510
|
+
|
|
511
|
+
const result: Record<string, MicrosoftTeamsChat> =
|
|
512
|
+
await WorkspaceNotificationRuleService.getConnectedMicrosoftTeamsChats({
|
|
513
|
+
projectId: projectId,
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
expect(result).toEqual(availableChats);
|
|
517
|
+
expect(getProjectAuthSpy).toHaveBeenCalledWith({
|
|
518
|
+
projectId: projectId,
|
|
519
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
describe("WorkspaceNotificationRuleService.sendWorkspaceMarkdownNotification (chat payloads)", () => {
|
|
525
|
+
let projectId: ObjectID;
|
|
526
|
+
let notificationFor: NotificationFor;
|
|
527
|
+
|
|
528
|
+
interface MarkdownSendMocks {
|
|
529
|
+
postSpy: jest.SpyInstance;
|
|
530
|
+
createLogSpy: jest.SpyInstance;
|
|
531
|
+
chatIdsSpy: jest.SpyInstance;
|
|
532
|
+
existingChannelsSpy: jest.SpyInstance;
|
|
533
|
+
monitorChannelsSpy: jest.SpyInstance;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
beforeEach(() => {
|
|
537
|
+
projectId = ObjectID.generate();
|
|
538
|
+
notificationFor = { incidentId: ObjectID.generate() };
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
function mockMarkdownSendDeps(data: {
|
|
542
|
+
blocks: Array<MessageBlocksByWorkspaceType>;
|
|
543
|
+
existingChannelsByWorkspaceType: Partial<
|
|
544
|
+
Record<WorkspaceType, Array<WorkspaceChannel>>
|
|
545
|
+
>;
|
|
546
|
+
monitorChannelsByWorkspaceType: Partial<
|
|
547
|
+
Record<WorkspaceType, Array<WorkspaceChannel>>
|
|
548
|
+
>;
|
|
549
|
+
chatIdsByWorkspaceType: Partial<Record<WorkspaceType, Array<string>>>;
|
|
550
|
+
responses: Array<WorkspaceSendMessageResponse>;
|
|
551
|
+
}): MarkdownSendMocks {
|
|
552
|
+
jest
|
|
553
|
+
.spyOn(WorkspaceUtil, "getMessageBlocksByMarkdown")
|
|
554
|
+
.mockResolvedValue(data.blocks);
|
|
555
|
+
|
|
556
|
+
const existingChannelsSpy: jest.SpyInstance = jest
|
|
557
|
+
.spyOn(
|
|
558
|
+
WorkspaceNotificationRuleService,
|
|
559
|
+
"getExistingChannelNamesBasedOnEventType",
|
|
560
|
+
)
|
|
561
|
+
.mockImplementation(
|
|
562
|
+
(args: {
|
|
563
|
+
projectId: ObjectID;
|
|
564
|
+
workspaceType: WorkspaceType;
|
|
565
|
+
notificationRuleEventType: NotificationRuleEventType;
|
|
566
|
+
notificationFor: NotificationFor;
|
|
567
|
+
}): Promise<Array<WorkspaceChannel>> => {
|
|
568
|
+
return Promise.resolve(
|
|
569
|
+
data.existingChannelsByWorkspaceType[args.workspaceType] || [],
|
|
570
|
+
);
|
|
571
|
+
},
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
const monitorChannelsSpy: jest.SpyInstance = jest
|
|
575
|
+
.spyOn(
|
|
576
|
+
WorkspaceNotificationRuleService as any,
|
|
577
|
+
"getWorkspaceChannelsByNotificationFor",
|
|
578
|
+
)
|
|
579
|
+
.mockImplementation(
|
|
580
|
+
(...args: Array<unknown>): Promise<Array<WorkspaceChannel>> => {
|
|
581
|
+
const callData: { workspaceType: WorkspaceType } = args[0] as {
|
|
582
|
+
workspaceType: WorkspaceType;
|
|
583
|
+
};
|
|
584
|
+
return Promise.resolve(
|
|
585
|
+
data.monitorChannelsByWorkspaceType[callData.workspaceType] || [],
|
|
586
|
+
);
|
|
587
|
+
},
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
const chatIdsSpy: jest.SpyInstance = jest
|
|
591
|
+
.spyOn(
|
|
592
|
+
WorkspaceNotificationRuleService,
|
|
593
|
+
"getExistingChatIdsBasedOnEventType",
|
|
594
|
+
)
|
|
595
|
+
.mockImplementation(
|
|
596
|
+
(args: {
|
|
597
|
+
projectId: ObjectID;
|
|
598
|
+
workspaceType: WorkspaceType;
|
|
599
|
+
notificationRuleEventType: NotificationRuleEventType;
|
|
600
|
+
notificationFor: NotificationFor;
|
|
601
|
+
}): Promise<Array<string>> => {
|
|
602
|
+
return Promise.resolve(
|
|
603
|
+
data.chatIdsByWorkspaceType[args.workspaceType] || [],
|
|
604
|
+
);
|
|
605
|
+
},
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
const postSpy: jest.SpyInstance = jest
|
|
609
|
+
.spyOn(WorkspaceUtil, "postMessageToAllWorkspaceChannelsAsBot")
|
|
610
|
+
.mockResolvedValue(data.responses);
|
|
611
|
+
|
|
612
|
+
const createLogSpy: jest.SpyInstance = jest
|
|
613
|
+
.spyOn(WorkspaceNotificationLogService, "create")
|
|
614
|
+
.mockResolvedValue(new WorkspaceNotificationLog());
|
|
615
|
+
|
|
616
|
+
return {
|
|
617
|
+
postSpy,
|
|
618
|
+
createLogSpy,
|
|
619
|
+
chatIdsSpy,
|
|
620
|
+
existingChannelsSpy,
|
|
621
|
+
monitorChannelsSpy,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function getSentPayloads(
|
|
626
|
+
postSpy: jest.SpyInstance,
|
|
627
|
+
): Array<WorkspaceMessagePayload> {
|
|
628
|
+
expect(postSpy).toHaveBeenCalledTimes(1);
|
|
629
|
+
const callArg: {
|
|
630
|
+
projectId: ObjectID;
|
|
631
|
+
messagePayloadsByWorkspace: Array<WorkspaceMessagePayload>;
|
|
632
|
+
} = postSpy.mock.calls[0]?.[0] as {
|
|
633
|
+
projectId: ObjectID;
|
|
634
|
+
messagePayloadsByWorkspace: Array<WorkspaceMessagePayload>;
|
|
635
|
+
};
|
|
636
|
+
return callArg.messagePayloadsByWorkspace;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
test("builds one chat payload per chat id with empty channelNames/channelIds", async () => {
|
|
640
|
+
const teamsBlocks: MessageBlocksByWorkspaceType = {
|
|
641
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
642
|
+
messageBlocks: [markdownBlock("Incident update")],
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
const mocks: MarkdownSendMocks = mockMarkdownSendDeps({
|
|
646
|
+
blocks: [teamsBlocks],
|
|
647
|
+
existingChannelsByWorkspaceType: {},
|
|
648
|
+
monitorChannelsByWorkspaceType: {},
|
|
649
|
+
chatIdsByWorkspaceType: {
|
|
650
|
+
[WorkspaceType.MicrosoftTeams]: ["chat-a", "chat-b"],
|
|
651
|
+
},
|
|
652
|
+
responses: [],
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
await WorkspaceNotificationRuleService.sendWorkspaceMarkdownNotification({
|
|
656
|
+
projectId: projectId,
|
|
657
|
+
notificationFor: notificationFor,
|
|
658
|
+
feedInfoInMarkdown: "Incident update",
|
|
659
|
+
workspaceNotification: {
|
|
660
|
+
sendWorkspaceNotification: true,
|
|
661
|
+
},
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
const payloads: Array<WorkspaceMessagePayload> = getSentPayloads(
|
|
665
|
+
mocks.postSpy,
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
expect(payloads).toHaveLength(2);
|
|
669
|
+
|
|
670
|
+
expect(payloads[0]).toEqual({
|
|
671
|
+
_type: "WorkspaceMessagePayload",
|
|
672
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
673
|
+
messageBlocks: teamsBlocks.messageBlocks,
|
|
674
|
+
channelNames: [],
|
|
675
|
+
channelIds: [],
|
|
676
|
+
chatIds: ["chat-a"],
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
expect(payloads[1]).toEqual({
|
|
680
|
+
_type: "WorkspaceMessagePayload",
|
|
681
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
682
|
+
messageBlocks: teamsBlocks.messageBlocks,
|
|
683
|
+
channelNames: [],
|
|
684
|
+
channelIds: [],
|
|
685
|
+
chatIds: ["chat-b"],
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
test("chat payloads coexist with unchanged channel payloads (persisted -> channelIds, named -> channelNames)", async () => {
|
|
690
|
+
const teamsBlocks: MessageBlocksByWorkspaceType = {
|
|
691
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
692
|
+
messageBlocks: [markdownBlock("Incident update")],
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
const persistedChannel: WorkspaceChannel = makeChannel({
|
|
696
|
+
id: "chan-123",
|
|
697
|
+
name: "persisted-channel",
|
|
698
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
699
|
+
teamId: "team-1",
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
const namedChannel: WorkspaceChannel = makeChannel({
|
|
703
|
+
id: "",
|
|
704
|
+
name: "named-channel",
|
|
705
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
706
|
+
teamId: "team-1",
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
const mocks: MarkdownSendMocks = mockMarkdownSendDeps({
|
|
710
|
+
blocks: [teamsBlocks],
|
|
711
|
+
existingChannelsByWorkspaceType: {
|
|
712
|
+
[WorkspaceType.MicrosoftTeams]: [namedChannel],
|
|
713
|
+
},
|
|
714
|
+
monitorChannelsByWorkspaceType: {
|
|
715
|
+
[WorkspaceType.MicrosoftTeams]: [persistedChannel],
|
|
716
|
+
},
|
|
717
|
+
chatIdsByWorkspaceType: {
|
|
718
|
+
[WorkspaceType.MicrosoftTeams]: ["chat-a"],
|
|
719
|
+
},
|
|
720
|
+
responses: [],
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
await WorkspaceNotificationRuleService.sendWorkspaceMarkdownNotification({
|
|
724
|
+
projectId: projectId,
|
|
725
|
+
notificationFor: notificationFor,
|
|
726
|
+
feedInfoInMarkdown: "Incident update",
|
|
727
|
+
workspaceNotification: {
|
|
728
|
+
sendWorkspaceNotification: true,
|
|
729
|
+
},
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
const payloads: Array<WorkspaceMessagePayload> = getSentPayloads(
|
|
733
|
+
mocks.postSpy,
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
expect(payloads).toHaveLength(3);
|
|
737
|
+
|
|
738
|
+
// Persisted channel payload: by channel id, unchanged by the chat feature.
|
|
739
|
+
expect(payloads[0]).toEqual({
|
|
740
|
+
_type: "WorkspaceMessagePayload",
|
|
741
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
742
|
+
messageBlocks: teamsBlocks.messageBlocks,
|
|
743
|
+
channelNames: [],
|
|
744
|
+
channelIds: ["chan-123"],
|
|
745
|
+
teamId: "team-1",
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
// Named channel payload: by channel name, unchanged by the chat feature.
|
|
749
|
+
expect(payloads[1]).toEqual({
|
|
750
|
+
_type: "WorkspaceMessagePayload",
|
|
751
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
752
|
+
messageBlocks: teamsBlocks.messageBlocks,
|
|
753
|
+
channelNames: ["named-channel"],
|
|
754
|
+
channelIds: [],
|
|
755
|
+
teamId: "team-1",
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
// Chat payload comes last and carries no teamId.
|
|
759
|
+
expect(payloads[2]).toEqual({
|
|
760
|
+
_type: "WorkspaceMessagePayload",
|
|
761
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
762
|
+
messageBlocks: teamsBlocks.messageBlocks,
|
|
763
|
+
channelNames: [],
|
|
764
|
+
channelIds: [],
|
|
765
|
+
chatIds: ["chat-a"],
|
|
766
|
+
});
|
|
767
|
+
expect(payloads[2]).not.toHaveProperty("teamId");
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
test("with Slack + Teams blocks, chat payloads are built ONLY for Teams", async () => {
|
|
771
|
+
const teamsBlocks: MessageBlocksByWorkspaceType = {
|
|
772
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
773
|
+
messageBlocks: [markdownBlock("Teams text")],
|
|
774
|
+
};
|
|
775
|
+
const slackBlocks: MessageBlocksByWorkspaceType = {
|
|
776
|
+
workspaceType: WorkspaceType.Slack,
|
|
777
|
+
messageBlocks: [markdownBlock("Slack text")],
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
const slackChannel: WorkspaceChannel = makeChannel({
|
|
781
|
+
id: "slack-chan-1",
|
|
782
|
+
name: "slack-channel",
|
|
783
|
+
workspaceType: WorkspaceType.Slack,
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
const mocks: MarkdownSendMocks = mockMarkdownSendDeps({
|
|
787
|
+
blocks: [slackBlocks, teamsBlocks],
|
|
788
|
+
existingChannelsByWorkspaceType: {},
|
|
789
|
+
monitorChannelsByWorkspaceType: {
|
|
790
|
+
[WorkspaceType.Slack]: [slackChannel],
|
|
791
|
+
},
|
|
792
|
+
chatIdsByWorkspaceType: {
|
|
793
|
+
[WorkspaceType.MicrosoftTeams]: ["chat-a", "chat-b"],
|
|
794
|
+
[WorkspaceType.Slack]: [],
|
|
795
|
+
},
|
|
796
|
+
responses: [],
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
await WorkspaceNotificationRuleService.sendWorkspaceMarkdownNotification({
|
|
800
|
+
projectId: projectId,
|
|
801
|
+
notificationFor: notificationFor,
|
|
802
|
+
feedInfoInMarkdown: "Update",
|
|
803
|
+
workspaceNotification: {
|
|
804
|
+
sendWorkspaceNotification: true,
|
|
805
|
+
},
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
const payloads: Array<WorkspaceMessagePayload> = getSentPayloads(
|
|
809
|
+
mocks.postSpy,
|
|
810
|
+
);
|
|
811
|
+
|
|
812
|
+
const chatPayloads: Array<WorkspaceMessagePayload> = payloads.filter(
|
|
813
|
+
(payload: WorkspaceMessagePayload) => {
|
|
814
|
+
return payload.chatIds && payload.chatIds.length > 0;
|
|
815
|
+
},
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
expect(chatPayloads).toHaveLength(2);
|
|
819
|
+
for (const chatPayload of chatPayloads) {
|
|
820
|
+
expect(chatPayload.workspaceType).toBe(WorkspaceType.MicrosoftTeams);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
const slackPayloads: Array<WorkspaceMessagePayload> = payloads.filter(
|
|
824
|
+
(payload: WorkspaceMessagePayload) => {
|
|
825
|
+
return payload.workspaceType === WorkspaceType.Slack;
|
|
826
|
+
},
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
expect(slackPayloads).toHaveLength(1);
|
|
830
|
+
expect(slackPayloads[0]?.channelIds).toEqual(["slack-chan-1"]);
|
|
831
|
+
expect(slackPayloads[0]?.chatIds).toBeUndefined();
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
test("queries chat ids with the correct event type and workspace type per block", async () => {
|
|
835
|
+
const teamsBlocks: MessageBlocksByWorkspaceType = {
|
|
836
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
837
|
+
messageBlocks: [markdownBlock("Teams text")],
|
|
838
|
+
};
|
|
839
|
+
const slackBlocks: MessageBlocksByWorkspaceType = {
|
|
840
|
+
workspaceType: WorkspaceType.Slack,
|
|
841
|
+
messageBlocks: [markdownBlock("Slack text")],
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
const mocks: MarkdownSendMocks = mockMarkdownSendDeps({
|
|
845
|
+
blocks: [slackBlocks, teamsBlocks],
|
|
846
|
+
existingChannelsByWorkspaceType: {},
|
|
847
|
+
monitorChannelsByWorkspaceType: {},
|
|
848
|
+
chatIdsByWorkspaceType: {},
|
|
849
|
+
responses: [],
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
await WorkspaceNotificationRuleService.sendWorkspaceMarkdownNotification({
|
|
853
|
+
projectId: projectId,
|
|
854
|
+
notificationFor: notificationFor,
|
|
855
|
+
feedInfoInMarkdown: "Update",
|
|
856
|
+
workspaceNotification: {
|
|
857
|
+
sendWorkspaceNotification: true,
|
|
858
|
+
},
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
expect(mocks.chatIdsSpy).toHaveBeenCalledTimes(2);
|
|
862
|
+
expect(mocks.chatIdsSpy).toHaveBeenCalledWith({
|
|
863
|
+
projectId: projectId,
|
|
864
|
+
notificationRuleEventType: NotificationRuleEventType.Incident,
|
|
865
|
+
workspaceType: WorkspaceType.Slack,
|
|
866
|
+
notificationFor: notificationFor,
|
|
867
|
+
});
|
|
868
|
+
expect(mocks.chatIdsSpy).toHaveBeenCalledWith({
|
|
869
|
+
projectId: projectId,
|
|
870
|
+
notificationRuleEventType: NotificationRuleEventType.Incident,
|
|
871
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
872
|
+
notificationFor: notificationFor,
|
|
873
|
+
});
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
test("posts once with all payloads and no chat payloads when no chats are configured", async () => {
|
|
877
|
+
const teamsBlocks: MessageBlocksByWorkspaceType = {
|
|
878
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
879
|
+
messageBlocks: [markdownBlock("Teams text")],
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
const mocks: MarkdownSendMocks = mockMarkdownSendDeps({
|
|
883
|
+
blocks: [teamsBlocks],
|
|
884
|
+
existingChannelsByWorkspaceType: {},
|
|
885
|
+
monitorChannelsByWorkspaceType: {},
|
|
886
|
+
chatIdsByWorkspaceType: {},
|
|
887
|
+
responses: [],
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
await WorkspaceNotificationRuleService.sendWorkspaceMarkdownNotification({
|
|
891
|
+
projectId: projectId,
|
|
892
|
+
notificationFor: notificationFor,
|
|
893
|
+
feedInfoInMarkdown: "Update",
|
|
894
|
+
workspaceNotification: {
|
|
895
|
+
sendWorkspaceNotification: true,
|
|
896
|
+
},
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
const payloads: Array<WorkspaceMessagePayload> = getSentPayloads(
|
|
900
|
+
mocks.postSpy,
|
|
901
|
+
);
|
|
902
|
+
|
|
903
|
+
expect(payloads).toEqual([]);
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
test("logs a Success notification with channelId = chat id when the response thread is a chat", async () => {
|
|
907
|
+
const teamsBlocks: MessageBlocksByWorkspaceType = {
|
|
908
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
909
|
+
messageBlocks: [markdownBlock("Incident update")],
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
const response: WorkspaceSendMessageResponse = makeSendResponse({
|
|
913
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
914
|
+
threads: [
|
|
915
|
+
{
|
|
916
|
+
channelId: "chat-a",
|
|
917
|
+
channelName: "Chat chat-a",
|
|
918
|
+
threadId: "thread-1",
|
|
919
|
+
},
|
|
920
|
+
],
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
const mocks: MarkdownSendMocks = mockMarkdownSendDeps({
|
|
924
|
+
blocks: [teamsBlocks],
|
|
925
|
+
existingChannelsByWorkspaceType: {},
|
|
926
|
+
monitorChannelsByWorkspaceType: {},
|
|
927
|
+
chatIdsByWorkspaceType: {
|
|
928
|
+
[WorkspaceType.MicrosoftTeams]: ["chat-a"],
|
|
929
|
+
},
|
|
930
|
+
responses: [response],
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
await WorkspaceNotificationRuleService.sendWorkspaceMarkdownNotification({
|
|
934
|
+
projectId: projectId,
|
|
935
|
+
notificationFor: notificationFor,
|
|
936
|
+
feedInfoInMarkdown: "Incident update",
|
|
937
|
+
workspaceNotification: {
|
|
938
|
+
sendWorkspaceNotification: true,
|
|
939
|
+
},
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
expect(mocks.createLogSpy).toHaveBeenCalledTimes(1);
|
|
943
|
+
|
|
944
|
+
const createdLog: WorkspaceNotificationLog = mocks.createLogSpy.mock
|
|
945
|
+
.calls[0]?.[0]?.data as WorkspaceNotificationLog;
|
|
946
|
+
|
|
947
|
+
expect(createdLog.channelId).toBe("chat-a");
|
|
948
|
+
expect(createdLog.channelName).toBe("Chat chat-a");
|
|
949
|
+
expect(createdLog.threadId).toBe("thread-1");
|
|
950
|
+
expect(createdLog.status).toBe(WorkspaceNotificationStatus.Success);
|
|
951
|
+
expect(createdLog.workspaceType).toBe(WorkspaceType.MicrosoftTeams);
|
|
952
|
+
expect(createdLog.message).toBe("Incident update");
|
|
953
|
+
expect(createdLog.incidentId).toBe(notificationFor.incidentId);
|
|
954
|
+
expect(createdLog.actionType).toBe(
|
|
955
|
+
WorkspaceNotificationActionType.SendMessage,
|
|
956
|
+
);
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
test("creates no logs when the send response has zero threads", async () => {
|
|
960
|
+
const teamsBlocks: MessageBlocksByWorkspaceType = {
|
|
961
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
962
|
+
messageBlocks: [markdownBlock("Incident update")],
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
const response: WorkspaceSendMessageResponse = makeSendResponse({
|
|
966
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
967
|
+
threads: [],
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
const mocks: MarkdownSendMocks = mockMarkdownSendDeps({
|
|
971
|
+
blocks: [teamsBlocks],
|
|
972
|
+
existingChannelsByWorkspaceType: {},
|
|
973
|
+
monitorChannelsByWorkspaceType: {},
|
|
974
|
+
chatIdsByWorkspaceType: {
|
|
975
|
+
[WorkspaceType.MicrosoftTeams]: ["chat-a"],
|
|
976
|
+
},
|
|
977
|
+
responses: [response],
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
await WorkspaceNotificationRuleService.sendWorkspaceMarkdownNotification({
|
|
981
|
+
projectId: projectId,
|
|
982
|
+
notificationFor: notificationFor,
|
|
983
|
+
feedInfoInMarkdown: "Incident update",
|
|
984
|
+
workspaceNotification: {
|
|
985
|
+
sendWorkspaceNotification: true,
|
|
986
|
+
},
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
expect(mocks.createLogSpy).not.toHaveBeenCalled();
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
test("logs an Error notification for each per-destination failure in the send response", async () => {
|
|
993
|
+
const teamsBlocks: MessageBlocksByWorkspaceType = {
|
|
994
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
995
|
+
messageBlocks: [markdownBlock("Incident update")],
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
const response: WorkspaceSendMessageResponse = makeSendResponse({
|
|
999
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1000
|
+
threads: [
|
|
1001
|
+
{
|
|
1002
|
+
channelId: "chat-a",
|
|
1003
|
+
channelName: "Chat chat-a",
|
|
1004
|
+
threadId: "thread-1",
|
|
1005
|
+
},
|
|
1006
|
+
],
|
|
1007
|
+
errors: [
|
|
1008
|
+
{
|
|
1009
|
+
channelId: "chat-b",
|
|
1010
|
+
channelName: "Chat chat-b",
|
|
1011
|
+
error:
|
|
1012
|
+
"This chat is not connected to OneUptime. Please add the OneUptime app to the chat in Microsoft Teams and try again.",
|
|
1013
|
+
},
|
|
1014
|
+
],
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
const mocks: MarkdownSendMocks = mockMarkdownSendDeps({
|
|
1018
|
+
blocks: [teamsBlocks],
|
|
1019
|
+
existingChannelsByWorkspaceType: {},
|
|
1020
|
+
monitorChannelsByWorkspaceType: {},
|
|
1021
|
+
chatIdsByWorkspaceType: {
|
|
1022
|
+
[WorkspaceType.MicrosoftTeams]: ["chat-a", "chat-b"],
|
|
1023
|
+
},
|
|
1024
|
+
responses: [response],
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
await WorkspaceNotificationRuleService.sendWorkspaceMarkdownNotification({
|
|
1028
|
+
projectId: projectId,
|
|
1029
|
+
notificationFor: notificationFor,
|
|
1030
|
+
feedInfoInMarkdown: "Incident update",
|
|
1031
|
+
workspaceNotification: {
|
|
1032
|
+
sendWorkspaceNotification: true,
|
|
1033
|
+
},
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
expect(mocks.createLogSpy).toHaveBeenCalledTimes(2);
|
|
1037
|
+
|
|
1038
|
+
const successLog: WorkspaceNotificationLog = mocks.createLogSpy.mock
|
|
1039
|
+
.calls[0]?.[0]?.data as WorkspaceNotificationLog;
|
|
1040
|
+
expect(successLog.status).toBe(WorkspaceNotificationStatus.Success);
|
|
1041
|
+
expect(successLog.channelId).toBe("chat-a");
|
|
1042
|
+
|
|
1043
|
+
const errorLog: WorkspaceNotificationLog = mocks.createLogSpy.mock
|
|
1044
|
+
.calls[1]?.[0]?.data as WorkspaceNotificationLog;
|
|
1045
|
+
expect(errorLog.status).toBe(WorkspaceNotificationStatus.Error);
|
|
1046
|
+
expect(errorLog.channelId).toBe("chat-b");
|
|
1047
|
+
expect(errorLog.channelName).toBe("Chat chat-b");
|
|
1048
|
+
expect(errorLog.statusMessage).toContain(
|
|
1049
|
+
"This chat is not connected to OneUptime",
|
|
1050
|
+
);
|
|
1051
|
+
expect(errorLog.incidentId).toBe(notificationFor.incidentId);
|
|
1052
|
+
});
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
describe("WorkspaceNotificationRuleService.testRule (Microsoft Teams chat path)", () => {
|
|
1056
|
+
const testByUserId: ObjectID = ObjectID.generate();
|
|
1057
|
+
|
|
1058
|
+
interface TestRuleMocks {
|
|
1059
|
+
postSpy: jest.SpyInstance;
|
|
1060
|
+
createLogSpy: jest.SpyInstance;
|
|
1061
|
+
getProjectAuthSpy: jest.SpyInstance;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function mockTestRuleDeps(data: {
|
|
1065
|
+
rule: WorkspaceNotificationRule;
|
|
1066
|
+
userAuth?: WorkspaceUserAuthToken | null | undefined;
|
|
1067
|
+
projectAuth?: WorkspaceProjectAuthToken | null | undefined;
|
|
1068
|
+
availableChats?: Record<string, MicrosoftTeamsChat> | undefined;
|
|
1069
|
+
responses?: Array<WorkspaceSendMessageResponse> | undefined;
|
|
1070
|
+
}): TestRuleMocks {
|
|
1071
|
+
jest
|
|
1072
|
+
.spyOn(WorkspaceNotificationRuleService, "findOneById")
|
|
1073
|
+
.mockResolvedValue(data.rule);
|
|
1074
|
+
|
|
1075
|
+
let userAuth: WorkspaceUserAuthToken | null;
|
|
1076
|
+
if (data.userAuth === undefined) {
|
|
1077
|
+
userAuth = new WorkspaceUserAuthToken();
|
|
1078
|
+
userAuth.workspaceUserId = "teams-user-1";
|
|
1079
|
+
} else {
|
|
1080
|
+
userAuth = data.userAuth;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
jest
|
|
1084
|
+
.spyOn(WorkspaceUserAuthTokenService, "findOneBy")
|
|
1085
|
+
.mockResolvedValue(userAuth);
|
|
1086
|
+
|
|
1087
|
+
let projectAuth: WorkspaceProjectAuthToken | null;
|
|
1088
|
+
if (data.projectAuth === undefined) {
|
|
1089
|
+
projectAuth = new WorkspaceProjectAuthToken();
|
|
1090
|
+
projectAuth.authToken = "project-auth-token";
|
|
1091
|
+
projectAuth.workspaceType = data.rule.workspaceType!;
|
|
1092
|
+
} else {
|
|
1093
|
+
projectAuth = data.projectAuth;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
jest
|
|
1097
|
+
.spyOn(WorkspaceProjectAuthTokenService, "findOneBy")
|
|
1098
|
+
.mockResolvedValue(projectAuth);
|
|
1099
|
+
|
|
1100
|
+
const authWithChats: WorkspaceProjectAuthToken =
|
|
1101
|
+
new WorkspaceProjectAuthToken();
|
|
1102
|
+
authWithChats.miscData = makeTeamsMiscData(data.availableChats || {});
|
|
1103
|
+
|
|
1104
|
+
const getProjectAuthSpy: jest.SpyInstance = jest
|
|
1105
|
+
.spyOn(WorkspaceProjectAuthTokenService, "getProjectAuth")
|
|
1106
|
+
.mockResolvedValue(authWithChats);
|
|
1107
|
+
|
|
1108
|
+
const postSpy: jest.SpyInstance = jest
|
|
1109
|
+
.spyOn(WorkspaceUtil, "postMessageToAllWorkspaceChannelsAsBot")
|
|
1110
|
+
.mockResolvedValue(data.responses || []);
|
|
1111
|
+
|
|
1112
|
+
const createLogSpy: jest.SpyInstance = jest
|
|
1113
|
+
.spyOn(WorkspaceNotificationLogService, "create")
|
|
1114
|
+
.mockResolvedValue(new WorkspaceNotificationLog());
|
|
1115
|
+
|
|
1116
|
+
return { postSpy, createLogSpy, getProjectAuthSpy };
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function makeChatRule(
|
|
1120
|
+
overrides: BaseRuleOverrides,
|
|
1121
|
+
): WorkspaceNotificationRule {
|
|
1122
|
+
return makeWorkspaceRule({
|
|
1123
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1124
|
+
notificationRule: makeBaseRule({
|
|
1125
|
+
shouldPostToExistingChat: true,
|
|
1126
|
+
existingChatIds: ["chat-1"],
|
|
1127
|
+
...overrides,
|
|
1128
|
+
}),
|
|
1129
|
+
name: "Chat Rule",
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
async function runTestRule(rule: WorkspaceNotificationRule): Promise<void> {
|
|
1134
|
+
await WorkspaceNotificationRuleService.testRule({
|
|
1135
|
+
ruleId: rule.id!,
|
|
1136
|
+
projectId: ObjectID.generate(), // testRule overwrites this with rule.projectId.
|
|
1137
|
+
testByUserId: testByUserId,
|
|
1138
|
+
props: { isRoot: true },
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
test("happy path: posts a single payload with chatIds and logs a chat Success", async () => {
|
|
1143
|
+
const rule: WorkspaceNotificationRule = makeChatRule({});
|
|
1144
|
+
|
|
1145
|
+
const response: WorkspaceSendMessageResponse = makeSendResponse({
|
|
1146
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1147
|
+
threads: [
|
|
1148
|
+
{
|
|
1149
|
+
channelId: "chat-1",
|
|
1150
|
+
channelName: "Chat chat-1",
|
|
1151
|
+
threadId: "thread-9",
|
|
1152
|
+
},
|
|
1153
|
+
],
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
const mocks: TestRuleMocks = mockTestRuleDeps({
|
|
1157
|
+
rule: rule,
|
|
1158
|
+
availableChats: { "chat-1": makeChat("chat-1") },
|
|
1159
|
+
responses: [response],
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
await runTestRule(rule);
|
|
1163
|
+
|
|
1164
|
+
expect(mocks.postSpy).toHaveBeenCalledTimes(1);
|
|
1165
|
+
|
|
1166
|
+
const callArg: {
|
|
1167
|
+
projectId: ObjectID;
|
|
1168
|
+
messagePayloadsByWorkspace: Array<WorkspaceMessagePayload>;
|
|
1169
|
+
} = mocks.postSpy.mock.calls[0]?.[0] as {
|
|
1170
|
+
projectId: ObjectID;
|
|
1171
|
+
messagePayloadsByWorkspace: Array<WorkspaceMessagePayload>;
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
expect(callArg.projectId).toBe(rule.projectId);
|
|
1175
|
+
expect(callArg.messagePayloadsByWorkspace).toHaveLength(1);
|
|
1176
|
+
expect(callArg.messagePayloadsByWorkspace[0]).toEqual({
|
|
1177
|
+
_type: "WorkspaceMessagePayload",
|
|
1178
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1179
|
+
messageBlocks: [
|
|
1180
|
+
{
|
|
1181
|
+
_type: "WorkspacePayloadMarkdown",
|
|
1182
|
+
text: "This is a test message for rule **Chat Rule**",
|
|
1183
|
+
},
|
|
1184
|
+
],
|
|
1185
|
+
channelNames: [],
|
|
1186
|
+
channelIds: [],
|
|
1187
|
+
chatIds: ["chat-1"],
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
expect(mocks.createLogSpy).toHaveBeenCalledTimes(1);
|
|
1191
|
+
const createdLog: WorkspaceNotificationLog = mocks.createLogSpy.mock
|
|
1192
|
+
.calls[0]?.[0]?.data as WorkspaceNotificationLog;
|
|
1193
|
+
|
|
1194
|
+
expect(createdLog.channelId).toBe("chat-1");
|
|
1195
|
+
expect(createdLog.channelName).toBe("Chat chat-1");
|
|
1196
|
+
expect(createdLog.threadId).toBe("thread-9");
|
|
1197
|
+
expect(createdLog.status).toBe(WorkspaceNotificationStatus.Success);
|
|
1198
|
+
expect(createdLog.statusMessage).toBe(
|
|
1199
|
+
"Test message posted to workspace chat",
|
|
1200
|
+
);
|
|
1201
|
+
expect(createdLog.message).toBe(
|
|
1202
|
+
"This is a test message for rule **Chat Rule**",
|
|
1203
|
+
);
|
|
1204
|
+
expect(createdLog.userId).toBe(testByUserId);
|
|
1205
|
+
expect(createdLog.actionType).toBe(
|
|
1206
|
+
WorkspaceNotificationActionType.SendMessage,
|
|
1207
|
+
);
|
|
1208
|
+
expect(createdLog.projectId).toBe(rule.projectId);
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
test("posts a single payload containing ALL selected chat ids and logs each thread", async () => {
|
|
1212
|
+
const rule: WorkspaceNotificationRule = makeChatRule({
|
|
1213
|
+
existingChatIds: ["chat-1", "chat-2"],
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
const response: WorkspaceSendMessageResponse = makeSendResponse({
|
|
1217
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1218
|
+
threads: [
|
|
1219
|
+
{
|
|
1220
|
+
channelId: "chat-1",
|
|
1221
|
+
channelName: "Chat chat-1",
|
|
1222
|
+
threadId: "thread-1",
|
|
1223
|
+
},
|
|
1224
|
+
{
|
|
1225
|
+
channelId: "chat-2",
|
|
1226
|
+
channelName: "Chat chat-2",
|
|
1227
|
+
threadId: "thread-2",
|
|
1228
|
+
},
|
|
1229
|
+
],
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
const mocks: TestRuleMocks = mockTestRuleDeps({
|
|
1233
|
+
rule: rule,
|
|
1234
|
+
availableChats: {
|
|
1235
|
+
"chat-1": makeChat("chat-1"),
|
|
1236
|
+
"chat-2": makeChat("chat-2"),
|
|
1237
|
+
},
|
|
1238
|
+
responses: [response],
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
await runTestRule(rule);
|
|
1242
|
+
|
|
1243
|
+
expect(mocks.postSpy).toHaveBeenCalledTimes(1);
|
|
1244
|
+
|
|
1245
|
+
const callArg: {
|
|
1246
|
+
messagePayloadsByWorkspace: Array<WorkspaceMessagePayload>;
|
|
1247
|
+
} = mocks.postSpy.mock.calls[0]?.[0] as {
|
|
1248
|
+
messagePayloadsByWorkspace: Array<WorkspaceMessagePayload>;
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
expect(callArg.messagePayloadsByWorkspace).toHaveLength(1);
|
|
1252
|
+
expect(callArg.messagePayloadsByWorkspace[0]?.chatIds).toEqual([
|
|
1253
|
+
"chat-1",
|
|
1254
|
+
"chat-2",
|
|
1255
|
+
]);
|
|
1256
|
+
|
|
1257
|
+
expect(mocks.createLogSpy).toHaveBeenCalledTimes(2);
|
|
1258
|
+
const firstLog: WorkspaceNotificationLog = mocks.createLogSpy.mock
|
|
1259
|
+
.calls[0]?.[0]?.data as WorkspaceNotificationLog;
|
|
1260
|
+
const secondLog: WorkspaceNotificationLog = mocks.createLogSpy.mock
|
|
1261
|
+
.calls[1]?.[0]?.data as WorkspaceNotificationLog;
|
|
1262
|
+
expect(firstLog.channelId).toBe("chat-1");
|
|
1263
|
+
expect(secondLog.channelId).toBe("chat-2");
|
|
1264
|
+
});
|
|
1265
|
+
|
|
1266
|
+
test("throws when a selected chat is no longer connected", async () => {
|
|
1267
|
+
const rule: WorkspaceNotificationRule = makeChatRule({});
|
|
1268
|
+
|
|
1269
|
+
const mocks: TestRuleMocks = mockTestRuleDeps({
|
|
1270
|
+
rule: rule,
|
|
1271
|
+
availableChats: { "some-other-chat": makeChat("some-other-chat") },
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
await expect(runTestRule(rule)).rejects.toThrow(/no longer connected/);
|
|
1275
|
+
expect(mocks.postSpy).not.toHaveBeenCalled();
|
|
1276
|
+
expect(mocks.createLogSpy).not.toHaveBeenCalled();
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
test("throws when the chat toggle is on but no chats are selected", async () => {
|
|
1280
|
+
const rule: WorkspaceNotificationRule = makeChatRule({
|
|
1281
|
+
existingChatIds: [],
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
const mocks: TestRuleMocks = mockTestRuleDeps({
|
|
1285
|
+
rule: rule,
|
|
1286
|
+
availableChats: { "chat-1": makeChat("chat-1") },
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
await expect(runTestRule(rule)).rejects.toThrow(/select at least one chat/);
|
|
1290
|
+
expect(mocks.getProjectAuthSpy).not.toHaveBeenCalled();
|
|
1291
|
+
expect(mocks.postSpy).not.toHaveBeenCalled();
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
test("throws 'Failed to send test message to some chats' when the send response has errors", async () => {
|
|
1295
|
+
const rule: WorkspaceNotificationRule = makeChatRule({});
|
|
1296
|
+
|
|
1297
|
+
const response: WorkspaceSendMessageResponse = makeSendResponse({
|
|
1298
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1299
|
+
threads: [],
|
|
1300
|
+
errors: [
|
|
1301
|
+
{
|
|
1302
|
+
channelId: "chat-1",
|
|
1303
|
+
channelName: "Chat chat-1",
|
|
1304
|
+
error: "Bot was removed from the chat",
|
|
1305
|
+
},
|
|
1306
|
+
],
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
const mocks: TestRuleMocks = mockTestRuleDeps({
|
|
1310
|
+
rule: rule,
|
|
1311
|
+
availableChats: { "chat-1": makeChat("chat-1") },
|
|
1312
|
+
responses: [response],
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
await expect(runTestRule(rule)).rejects.toThrow(
|
|
1316
|
+
/Failed to send test message to some chats/,
|
|
1317
|
+
);
|
|
1318
|
+
expect(mocks.createLogSpy).not.toHaveBeenCalled();
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
test("throws when the testing user is not connected to the workspace", async () => {
|
|
1322
|
+
const rule: WorkspaceNotificationRule = makeChatRule({});
|
|
1323
|
+
|
|
1324
|
+
const mocks: TestRuleMocks = mockTestRuleDeps({
|
|
1325
|
+
rule: rule,
|
|
1326
|
+
userAuth: null,
|
|
1327
|
+
availableChats: { "chat-1": makeChat("chat-1") },
|
|
1328
|
+
});
|
|
1329
|
+
|
|
1330
|
+
await expect(runTestRule(rule)).rejects.toThrow(
|
|
1331
|
+
/This account is not connected to MicrosoftTeams/,
|
|
1332
|
+
);
|
|
1333
|
+
expect(mocks.postSpy).not.toHaveBeenCalled();
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
test("throws when the project is not connected to the workspace", async () => {
|
|
1337
|
+
const rule: WorkspaceNotificationRule = makeChatRule({});
|
|
1338
|
+
|
|
1339
|
+
const mocks: TestRuleMocks = mockTestRuleDeps({
|
|
1340
|
+
rule: rule,
|
|
1341
|
+
projectAuth: null,
|
|
1342
|
+
availableChats: { "chat-1": makeChat("chat-1") },
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
await expect(runTestRule(rule)).rejects.toThrow(
|
|
1346
|
+
/This project is not connected to MicrosoftTeams/,
|
|
1347
|
+
);
|
|
1348
|
+
expect(mocks.postSpy).not.toHaveBeenCalled();
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
test("sends nothing when chat, channel and create-channel toggles are all off", async () => {
|
|
1352
|
+
const rule: WorkspaceNotificationRule = makeChatRule({
|
|
1353
|
+
shouldPostToExistingChat: false,
|
|
1354
|
+
shouldPostToExistingChannel: false,
|
|
1355
|
+
shouldCreateNewChannel: false,
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
const mocks: TestRuleMocks = mockTestRuleDeps({
|
|
1359
|
+
rule: rule,
|
|
1360
|
+
availableChats: { "chat-1": makeChat("chat-1") },
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
await runTestRule(rule);
|
|
1364
|
+
|
|
1365
|
+
expect(mocks.postSpy).not.toHaveBeenCalled();
|
|
1366
|
+
expect(mocks.createLogSpy).not.toHaveBeenCalled();
|
|
1367
|
+
});
|
|
1368
|
+
|
|
1369
|
+
test("Slack rules never enter the chat block even with the chat toggle on", async () => {
|
|
1370
|
+
const rule: WorkspaceNotificationRule = makeWorkspaceRule({
|
|
1371
|
+
workspaceType: WorkspaceType.Slack,
|
|
1372
|
+
notificationRule: makeBaseRule({
|
|
1373
|
+
shouldPostToExistingChat: true,
|
|
1374
|
+
existingChatIds: ["chat-1"],
|
|
1375
|
+
}),
|
|
1376
|
+
name: "Slack Rule",
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
const mocks: TestRuleMocks = mockTestRuleDeps({
|
|
1380
|
+
rule: rule,
|
|
1381
|
+
availableChats: {},
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
await runTestRule(rule);
|
|
1385
|
+
|
|
1386
|
+
expect(mocks.postSpy).not.toHaveBeenCalled();
|
|
1387
|
+
expect(mocks.getProjectAuthSpy).not.toHaveBeenCalled();
|
|
1388
|
+
expect(mocks.createLogSpy).not.toHaveBeenCalled();
|
|
1389
|
+
});
|
|
1390
|
+
});
|