@oneuptime/common 14.0.5 → 14.0.6
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/Server/Services/MetricService.ts +1 -1
- package/Server/Types/Database/QueryHelper.ts +54 -1
- package/Server/Utils/AI/Toolbox/MetricTools.ts +13 -2
- package/Server/Utils/AI/Toolbox/ResourceTools.ts +12 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +1 -1
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +33 -0
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +4 -3
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +3 -1
- package/Tests/App/Dashboard/NetworkTopologyExplorerSearch.test.tsx +1267 -0
- package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +4 -1
- package/Tests/App/Dashboard/RumOverviewPage.test.tsx +127 -0
- package/Tests/App/Dashboard/SiteSearchBox.test.tsx +2110 -0
- package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +168 -8
- package/Tests/Server/Services/MetricServiceAggregate.test.ts +12 -0
- package/Tests/Server/Services/SavedViewDefaults.test.ts +325 -0
- package/Tests/Server/Services/StatusPageCertificateService.test.ts +169 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +191 -0
- package/Tests/Server/Types/Database/QueryHelperSearchAllWords.test.ts +651 -0
- package/Tests/Server/Utils/AI/AlertWriteTools.test.ts +256 -0
- package/Tests/Server/Utils/AI/IncidentWriteTools.test.ts +482 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +54 -0
- package/Tests/Server/Utils/Monitor/MetricDecimalThreshold.test.ts +417 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +96 -0
- package/Tests/Types/BaseDatabase/AggregationType.test.ts +5 -1
- package/Tests/Types/Dashboard/KubernetesDashboardTemplate.test.ts +184 -0
- package/Tests/Types/Dashboard/KubernetesDashboardTemplates.test.ts +107 -0
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +8 -14
- package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +4 -3
- package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +27 -6
- package/Tests/UI/Rum/ChunkLoader.test.ts +76 -0
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +53 -0
- package/Tests/UI/Rum/ReplaySignals.test.ts +54 -0
- package/Tests/Utils/Dashboard/KubernetesClusterCpuTileRepair.test.ts +370 -0
- package/Tests/Utils/Rules/MonitorTypeRuleCriteria.test.ts +181 -0
- package/Types/BaseDatabase/AggregationType.ts +7 -0
- package/Types/Dashboard/DashboardTemplates.ts +20 -7
- package/Types/Rum/SessionReplayCustomEvents.ts +29 -3
- package/Types/Rum/WebVitals.ts +13 -0
- package/UI/Components/Input/Input.tsx +11 -0
- package/Utils/Dashboard/KubernetesClusterCpuTileRepair.ts +147 -0
- package/build/dist/Server/Services/MetricService.js +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +47 -1
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +13 -2
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +12 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +31 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Types/BaseDatabase/AggregationType.js +7 -0
- package/build/dist/Types/BaseDatabase/AggregationType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardTemplates.js +20 -7
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -1
- package/build/dist/Types/Rum/WebVitals.js +12 -0
- package/build/dist/Types/Rum/WebVitals.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js +110 -0
- package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import StatusPageCertificateService from "../../../Server/Services/StatusPageCertificateService";
|
|
2
|
+
import DatabaseConfig from "../../../Server/DatabaseConfig";
|
|
3
|
+
import { AppApiHostname } from "../../../Server/EnvironmentConfig";
|
|
4
|
+
import API from "../../../Utils/API";
|
|
5
|
+
import Protocol from "../../../Types/API/Protocol";
|
|
6
|
+
import URL from "../../../Types/API/URL";
|
|
7
|
+
import { afterEach, describe, expect, test } from "@jest/globals";
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Custom status page domains get their TLS certificate from the cert worker,
|
|
11
|
+
* and this service is the only caller. Nothing about it is visible until a
|
|
12
|
+
* customer's domain fails to serve HTTPS, so what these tests hold is the
|
|
13
|
+
* request itself: the verb, the worker route, the domain in the body, and the
|
|
14
|
+
* protocol taken from the instance's own configuration rather than assumed.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const WORKER_ROUTE: string = "/api/workers/cert";
|
|
18
|
+
const DOMAIN: string = "status.example.com";
|
|
19
|
+
|
|
20
|
+
type ApiCall = { url: URL; data: Record<string, unknown> };
|
|
21
|
+
|
|
22
|
+
function lastCall(spy: jest.SpyInstance): ApiCall {
|
|
23
|
+
return spy.mock.calls[spy.mock.calls.length - 1]?.[0] as ApiCall;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
jest.restoreAllMocks();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe.each([
|
|
31
|
+
{ label: "https", protocol: Protocol.HTTPS },
|
|
32
|
+
{ label: "http", protocol: Protocol.HTTP },
|
|
33
|
+
])(
|
|
34
|
+
"StatusPageCertificateService over $label",
|
|
35
|
+
({ protocol }: { protocol: Protocol }) => {
|
|
36
|
+
function mockProtocol(): jest.SpyInstance {
|
|
37
|
+
return jest
|
|
38
|
+
.spyOn(DatabaseConfig, "getHttpProtocol")
|
|
39
|
+
.mockResolvedValue(protocol as never);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
test("add posts the domain to the cert worker", async () => {
|
|
43
|
+
mockProtocol();
|
|
44
|
+
const postSpy: jest.SpyInstance = jest
|
|
45
|
+
.spyOn(API, "post")
|
|
46
|
+
.mockResolvedValue({} as never);
|
|
47
|
+
|
|
48
|
+
await StatusPageCertificateService.add(DOMAIN);
|
|
49
|
+
|
|
50
|
+
expect(postSpy).toHaveBeenCalledTimes(1);
|
|
51
|
+
const call: ApiCall = lastCall(postSpy);
|
|
52
|
+
expect(call.data).toEqual({ domain: DOMAIN });
|
|
53
|
+
expect(call.url.protocol).toBe(protocol);
|
|
54
|
+
expect(call.url.hostname.toString()).toBe(AppApiHostname.toString());
|
|
55
|
+
expect(call.url.route.toString()).toBe(WORKER_ROUTE);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("remove deletes the domain at the same route", async () => {
|
|
59
|
+
mockProtocol();
|
|
60
|
+
const deleteSpy: jest.SpyInstance = jest
|
|
61
|
+
.spyOn(API, "delete")
|
|
62
|
+
.mockResolvedValue({} as never);
|
|
63
|
+
|
|
64
|
+
await StatusPageCertificateService.remove(DOMAIN);
|
|
65
|
+
|
|
66
|
+
expect(deleteSpy).toHaveBeenCalledTimes(1);
|
|
67
|
+
const call: ApiCall = lastCall(deleteSpy);
|
|
68
|
+
expect(call.data).toEqual({ domain: DOMAIN });
|
|
69
|
+
expect(call.url.protocol).toBe(protocol);
|
|
70
|
+
expect(call.url.route.toString()).toBe(WORKER_ROUTE);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("get reads the domain's certificate from the same route", async () => {
|
|
74
|
+
mockProtocol();
|
|
75
|
+
const getSpy: jest.SpyInstance = jest
|
|
76
|
+
.spyOn(API, "get")
|
|
77
|
+
.mockResolvedValue({} as never);
|
|
78
|
+
|
|
79
|
+
await StatusPageCertificateService.get(DOMAIN);
|
|
80
|
+
|
|
81
|
+
expect(getSpy).toHaveBeenCalledTimes(1);
|
|
82
|
+
const call: ApiCall = lastCall(getSpy);
|
|
83
|
+
expect(call.data).toEqual({ domain: DOMAIN });
|
|
84
|
+
expect(call.url.protocol).toBe(protocol);
|
|
85
|
+
expect(call.url.route.toString()).toBe(WORKER_ROUTE);
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
describe("StatusPageCertificateService", () => {
|
|
91
|
+
test("each verb reads the protocol fresh rather than caching the first answer", async () => {
|
|
92
|
+
const protocolSpy: jest.SpyInstance = jest
|
|
93
|
+
.spyOn(DatabaseConfig, "getHttpProtocol")
|
|
94
|
+
.mockResolvedValueOnce(Protocol.HTTP as never)
|
|
95
|
+
.mockResolvedValueOnce(Protocol.HTTPS as never);
|
|
96
|
+
const postSpy: jest.SpyInstance = jest
|
|
97
|
+
.spyOn(API, "post")
|
|
98
|
+
.mockResolvedValue({} as never);
|
|
99
|
+
|
|
100
|
+
await StatusPageCertificateService.add(DOMAIN);
|
|
101
|
+
await StatusPageCertificateService.add(DOMAIN);
|
|
102
|
+
|
|
103
|
+
expect(protocolSpy).toHaveBeenCalledTimes(2);
|
|
104
|
+
expect((postSpy.mock.calls[0]?.[0] as ApiCall).url.protocol).toBe(
|
|
105
|
+
Protocol.HTTP,
|
|
106
|
+
);
|
|
107
|
+
expect((postSpy.mock.calls[1]?.[0] as ApiCall).url.protocol).toBe(
|
|
108
|
+
Protocol.HTTPS,
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("the three verbs do not call one another", async () => {
|
|
113
|
+
jest
|
|
114
|
+
.spyOn(DatabaseConfig, "getHttpProtocol")
|
|
115
|
+
.mockResolvedValue(Protocol.HTTPS as never);
|
|
116
|
+
const postSpy: jest.SpyInstance = jest
|
|
117
|
+
.spyOn(API, "post")
|
|
118
|
+
.mockResolvedValue({} as never);
|
|
119
|
+
const deleteSpy: jest.SpyInstance = jest
|
|
120
|
+
.spyOn(API, "delete")
|
|
121
|
+
.mockResolvedValue({} as never);
|
|
122
|
+
const getSpy: jest.SpyInstance = jest
|
|
123
|
+
.spyOn(API, "get")
|
|
124
|
+
.mockResolvedValue({} as never);
|
|
125
|
+
|
|
126
|
+
await StatusPageCertificateService.get(DOMAIN);
|
|
127
|
+
|
|
128
|
+
expect(getSpy).toHaveBeenCalledTimes(1);
|
|
129
|
+
expect(postSpy).not.toHaveBeenCalled();
|
|
130
|
+
expect(deleteSpy).not.toHaveBeenCalled();
|
|
131
|
+
|
|
132
|
+
await StatusPageCertificateService.remove(DOMAIN);
|
|
133
|
+
|
|
134
|
+
expect(deleteSpy).toHaveBeenCalledTimes(1);
|
|
135
|
+
expect(postSpy).not.toHaveBeenCalled();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("the domain is passed through untouched, punycode and all", async () => {
|
|
139
|
+
jest
|
|
140
|
+
.spyOn(DatabaseConfig, "getHttpProtocol")
|
|
141
|
+
.mockResolvedValue(Protocol.HTTPS as never);
|
|
142
|
+
const postSpy: jest.SpyInstance = jest
|
|
143
|
+
.spyOn(API, "post")
|
|
144
|
+
.mockResolvedValue({} as never);
|
|
145
|
+
|
|
146
|
+
for (const domain of [
|
|
147
|
+
"status.example.com",
|
|
148
|
+
"xn--80ak6aa92e.example.com",
|
|
149
|
+
"STATUS.EXAMPLE.COM",
|
|
150
|
+
"status.example.co.uk",
|
|
151
|
+
]) {
|
|
152
|
+
await StatusPageCertificateService.add(domain);
|
|
153
|
+
expect(lastCall(postSpy).data).toEqual({ domain: domain });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("a failing worker call is surfaced, not swallowed", async () => {
|
|
158
|
+
jest
|
|
159
|
+
.spyOn(DatabaseConfig, "getHttpProtocol")
|
|
160
|
+
.mockResolvedValue(Protocol.HTTPS as never);
|
|
161
|
+
jest
|
|
162
|
+
.spyOn(API, "post")
|
|
163
|
+
.mockRejectedValue(new Error("cert worker unreachable") as never);
|
|
164
|
+
|
|
165
|
+
await expect(StatusPageCertificateService.add(DOMAIN)).rejects.toThrow(
|
|
166
|
+
"cert worker unreachable",
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import TeamComplianceSettingService from "../../../Server/Services/TeamComplianceSettingService";
|
|
2
|
+
import CreateBy from "../../../Server/Types/Database/CreateBy";
|
|
3
|
+
import { OnCreate } from "../../../Server/Types/Database/Hooks";
|
|
4
|
+
import TeamComplianceSetting from "../../../Models/DatabaseModels/TeamComplianceSetting";
|
|
5
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
6
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
7
|
+
import { afterEach, describe, expect, test } from "@jest/globals";
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* A team may hold at most one compliance setting per rule type: two rows for
|
|
11
|
+
* the same rule would leave the compliance check with no defined answer. The
|
|
12
|
+
* guard lives in onBeforeCreate, and these tests hold it to refusing loudly
|
|
13
|
+
* (BadDataException naming the rule type), to scoping the duplicate lookup by
|
|
14
|
+
* team *and* rule type -- so the same rule on another team is still allowed --
|
|
15
|
+
* and to running that lookup as root, since the row that already exists may be
|
|
16
|
+
* one the caller cannot see.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
type OnBeforeCreateFunction = (
|
|
20
|
+
createBy: CreateBy<TeamComplianceSetting>,
|
|
21
|
+
) => Promise<OnCreate<TeamComplianceSetting>>;
|
|
22
|
+
|
|
23
|
+
const TEAM_ID: ObjectID = new ObjectID("11111111-1111-4111-8111-111111111111");
|
|
24
|
+
const OTHER_TEAM_ID: ObjectID = new ObjectID(
|
|
25
|
+
"22222222-2222-4222-8222-222222222222",
|
|
26
|
+
);
|
|
27
|
+
const RULE_TYPE: string = "RequireTwoFactorAuth";
|
|
28
|
+
|
|
29
|
+
function onBeforeCreate(
|
|
30
|
+
createBy: CreateBy<TeamComplianceSetting>,
|
|
31
|
+
): Promise<OnCreate<TeamComplianceSetting>> {
|
|
32
|
+
return (
|
|
33
|
+
TeamComplianceSettingService as unknown as {
|
|
34
|
+
onBeforeCreate: OnBeforeCreateFunction;
|
|
35
|
+
}
|
|
36
|
+
).onBeforeCreate(createBy);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function buildCreateBy(data: {
|
|
40
|
+
teamId?: ObjectID;
|
|
41
|
+
ruleType?: string;
|
|
42
|
+
}): CreateBy<TeamComplianceSetting> {
|
|
43
|
+
const setting: TeamComplianceSetting = new TeamComplianceSetting();
|
|
44
|
+
if (data.teamId) {
|
|
45
|
+
setting.teamId = data.teamId;
|
|
46
|
+
}
|
|
47
|
+
if (data.ruleType) {
|
|
48
|
+
(setting as unknown as Record<string, unknown>)["ruleType"] = data.ruleType;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
data: setting,
|
|
52
|
+
props: { isRoot: true },
|
|
53
|
+
} as CreateBy<TeamComplianceSetting>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function mockExisting(
|
|
57
|
+
existing: TeamComplianceSetting | null,
|
|
58
|
+
): jest.SpyInstance {
|
|
59
|
+
return jest
|
|
60
|
+
.spyOn(TeamComplianceSettingService, "findOneBy")
|
|
61
|
+
.mockResolvedValue(existing as never);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
jest.restoreAllMocks();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("TeamComplianceSettingService onBeforeCreate", () => {
|
|
69
|
+
test("allows the first setting for a team and rule type", async () => {
|
|
70
|
+
mockExisting(null);
|
|
71
|
+
|
|
72
|
+
const createBy: CreateBy<TeamComplianceSetting> = buildCreateBy({
|
|
73
|
+
teamId: TEAM_ID,
|
|
74
|
+
ruleType: RULE_TYPE,
|
|
75
|
+
});
|
|
76
|
+
const result: OnCreate<TeamComplianceSetting> =
|
|
77
|
+
await onBeforeCreate(createBy);
|
|
78
|
+
|
|
79
|
+
expect(result.createBy).toBe(createBy);
|
|
80
|
+
expect(result.carryForward).toBeNull();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("refuses a second setting for the same team and rule type", async () => {
|
|
84
|
+
const existing: TeamComplianceSetting = new TeamComplianceSetting();
|
|
85
|
+
existing._id = "33333333-3333-4333-8333-333333333333";
|
|
86
|
+
mockExisting(existing);
|
|
87
|
+
|
|
88
|
+
await expect(
|
|
89
|
+
onBeforeCreate(buildCreateBy({ teamId: TEAM_ID, ruleType: RULE_TYPE })),
|
|
90
|
+
).rejects.toThrow(BadDataException);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("the refusal names the rule type, so the form can point at it", async () => {
|
|
94
|
+
const existing: TeamComplianceSetting = new TeamComplianceSetting();
|
|
95
|
+
existing._id = "33333333-3333-4333-8333-333333333333";
|
|
96
|
+
mockExisting(existing);
|
|
97
|
+
|
|
98
|
+
await expect(
|
|
99
|
+
onBeforeCreate(buildCreateBy({ teamId: TEAM_ID, ruleType: RULE_TYPE })),
|
|
100
|
+
).rejects.toThrow(
|
|
101
|
+
`A compliance setting for rule type "${RULE_TYPE}" already exists for this team.`,
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("the duplicate lookup is keyed on both the team and the rule type", async () => {
|
|
106
|
+
const findSpy: jest.SpyInstance = mockExisting(null);
|
|
107
|
+
|
|
108
|
+
await onBeforeCreate(
|
|
109
|
+
buildCreateBy({ teamId: TEAM_ID, ruleType: RULE_TYPE }),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const findArgs: Record<string, unknown> = findSpy.mock
|
|
113
|
+
.calls[0]?.[0] as Record<string, unknown>;
|
|
114
|
+
expect(findArgs["query"]).toEqual({
|
|
115
|
+
teamId: TEAM_ID,
|
|
116
|
+
ruleType: RULE_TYPE,
|
|
117
|
+
});
|
|
118
|
+
// Only the id is needed to know whether one exists.
|
|
119
|
+
expect(findArgs["select"]).toEqual({ _id: true });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("the lookup runs as root: the existing row may be invisible to the caller", async () => {
|
|
123
|
+
const findSpy: jest.SpyInstance = mockExisting(null);
|
|
124
|
+
|
|
125
|
+
await onBeforeCreate(
|
|
126
|
+
buildCreateBy({ teamId: TEAM_ID, ruleType: RULE_TYPE }),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const findArgs: Record<string, unknown> = findSpy.mock
|
|
130
|
+
.calls[0]?.[0] as Record<string, unknown>;
|
|
131
|
+
expect(findArgs["props"]).toEqual({ isRoot: true });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("the same rule type on a different team is a different row", async () => {
|
|
135
|
+
const findSpy: jest.SpyInstance = jest
|
|
136
|
+
.spyOn(TeamComplianceSettingService, "findOneBy")
|
|
137
|
+
.mockImplementation(((
|
|
138
|
+
args: Record<string, unknown>,
|
|
139
|
+
): Promise<unknown> => {
|
|
140
|
+
const query: Record<string, unknown> = args["query"] as Record<
|
|
141
|
+
string,
|
|
142
|
+
unknown
|
|
143
|
+
>;
|
|
144
|
+
// Only the first team already holds this rule.
|
|
145
|
+
if (query["teamId"] === TEAM_ID) {
|
|
146
|
+
const existing: TeamComplianceSetting = new TeamComplianceSetting();
|
|
147
|
+
existing._id = "33333333-3333-4333-8333-333333333333";
|
|
148
|
+
return Promise.resolve(existing);
|
|
149
|
+
}
|
|
150
|
+
return Promise.resolve(null);
|
|
151
|
+
}) as never);
|
|
152
|
+
|
|
153
|
+
await expect(
|
|
154
|
+
onBeforeCreate(buildCreateBy({ teamId: TEAM_ID, ruleType: RULE_TYPE })),
|
|
155
|
+
).rejects.toThrow(BadDataException);
|
|
156
|
+
|
|
157
|
+
await expect(
|
|
158
|
+
onBeforeCreate(
|
|
159
|
+
buildCreateBy({ teamId: OTHER_TEAM_ID, ruleType: RULE_TYPE }),
|
|
160
|
+
),
|
|
161
|
+
).resolves.toBeDefined();
|
|
162
|
+
|
|
163
|
+
expect(findSpy).toHaveBeenCalledTimes(2);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("a different rule type on the same team is allowed", async () => {
|
|
167
|
+
const findSpy: jest.SpyInstance = jest
|
|
168
|
+
.spyOn(TeamComplianceSettingService, "findOneBy")
|
|
169
|
+
.mockImplementation(((
|
|
170
|
+
args: Record<string, unknown>,
|
|
171
|
+
): Promise<unknown> => {
|
|
172
|
+
const query: Record<string, unknown> = args["query"] as Record<
|
|
173
|
+
string,
|
|
174
|
+
unknown
|
|
175
|
+
>;
|
|
176
|
+
if (query["ruleType"] === RULE_TYPE) {
|
|
177
|
+
const existing: TeamComplianceSetting = new TeamComplianceSetting();
|
|
178
|
+
existing._id = "33333333-3333-4333-8333-333333333333";
|
|
179
|
+
return Promise.resolve(existing);
|
|
180
|
+
}
|
|
181
|
+
return Promise.resolve(null);
|
|
182
|
+
}) as never);
|
|
183
|
+
|
|
184
|
+
await expect(
|
|
185
|
+
onBeforeCreate(
|
|
186
|
+
buildCreateBy({ teamId: TEAM_ID, ruleType: "RequirePhoneNumber" }),
|
|
187
|
+
),
|
|
188
|
+
).resolves.toBeDefined();
|
|
189
|
+
expect(findSpy).toHaveBeenCalledTimes(1);
|
|
190
|
+
});
|
|
191
|
+
});
|