@stamhoofd/backend 2.79.8 → 2.80.1
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/package.json +11 -11
- package/src/audit-logs/ModelLogger.ts +2 -2
- package/src/crons/amazon-ses.ts +207 -229
- package/src/crons/clearExcelCache.test.ts +1 -1
- package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +105 -0
- package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +6 -10
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +1 -1
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +997 -0
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +19 -3
- package/src/endpoints/global/members/GetMembersEndpoint.ts +7 -7
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +10 -3
- package/src/helpers/AdminPermissionChecker.ts +3 -3
- package/src/helpers/ForwardHandler.ts +3 -2
- package/src/helpers/MemberCharger.ts +39 -0
- package/src/helpers/OrganizationCharger.ts +9 -20
- package/src/services/EventNotificationService.ts +3 -0
- package/tests/e2e/charge-members.test.ts +429 -0
- package/tests/jest.setup.ts +7 -1
- package/tests/toMatchMap.ts +68 -0
- package/src/services/diff.ts +0 -514
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import { Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { GroupFactory, MemberFactory, Organization, OrganizationFactory, RegistrationFactory, RegistrationPeriod, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
3
|
+
import { AccessRight, BalanceItemWithPayments, ChargeMembersRequest, LimitedFilteredRequest, PermissionLevel, PermissionRoleDetailed, Permissions, PermissionsResourceType, ResourcePermissions, StamhoofdFilter, Version } from '@stamhoofd/structures';
|
|
4
|
+
import { TestUtils } from '@stamhoofd/test-utils';
|
|
5
|
+
import { ChargeMembersEndpoint } from '../../src/endpoints/admin/members/ChargeMembersEndpoint';
|
|
6
|
+
import { GetMemberBalanceEndpoint } from '../../src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint';
|
|
7
|
+
import { testServer } from '../helpers/TestServer';
|
|
8
|
+
|
|
9
|
+
describe('E2E.ChargeMembers', () => {
|
|
10
|
+
const chargeMembersEndpoint = new ChargeMembersEndpoint();
|
|
11
|
+
const memberBalanceEndpoint = new GetMemberBalanceEndpoint();
|
|
12
|
+
let period: RegistrationPeriod;
|
|
13
|
+
let organization: Organization;
|
|
14
|
+
let otherOrganization: Organization;
|
|
15
|
+
let financialDirectorToken: Token;
|
|
16
|
+
let financialDirectorRole: PermissionRoleDetailed;
|
|
17
|
+
let financialDirectorRoleOfOtherOrganization: PermissionRoleDetailed;
|
|
18
|
+
|
|
19
|
+
const postCharge = async (filter: StamhoofdFilter, organization: Organization, body: ChargeMembersRequest, token: Token) => {
|
|
20
|
+
const request = Request.buildJson('POST', `/v${Version}/admin/charge-members`, organization.getApiHost(), body);
|
|
21
|
+
const filterRequest = new LimitedFilteredRequest({
|
|
22
|
+
filter,
|
|
23
|
+
limit: 100,
|
|
24
|
+
});
|
|
25
|
+
request.query = filterRequest.encode({ version: Version }) as any;
|
|
26
|
+
request.headers.authorization = 'Bearer ' + token.accessToken;
|
|
27
|
+
return await testServer.test(chargeMembersEndpoint, request);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const getBalance = async (memberId: string, organization: Organization, token: Token): Promise<Response<BalanceItemWithPayments[]>> => {
|
|
31
|
+
const request = Request.buildJson('GET', `/v${Version}/organization/members/${memberId}/balance`, organization.getApiHost());
|
|
32
|
+
request.headers.authorization = 'Bearer ' + token.accessToken;
|
|
33
|
+
return await testServer.test(memberBalanceEndpoint, request);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const createUserData = async (permissions: Permissions | null | undefined, roles: PermissionRoleDetailed[]) => {
|
|
37
|
+
const organization = await new OrganizationFactory({ period, roles })
|
|
38
|
+
.create();
|
|
39
|
+
|
|
40
|
+
const user = await new UserFactory({
|
|
41
|
+
organization,
|
|
42
|
+
permissions,
|
|
43
|
+
})
|
|
44
|
+
.create();
|
|
45
|
+
|
|
46
|
+
const token = await Token.createToken(user);
|
|
47
|
+
|
|
48
|
+
return { organization, user, token };
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const createFinancialDirectorData = async () => {
|
|
52
|
+
const role = PermissionRoleDetailed.create({
|
|
53
|
+
name: 'financial director',
|
|
54
|
+
accessRights: [AccessRight.OrganizationFinanceDirector],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const { organization, user: financialDirector, token } = await createUserData(Permissions.create({
|
|
58
|
+
level: PermissionLevel.None,
|
|
59
|
+
roles: [
|
|
60
|
+
role,
|
|
61
|
+
],
|
|
62
|
+
resources: new Map([[PermissionsResourceType.Groups, new Map([[
|
|
63
|
+
'',
|
|
64
|
+
ResourcePermissions.create({
|
|
65
|
+
level: PermissionLevel.Write,
|
|
66
|
+
}),
|
|
67
|
+
]])]]),
|
|
68
|
+
}), [role]);
|
|
69
|
+
|
|
70
|
+
return { role, organization, financialDirector, token };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
beforeEach(async () => {
|
|
74
|
+
TestUtils.setEnvironment('userMode', 'platform');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
beforeAll(async () => {
|
|
78
|
+
period = await new RegistrationPeriodFactory({
|
|
79
|
+
startDate: new Date(2023, 0, 1),
|
|
80
|
+
endDate: new Date(2023, 11, 31),
|
|
81
|
+
}).create();
|
|
82
|
+
|
|
83
|
+
const financialDirectorData = await createFinancialDirectorData();
|
|
84
|
+
financialDirectorRole = financialDirectorData.role;
|
|
85
|
+
financialDirectorToken = financialDirectorData.token;
|
|
86
|
+
organization = financialDirectorData.organization;
|
|
87
|
+
|
|
88
|
+
const otherFinancialDirectorData = await createFinancialDirectorData();
|
|
89
|
+
otherOrganization = otherFinancialDirectorData.organization;
|
|
90
|
+
financialDirectorRoleOfOtherOrganization = otherFinancialDirectorData.role;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('Should fail if user does can not manage the payments of the organization', async () => {
|
|
94
|
+
// arrange
|
|
95
|
+
const member1 = await new MemberFactory({ }).create();
|
|
96
|
+
const member2 = await new MemberFactory({ }).create();
|
|
97
|
+
|
|
98
|
+
await new RegistrationFactory({ member: member1, organization }).create();
|
|
99
|
+
await new RegistrationFactory({ member: member2, organization }).create();
|
|
100
|
+
|
|
101
|
+
const filter: StamhoofdFilter = {
|
|
102
|
+
id: {
|
|
103
|
+
$in: [member1.id, member2.id],
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const body = ChargeMembersRequest.create({
|
|
108
|
+
description: 'test description',
|
|
109
|
+
price: 3,
|
|
110
|
+
amount: 4,
|
|
111
|
+
dueAt: new Date(2023, 0, 10),
|
|
112
|
+
createdAt: new Date(2023, 0, 4),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const testUserFactories: UserFactory[] = [
|
|
116
|
+
new UserFactory({
|
|
117
|
+
organization,
|
|
118
|
+
permissions: Permissions.create({
|
|
119
|
+
level: PermissionLevel.None,
|
|
120
|
+
roles: [
|
|
121
|
+
financialDirectorRole,
|
|
122
|
+
],
|
|
123
|
+
resources: new Map([[PermissionsResourceType.Groups, new Map([[
|
|
124
|
+
'',
|
|
125
|
+
ResourcePermissions.create({
|
|
126
|
+
level: PermissionLevel.Read,
|
|
127
|
+
}),
|
|
128
|
+
]])]]),
|
|
129
|
+
}),
|
|
130
|
+
}),
|
|
131
|
+
new UserFactory({
|
|
132
|
+
organization,
|
|
133
|
+
permissions: Permissions.create({
|
|
134
|
+
level: PermissionLevel.None,
|
|
135
|
+
resources: new Map([[PermissionsResourceType.Groups, new Map([[
|
|
136
|
+
'',
|
|
137
|
+
ResourcePermissions.create({
|
|
138
|
+
level: PermissionLevel.Write,
|
|
139
|
+
}),
|
|
140
|
+
]])]]),
|
|
141
|
+
}),
|
|
142
|
+
}),
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
for (const userFactory of testUserFactories) {
|
|
146
|
+
const user = await userFactory.create();
|
|
147
|
+
|
|
148
|
+
const token = await Token.createToken(user);
|
|
149
|
+
|
|
150
|
+
await expect(async () => await postCharge(filter, organization, body, token))
|
|
151
|
+
.rejects
|
|
152
|
+
.toThrow('You do not have permissions for this action');
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('Should create balance items for members', async () => {
|
|
157
|
+
// arrange
|
|
158
|
+
const member1 = await new MemberFactory({ }).create();
|
|
159
|
+
|
|
160
|
+
const member2 = await new MemberFactory({ }).create();
|
|
161
|
+
|
|
162
|
+
await new RegistrationFactory({ member: member1, organization }).create();
|
|
163
|
+
await new RegistrationFactory({ member: member2, organization }).create();
|
|
164
|
+
|
|
165
|
+
const filter: StamhoofdFilter = {
|
|
166
|
+
id: {
|
|
167
|
+
$in: [member1.id, member2.id],
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const body = ChargeMembersRequest.create({
|
|
172
|
+
description: 'test description',
|
|
173
|
+
price: 3,
|
|
174
|
+
amount: 4,
|
|
175
|
+
dueAt: new Date(2023, 0, 10),
|
|
176
|
+
createdAt: new Date(2023, 0, 4),
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const result = await postCharge(filter, organization, body, financialDirectorToken);
|
|
180
|
+
expect(result).toBeDefined();
|
|
181
|
+
expect(result.body).toBeUndefined();
|
|
182
|
+
|
|
183
|
+
// act and assert
|
|
184
|
+
const testBalanceResponse = (response: Response<BalanceItemWithPayments[]>) => {
|
|
185
|
+
expect(response).toBeDefined();
|
|
186
|
+
expect(response.body.length).toBe(1);
|
|
187
|
+
const balanceItem1 = response.body[0];
|
|
188
|
+
expect(balanceItem1.price).toEqual(12);
|
|
189
|
+
expect(balanceItem1.amount).toEqual(body.amount);
|
|
190
|
+
expect(balanceItem1.description).toEqual(body.description);
|
|
191
|
+
expect(balanceItem1.organizationId).toEqual(organization.id);
|
|
192
|
+
// const dueAt = balanceItem1.dueAt!;
|
|
193
|
+
expect(balanceItem1.dueAt).toEqual(body.dueAt);
|
|
194
|
+
expect(balanceItem1.createdAt).toEqual(body.createdAt);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
testBalanceResponse(await getBalance(member1.id, organization, financialDirectorToken));
|
|
198
|
+
testBalanceResponse(await getBalance(member2.id, organization, financialDirectorToken));
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('Should not charge members of other organization', async () => {
|
|
202
|
+
// arrange
|
|
203
|
+
const member1 = await new MemberFactory({ }).create();
|
|
204
|
+
|
|
205
|
+
const member2 = await new MemberFactory({ }).create();
|
|
206
|
+
|
|
207
|
+
await new RegistrationFactory({ member: member1, organization: otherOrganization }).create();
|
|
208
|
+
await new RegistrationFactory({ member: member2, organization: otherOrganization }).create();
|
|
209
|
+
|
|
210
|
+
const filter: StamhoofdFilter = {
|
|
211
|
+
id: {
|
|
212
|
+
$in: [member1.id, member2.id],
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const otherFinancialDirector = await new UserFactory({
|
|
217
|
+
organization: otherOrganization,
|
|
218
|
+
permissions: Permissions.create({
|
|
219
|
+
level: PermissionLevel.None,
|
|
220
|
+
roles: [
|
|
221
|
+
financialDirectorRoleOfOtherOrganization,
|
|
222
|
+
],
|
|
223
|
+
resources: new Map([[PermissionsResourceType.Groups, new Map([[
|
|
224
|
+
'',
|
|
225
|
+
ResourcePermissions.create({
|
|
226
|
+
level: PermissionLevel.Write,
|
|
227
|
+
}),
|
|
228
|
+
]])]]),
|
|
229
|
+
}),
|
|
230
|
+
})
|
|
231
|
+
.create();
|
|
232
|
+
|
|
233
|
+
const otherFinancialDirectorToken = await Token.createToken(otherFinancialDirector);
|
|
234
|
+
|
|
235
|
+
const body = ChargeMembersRequest.create({
|
|
236
|
+
description: 'test description',
|
|
237
|
+
price: 3,
|
|
238
|
+
amount: 4,
|
|
239
|
+
dueAt: new Date(2023, 0, 10),
|
|
240
|
+
createdAt: new Date(2023, 0, 4),
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// act and assert
|
|
244
|
+
const result = await postCharge(filter, organization, body, financialDirectorToken);
|
|
245
|
+
expect(result).toBeDefined();
|
|
246
|
+
expect(result.body).toBeUndefined();
|
|
247
|
+
|
|
248
|
+
const testBalanceResponse = (response: Response<BalanceItemWithPayments[]>) => {
|
|
249
|
+
expect(response).toBeDefined();
|
|
250
|
+
expect(response.body.length).toBe(0);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
testBalanceResponse(await getBalance(member1.id, otherOrganization, otherFinancialDirectorToken));
|
|
254
|
+
testBalanceResponse(await getBalance(member2.id, otherOrganization, otherFinancialDirectorToken));
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test('Should fail if invalid request', async () => {
|
|
258
|
+
// arrange
|
|
259
|
+
const member1 = await new MemberFactory({ }).create();
|
|
260
|
+
|
|
261
|
+
const member2 = await new MemberFactory({ }).create();
|
|
262
|
+
|
|
263
|
+
await new RegistrationFactory({ member: member1, organization }).create();
|
|
264
|
+
await new RegistrationFactory({ member: member2, organization }).create();
|
|
265
|
+
|
|
266
|
+
const filter: StamhoofdFilter = {
|
|
267
|
+
id: {
|
|
268
|
+
$in: [member1.id, member2.id],
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const testCases: [body: ChargeMembersRequest, expectedErrorMessage: string][] = [
|
|
273
|
+
// empty description
|
|
274
|
+
[ChargeMembersRequest.create({
|
|
275
|
+
description: ' ',
|
|
276
|
+
price: 3,
|
|
277
|
+
amount: 4,
|
|
278
|
+
dueAt: new Date(2023, 0, 10),
|
|
279
|
+
createdAt: new Date(2023, 0, 4),
|
|
280
|
+
}), 'Invalid description'],
|
|
281
|
+
|
|
282
|
+
// price 0
|
|
283
|
+
[ChargeMembersRequest.create({
|
|
284
|
+
description: 'test description',
|
|
285
|
+
price: 0,
|
|
286
|
+
amount: 4,
|
|
287
|
+
dueAt: new Date(2023, 0, 10),
|
|
288
|
+
createdAt: new Date(2023, 0, 4),
|
|
289
|
+
}), 'Invalid price'],
|
|
290
|
+
|
|
291
|
+
// amount 0
|
|
292
|
+
[ChargeMembersRequest.create({
|
|
293
|
+
description: 'test description',
|
|
294
|
+
price: 3,
|
|
295
|
+
amount: 0,
|
|
296
|
+
dueAt: new Date(2023, 0, 10),
|
|
297
|
+
createdAt: new Date(2023, 0, 4),
|
|
298
|
+
}), 'Invalid amount'],
|
|
299
|
+
];
|
|
300
|
+
|
|
301
|
+
// act and assert
|
|
302
|
+
for (const [body, expectedErrorMessage] of testCases) {
|
|
303
|
+
await expect(async () => await postCharge(filter, organization, body, financialDirectorToken))
|
|
304
|
+
.rejects
|
|
305
|
+
.toThrow(expectedErrorMessage);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
describe('Access for single group', () => {
|
|
310
|
+
test('Should create balance items for members if can manage payments of group', async () => {
|
|
311
|
+
// arrange
|
|
312
|
+
const role = PermissionRoleDetailed.create({
|
|
313
|
+
name: 'financial director',
|
|
314
|
+
accessRights: [AccessRight.OrganizationFinanceDirector],
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const resources = new Map();
|
|
318
|
+
|
|
319
|
+
const { organization, token, user } = await createUserData(Permissions.create({
|
|
320
|
+
level: PermissionLevel.None,
|
|
321
|
+
roles: [
|
|
322
|
+
role,
|
|
323
|
+
],
|
|
324
|
+
resources,
|
|
325
|
+
}), [role]);
|
|
326
|
+
|
|
327
|
+
const member1 = await new MemberFactory({ }).create();
|
|
328
|
+
|
|
329
|
+
const member2 = await new MemberFactory({ }).create();
|
|
330
|
+
|
|
331
|
+
const group = await new GroupFactory({ organization, period }).create();
|
|
332
|
+
|
|
333
|
+
resources.set(PermissionsResourceType.Groups, new Map([[group.id, ResourcePermissions.create({
|
|
334
|
+
level: PermissionLevel.Write,
|
|
335
|
+
})]]));
|
|
336
|
+
|
|
337
|
+
await user.save();
|
|
338
|
+
|
|
339
|
+
await new RegistrationFactory({ member: member1, group }).create();
|
|
340
|
+
await new RegistrationFactory({ member: member2, group }).create();
|
|
341
|
+
|
|
342
|
+
const filter: StamhoofdFilter = {
|
|
343
|
+
id: {
|
|
344
|
+
$in: [member1.id, member2.id],
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const body = ChargeMembersRequest.create({
|
|
349
|
+
description: 'test description',
|
|
350
|
+
price: 3,
|
|
351
|
+
amount: 4,
|
|
352
|
+
dueAt: new Date(2023, 0, 10),
|
|
353
|
+
createdAt: new Date(2023, 0, 4),
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
const result = await postCharge(filter, organization, body, token);
|
|
357
|
+
expect(result).toBeDefined();
|
|
358
|
+
expect(result.body).toBeUndefined();
|
|
359
|
+
|
|
360
|
+
// act and assert
|
|
361
|
+
const testBalanceResponse = (response: Response<BalanceItemWithPayments[]>) => {
|
|
362
|
+
expect(response).toBeDefined();
|
|
363
|
+
expect(response.body.length).toBe(1);
|
|
364
|
+
const balanceItem1 = response.body[0];
|
|
365
|
+
expect(balanceItem1.price).toEqual(12);
|
|
366
|
+
expect(balanceItem1.amount).toEqual(body.amount);
|
|
367
|
+
expect(balanceItem1.description).toEqual(body.description);
|
|
368
|
+
expect(balanceItem1.organizationId).toEqual(organization.id);
|
|
369
|
+
// const dueAt = balanceItem1.dueAt!;
|
|
370
|
+
expect(balanceItem1.dueAt).toEqual(body.dueAt);
|
|
371
|
+
expect(balanceItem1.createdAt).toEqual(body.createdAt);
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
testBalanceResponse(await getBalance(member1.id, organization, token));
|
|
375
|
+
testBalanceResponse(await getBalance(member2.id, organization, token));
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test('Should fail if no write access for group', async () => {
|
|
379
|
+
// arrange
|
|
380
|
+
const role = PermissionRoleDetailed.create({
|
|
381
|
+
name: 'financial director',
|
|
382
|
+
accessRights: [AccessRight.OrganizationFinanceDirector],
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
const resources = new Map();
|
|
386
|
+
|
|
387
|
+
const { organization, token, user } = await createUserData(Permissions.create({
|
|
388
|
+
level: PermissionLevel.None,
|
|
389
|
+
roles: [
|
|
390
|
+
role,
|
|
391
|
+
],
|
|
392
|
+
resources,
|
|
393
|
+
}), [role]);
|
|
394
|
+
|
|
395
|
+
const member1 = await new MemberFactory({ }).create();
|
|
396
|
+
|
|
397
|
+
const member2 = await new MemberFactory({ }).create();
|
|
398
|
+
|
|
399
|
+
const group = await new GroupFactory({ organization, period }).create();
|
|
400
|
+
|
|
401
|
+
resources.set(PermissionsResourceType.Groups, new Map([[group.id, ResourcePermissions.create({
|
|
402
|
+
level: PermissionLevel.Read,
|
|
403
|
+
})]]));
|
|
404
|
+
|
|
405
|
+
await user.save();
|
|
406
|
+
|
|
407
|
+
await new RegistrationFactory({ member: member1, group }).create();
|
|
408
|
+
await new RegistrationFactory({ member: member2, group }).create();
|
|
409
|
+
|
|
410
|
+
const filter: StamhoofdFilter = {
|
|
411
|
+
id: {
|
|
412
|
+
$in: [member1.id, member2.id],
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const body = ChargeMembersRequest.create({
|
|
417
|
+
description: 'test description',
|
|
418
|
+
price: 3,
|
|
419
|
+
amount: 4,
|
|
420
|
+
dueAt: new Date(2023, 0, 10),
|
|
421
|
+
createdAt: new Date(2023, 0, 4),
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
await expect(async () => await postCharge(filter, organization, body, token))
|
|
425
|
+
.rejects
|
|
426
|
+
.toThrow('You do not have permissions for this action');
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
});
|
package/tests/jest.setup.ts
CHANGED
|
@@ -3,13 +3,14 @@ backendEnv.load({ path: __dirname + '/../../.env.test.json' });
|
|
|
3
3
|
|
|
4
4
|
import { Column, Database } from '@simonbackx/simple-database';
|
|
5
5
|
import { Request } from '@simonbackx/simple-endpoints';
|
|
6
|
-
import { Email } from '@stamhoofd/email';
|
|
6
|
+
import { Email, EmailMocker } from '@stamhoofd/email';
|
|
7
7
|
import { Version } from '@stamhoofd/structures';
|
|
8
8
|
import { sleep } from '@stamhoofd/utility';
|
|
9
9
|
import nock from 'nock';
|
|
10
10
|
import { GlobalHelper } from '../src/helpers/GlobalHelper';
|
|
11
11
|
import * as jose from 'jose';
|
|
12
12
|
import { TestUtils } from '@stamhoofd/test-utils';
|
|
13
|
+
import './toMatchMap';
|
|
13
14
|
|
|
14
15
|
// Set version of saved structures
|
|
15
16
|
Column.setJSONVersion(Version);
|
|
@@ -43,6 +44,7 @@ beforeAll(async () => {
|
|
|
43
44
|
await Database.delete('DELETE FROM `provinces`');
|
|
44
45
|
await Database.delete('DELETE FROM `email_recipients`');
|
|
45
46
|
await Database.delete('DELETE FROM `emails`');
|
|
47
|
+
await Database.delete('DELETE FROM `email_templates`');
|
|
46
48
|
|
|
47
49
|
await Database.delete('DELETE FROM `webshop_orders`');
|
|
48
50
|
await Database.delete('DELETE FROM `webshops`');
|
|
@@ -67,6 +69,9 @@ beforeAll(async () => {
|
|
|
67
69
|
TestUtils.setPermanentEnvironment('FILE_SIGNING_PRIVATE_KEY', exportedPrivateKey);
|
|
68
70
|
|
|
69
71
|
await GlobalHelper.load();
|
|
72
|
+
|
|
73
|
+
// Override default $t handlers
|
|
74
|
+
TestUtils.loadEnvironment();
|
|
70
75
|
});
|
|
71
76
|
|
|
72
77
|
afterAll(async () => {
|
|
@@ -79,3 +84,4 @@ afterAll(async () => {
|
|
|
79
84
|
});
|
|
80
85
|
|
|
81
86
|
TestUtils.setup();
|
|
87
|
+
EmailMocker.infect();
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { expect } from '@jest/globals';
|
|
2
|
+
import type { MatcherFunction } from 'expect';
|
|
3
|
+
import 'jest';
|
|
4
|
+
|
|
5
|
+
const toMatchMap: MatcherFunction<[map: unknown]> = function (actual, map: Map<any, any>) {
|
|
6
|
+
if (
|
|
7
|
+
!(map instanceof Map)
|
|
8
|
+
) {
|
|
9
|
+
throw new TypeError('You need to pass a Map to toMatchMap');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (
|
|
13
|
+
!(actual instanceof Map)
|
|
14
|
+
) {
|
|
15
|
+
return {
|
|
16
|
+
message: () =>
|
|
17
|
+
`expected ${this.utils.printReceived(
|
|
18
|
+
actual,
|
|
19
|
+
)} to be a Map`,
|
|
20
|
+
pass: false,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (const key of map.keys()) {
|
|
25
|
+
// Check key exists
|
|
26
|
+
if (!actual.has(key)) {
|
|
27
|
+
return {
|
|
28
|
+
message: () =>
|
|
29
|
+
`expected ${this.utils.printReceived(
|
|
30
|
+
actual,
|
|
31
|
+
)} to have key ${this.utils.printExpected(key)}`,
|
|
32
|
+
pass: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Compare values
|
|
37
|
+
const expectedValue = map.get(key);
|
|
38
|
+
const actualValue = actual.get(key);
|
|
39
|
+
|
|
40
|
+
if (!this.equals(expectedValue, actualValue)) {
|
|
41
|
+
return {
|
|
42
|
+
message: () =>
|
|
43
|
+
`expected ${this.utils.diff(actualValue, expectedValue)} at key ${this.utils.printExpected(key)}`,
|
|
44
|
+
pass: false,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check for extra keys in actual
|
|
50
|
+
for (const key of actual.keys()) {
|
|
51
|
+
if (!map.has(key)) {
|
|
52
|
+
return {
|
|
53
|
+
message: () =>
|
|
54
|
+
`unexpected key ${this.utils.printExpected(key)} in Map`,
|
|
55
|
+
pass: false,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
message: () => `ok`,
|
|
62
|
+
pass: true,
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
expect.extend({
|
|
67
|
+
toMatchMap,
|
|
68
|
+
});
|