@stamhoofd/backend 2.78.3 → 2.79.0
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/.env.ci.json +19 -8
- package/index.ts +8 -1
- package/package.json +13 -12
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +1 -1
- package/src/endpoints/auth/CreateAdminEndpoint.ts +1 -1
- package/src/endpoints/auth/CreateTokenEndpoint.ts +1 -1
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +1 -1
- package/src/endpoints/auth/PatchUserEndpoint.ts +1 -1
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +2686 -0
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +206 -20
- package/src/endpoints/global/members/shouldCheckIfMemberIsDuplicate.ts +9 -21
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +1 -1
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +2 -2
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +15 -14
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +3 -23
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +12 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +2 -3
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +164 -0
- package/src/helpers/AdminPermissionChecker.ts +41 -2
- package/src/helpers/AuthenticatedStructures.ts +77 -20
- package/src/helpers/ForwardHandler.test.ts +16 -5
- package/src/helpers/ForwardHandler.ts +21 -9
- package/src/helpers/MemberUserSyncer.test.ts +822 -0
- package/src/helpers/MemberUserSyncer.ts +137 -108
- package/src/helpers/TagHelper.ts +3 -3
- package/src/seeds/1734596144-fill-previous-period-id.ts +1 -1
- package/src/seeds/1741008870-fix-auditlog-description.ts +50 -0
- package/src/seeds/1741011468-fix-auditlog-description-uft16.ts +88 -0
- package/src/services/FileSignService.ts +1 -1
- package/src/services/PlatformMembershipService.ts +7 -2
- package/src/services/SSOService.ts +1 -1
- package/tests/e2e/register.test.ts +2 -2
|
@@ -0,0 +1,822 @@
|
|
|
1
|
+
import { Database } from '@simonbackx/simple-database';
|
|
2
|
+
import { Member, MemberFactory, MemberResponsibilityRecordFactory, User, UserFactory } from '@stamhoofd/models';
|
|
3
|
+
import { MemberDetails, Parent, UserPermissions } from '@stamhoofd/structures';
|
|
4
|
+
import { TestUtils } from '@stamhoofd/test-utils';
|
|
5
|
+
import { MemberUserSyncer } from './MemberUserSyncer';
|
|
6
|
+
|
|
7
|
+
describe('Helpers.MemberUserSyncer', () => {
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
TestUtils.setPermanentEnvironment('userMode', 'platform');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await Database.delete('DELETE FROM users');
|
|
14
|
+
await Database.delete('DELETE FROM members');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('Each email, parent and unverified email receives an account', async () => {
|
|
18
|
+
const member = await new MemberFactory({
|
|
19
|
+
details: MemberDetails.create({
|
|
20
|
+
firstName: 'John',
|
|
21
|
+
lastName: 'Doe',
|
|
22
|
+
parents: [
|
|
23
|
+
Parent.create({
|
|
24
|
+
firstName: 'Linda',
|
|
25
|
+
lastName: 'Potter',
|
|
26
|
+
email: 'linda@example.com',
|
|
27
|
+
}),
|
|
28
|
+
Parent.create({
|
|
29
|
+
firstName: 'Peter',
|
|
30
|
+
lastName: 'Doe',
|
|
31
|
+
email: 'peter@example.com',
|
|
32
|
+
alternativeEmails: [
|
|
33
|
+
'peter@work.com',
|
|
34
|
+
],
|
|
35
|
+
}),
|
|
36
|
+
],
|
|
37
|
+
email: 'john@example.com',
|
|
38
|
+
alternativeEmails: ['john@work.com'],
|
|
39
|
+
unverifiedEmails: ['untitled@example.com', 'peter@example.com'], // Last one should be ignored
|
|
40
|
+
}),
|
|
41
|
+
}).create();
|
|
42
|
+
|
|
43
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
44
|
+
|
|
45
|
+
const users = await Member.users.load(member);
|
|
46
|
+
expect(users).toIncludeSameMembers([
|
|
47
|
+
// Member
|
|
48
|
+
expect.objectContaining({
|
|
49
|
+
firstName: 'John',
|
|
50
|
+
lastName: 'Doe',
|
|
51
|
+
email: 'john@example.com',
|
|
52
|
+
memberId: member.id,
|
|
53
|
+
}),
|
|
54
|
+
expect.objectContaining({
|
|
55
|
+
firstName: 'John',
|
|
56
|
+
lastName: 'Doe',
|
|
57
|
+
email: 'john@work.com',
|
|
58
|
+
memberId: member.id,
|
|
59
|
+
}),
|
|
60
|
+
|
|
61
|
+
// Parents
|
|
62
|
+
expect.objectContaining({
|
|
63
|
+
firstName: 'Linda',
|
|
64
|
+
lastName: 'Potter',
|
|
65
|
+
email: 'linda@example.com',
|
|
66
|
+
memberId: null,
|
|
67
|
+
permissions: null,
|
|
68
|
+
}),
|
|
69
|
+
expect.objectContaining({
|
|
70
|
+
firstName: 'Peter',
|
|
71
|
+
lastName: 'Doe',
|
|
72
|
+
email: 'peter@example.com',
|
|
73
|
+
memberId: null,
|
|
74
|
+
permissions: null,
|
|
75
|
+
}),
|
|
76
|
+
expect.objectContaining({
|
|
77
|
+
firstName: 'Peter',
|
|
78
|
+
lastName: 'Doe',
|
|
79
|
+
email: 'peter@work.com',
|
|
80
|
+
memberId: null,
|
|
81
|
+
permissions: null,
|
|
82
|
+
}),
|
|
83
|
+
|
|
84
|
+
// Unverified
|
|
85
|
+
expect.objectContaining({
|
|
86
|
+
firstName: null,
|
|
87
|
+
lastName: null,
|
|
88
|
+
email: 'untitled@example.com',
|
|
89
|
+
memberId: null,
|
|
90
|
+
permissions: null,
|
|
91
|
+
}),
|
|
92
|
+
]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('Existing users are updated with a member id and name', async () => {
|
|
96
|
+
const user_m1 = await new UserFactory({ email: 'john@example.com' }).create();
|
|
97
|
+
const user_m2 = await new UserFactory({ email: 'john@work.com' }).create();
|
|
98
|
+
const user_p1 = await new UserFactory({ email: 'linda@example.com' }).create();
|
|
99
|
+
const user_p2a = await new UserFactory({ email: 'peter@example.com' }).create();
|
|
100
|
+
const user_p2b = await new UserFactory({ email: 'peter@work.com' }).create();
|
|
101
|
+
const user_unverified = await new UserFactory({ email: 'untitled@example.com' }).create();
|
|
102
|
+
|
|
103
|
+
const member = await new MemberFactory({
|
|
104
|
+
details: MemberDetails.create({
|
|
105
|
+
firstName: 'John',
|
|
106
|
+
lastName: 'Doe',
|
|
107
|
+
parents: [
|
|
108
|
+
Parent.create({
|
|
109
|
+
firstName: 'Linda',
|
|
110
|
+
lastName: 'Potter',
|
|
111
|
+
email: 'linda@example.com',
|
|
112
|
+
}),
|
|
113
|
+
Parent.create({
|
|
114
|
+
firstName: 'Peter',
|
|
115
|
+
lastName: 'Doe',
|
|
116
|
+
email: 'peter@example.com',
|
|
117
|
+
alternativeEmails: [
|
|
118
|
+
'peter@work.com',
|
|
119
|
+
],
|
|
120
|
+
}),
|
|
121
|
+
],
|
|
122
|
+
email: 'john@example.com',
|
|
123
|
+
alternativeEmails: ['john@work.com'],
|
|
124
|
+
unverifiedEmails: ['untitled@example.com', 'peter@example.com'], // Last one should be ignored
|
|
125
|
+
}),
|
|
126
|
+
}).create();
|
|
127
|
+
|
|
128
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
129
|
+
|
|
130
|
+
const users = await Member.users.load(member);
|
|
131
|
+
expect(users).toIncludeSameMembers([
|
|
132
|
+
// Member
|
|
133
|
+
expect.objectContaining({
|
|
134
|
+
id: user_m1.id,
|
|
135
|
+
firstName: 'John',
|
|
136
|
+
lastName: 'Doe',
|
|
137
|
+
email: 'john@example.com',
|
|
138
|
+
memberId: member.id,
|
|
139
|
+
permissions: null,
|
|
140
|
+
}),
|
|
141
|
+
expect.objectContaining({
|
|
142
|
+
id: user_m2.id,
|
|
143
|
+
firstName: 'John',
|
|
144
|
+
lastName: 'Doe',
|
|
145
|
+
email: 'john@work.com',
|
|
146
|
+
memberId: member.id,
|
|
147
|
+
permissions: null,
|
|
148
|
+
}),
|
|
149
|
+
|
|
150
|
+
// Parents
|
|
151
|
+
expect.objectContaining({
|
|
152
|
+
id: user_p1.id,
|
|
153
|
+
firstName: 'Linda',
|
|
154
|
+
lastName: 'Potter',
|
|
155
|
+
email: 'linda@example.com',
|
|
156
|
+
memberId: null,
|
|
157
|
+
permissions: null,
|
|
158
|
+
}),
|
|
159
|
+
expect.objectContaining({
|
|
160
|
+
id: user_p2a.id,
|
|
161
|
+
firstName: 'Peter',
|
|
162
|
+
lastName: 'Doe',
|
|
163
|
+
email: 'peter@example.com',
|
|
164
|
+
memberId: null,
|
|
165
|
+
permissions: null,
|
|
166
|
+
}),
|
|
167
|
+
expect.objectContaining({
|
|
168
|
+
id: user_p2b.id,
|
|
169
|
+
firstName: 'Peter',
|
|
170
|
+
lastName: 'Doe',
|
|
171
|
+
email: 'peter@work.com',
|
|
172
|
+
memberId: null,
|
|
173
|
+
permissions: null,
|
|
174
|
+
}),
|
|
175
|
+
|
|
176
|
+
// Unverified
|
|
177
|
+
expect.objectContaining({
|
|
178
|
+
id: user_unverified.id,
|
|
179
|
+
firstName: null,
|
|
180
|
+
lastName: null,
|
|
181
|
+
email: 'untitled@example.com',
|
|
182
|
+
memberId: null,
|
|
183
|
+
permissions: null,
|
|
184
|
+
}),
|
|
185
|
+
]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('Custom user names are maintained for parents', async () => {
|
|
189
|
+
const user_p1 = await new UserFactory({ email: 'linda@example.com', firstName: 'Custom', lastName: 'Name' }).create();
|
|
190
|
+
|
|
191
|
+
const member = await new MemberFactory({
|
|
192
|
+
details: MemberDetails.create({
|
|
193
|
+
firstName: 'John',
|
|
194
|
+
lastName: 'Doe',
|
|
195
|
+
parents: [
|
|
196
|
+
Parent.create({
|
|
197
|
+
firstName: 'Linda',
|
|
198
|
+
lastName: 'Potter',
|
|
199
|
+
email: 'linda@example.com',
|
|
200
|
+
}),
|
|
201
|
+
],
|
|
202
|
+
}),
|
|
203
|
+
}).create();
|
|
204
|
+
|
|
205
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
206
|
+
|
|
207
|
+
const users = await Member.users.load(member);
|
|
208
|
+
expect(users).toIncludeSameMembers([
|
|
209
|
+
// Member
|
|
210
|
+
expect.objectContaining({
|
|
211
|
+
id: user_p1.id,
|
|
212
|
+
firstName: 'Custom',
|
|
213
|
+
lastName: 'Name',
|
|
214
|
+
email: 'linda@example.com',
|
|
215
|
+
memberId: null,
|
|
216
|
+
permissions: null,
|
|
217
|
+
}),
|
|
218
|
+
]);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('Parent name is cleared if it equals the member name', async () => {
|
|
222
|
+
const user_p1 = await new UserFactory({ email: 'linda@example.com', firstName: 'John', lastName: 'Doe' }).create();
|
|
223
|
+
|
|
224
|
+
const member = await new MemberFactory({
|
|
225
|
+
details: MemberDetails.create({
|
|
226
|
+
firstName: 'John',
|
|
227
|
+
lastName: 'Doe',
|
|
228
|
+
parents: [
|
|
229
|
+
Parent.create({
|
|
230
|
+
firstName: 'Linda',
|
|
231
|
+
lastName: 'Potter',
|
|
232
|
+
email: 'linda@example.com',
|
|
233
|
+
}),
|
|
234
|
+
],
|
|
235
|
+
}),
|
|
236
|
+
}).create();
|
|
237
|
+
|
|
238
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
239
|
+
|
|
240
|
+
const users = await Member.users.load(member);
|
|
241
|
+
expect(users).toIncludeSameMembers([
|
|
242
|
+
// Member
|
|
243
|
+
expect.objectContaining({
|
|
244
|
+
id: user_p1.id,
|
|
245
|
+
firstName: 'Linda',
|
|
246
|
+
lastName: 'Potter',
|
|
247
|
+
email: 'linda@example.com',
|
|
248
|
+
memberId: null,
|
|
249
|
+
permissions: null,
|
|
250
|
+
}),
|
|
251
|
+
]);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test('Parent name is cleared if it equals the member name with caps difference', async () => {
|
|
255
|
+
const user_p1 = await new UserFactory({ email: 'linda@example.com', firstName: 'john', lastName: 'doe' }).create();
|
|
256
|
+
|
|
257
|
+
const member = await new MemberFactory({
|
|
258
|
+
details: MemberDetails.create({
|
|
259
|
+
firstName: 'John',
|
|
260
|
+
lastName: 'Doe',
|
|
261
|
+
parents: [
|
|
262
|
+
Parent.create({
|
|
263
|
+
firstName: 'Linda',
|
|
264
|
+
lastName: 'Potter',
|
|
265
|
+
email: 'linda@example.com',
|
|
266
|
+
}),
|
|
267
|
+
],
|
|
268
|
+
}),
|
|
269
|
+
}).create();
|
|
270
|
+
|
|
271
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
272
|
+
|
|
273
|
+
const users = await Member.users.load(member);
|
|
274
|
+
expect(users).toIncludeSameMembers([
|
|
275
|
+
// Member
|
|
276
|
+
expect.objectContaining({
|
|
277
|
+
id: user_p1.id,
|
|
278
|
+
firstName: 'Linda',
|
|
279
|
+
lastName: 'Potter',
|
|
280
|
+
email: 'linda@example.com',
|
|
281
|
+
memberId: null,
|
|
282
|
+
permissions: null,
|
|
283
|
+
}),
|
|
284
|
+
]);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test('Existing parent user can have the same name as the member', async () => {
|
|
288
|
+
const user_p1 = await new UserFactory({ email: 'linda@example.com', firstName: 'John', lastName: 'Doe' }).create();
|
|
289
|
+
|
|
290
|
+
const member = await new MemberFactory({
|
|
291
|
+
details: MemberDetails.create({
|
|
292
|
+
firstName: 'John',
|
|
293
|
+
lastName: 'Doe',
|
|
294
|
+
parents: [
|
|
295
|
+
Parent.create({
|
|
296
|
+
firstName: 'John',
|
|
297
|
+
lastName: 'Doe',
|
|
298
|
+
email: 'linda@example.com',
|
|
299
|
+
}),
|
|
300
|
+
],
|
|
301
|
+
}),
|
|
302
|
+
}).create();
|
|
303
|
+
|
|
304
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
305
|
+
|
|
306
|
+
const users = await Member.users.load(member);
|
|
307
|
+
expect(users).toIncludeSameMembers([
|
|
308
|
+
// Member
|
|
309
|
+
expect.objectContaining({
|
|
310
|
+
id: user_p1.id,
|
|
311
|
+
firstName: 'John',
|
|
312
|
+
lastName: 'Doe',
|
|
313
|
+
email: 'linda@example.com',
|
|
314
|
+
memberId: null,
|
|
315
|
+
permissions: null,
|
|
316
|
+
}),
|
|
317
|
+
]);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test('New parent user can have the same name as the member', async () => {
|
|
321
|
+
const member = await new MemberFactory({
|
|
322
|
+
details: MemberDetails.create({
|
|
323
|
+
firstName: 'John',
|
|
324
|
+
lastName: 'Doe',
|
|
325
|
+
parents: [
|
|
326
|
+
Parent.create({
|
|
327
|
+
firstName: 'John',
|
|
328
|
+
lastName: 'Doe',
|
|
329
|
+
email: 'linda@example.com',
|
|
330
|
+
}),
|
|
331
|
+
],
|
|
332
|
+
}),
|
|
333
|
+
}).create();
|
|
334
|
+
|
|
335
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
336
|
+
|
|
337
|
+
const users = await Member.users.load(member);
|
|
338
|
+
expect(users).toIncludeSameMembers([
|
|
339
|
+
// Member
|
|
340
|
+
expect.objectContaining({
|
|
341
|
+
firstName: 'John',
|
|
342
|
+
lastName: 'Doe',
|
|
343
|
+
email: 'linda@example.com',
|
|
344
|
+
memberId: null,
|
|
345
|
+
permissions: null,
|
|
346
|
+
}),
|
|
347
|
+
]);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
describe('Unlinking', () => {
|
|
351
|
+
test('Old emails without account are removed', async () => {
|
|
352
|
+
const member = await new MemberFactory({
|
|
353
|
+
details: MemberDetails.create({
|
|
354
|
+
firstName: 'John',
|
|
355
|
+
lastName: 'Doe',
|
|
356
|
+
parents: [
|
|
357
|
+
Parent.create({
|
|
358
|
+
firstName: 'Linda',
|
|
359
|
+
lastName: 'Potter',
|
|
360
|
+
email: 'linda@example.com',
|
|
361
|
+
}),
|
|
362
|
+
Parent.create({
|
|
363
|
+
firstName: 'Peter',
|
|
364
|
+
lastName: 'Doe',
|
|
365
|
+
email: 'peter@example.com',
|
|
366
|
+
alternativeEmails: [
|
|
367
|
+
'peter@work.com',
|
|
368
|
+
],
|
|
369
|
+
}),
|
|
370
|
+
],
|
|
371
|
+
email: 'john@example.com',
|
|
372
|
+
alternativeEmails: ['john@work.com'],
|
|
373
|
+
unverifiedEmails: ['untitled@example.com', 'peter@example.com'], // Last one should be ignored
|
|
374
|
+
}),
|
|
375
|
+
}).create();
|
|
376
|
+
await new MemberResponsibilityRecordFactory({
|
|
377
|
+
member,
|
|
378
|
+
}).create();
|
|
379
|
+
|
|
380
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
381
|
+
|
|
382
|
+
const users = await Member.users.load(member);
|
|
383
|
+
expect(users).toIncludeSameMembers([
|
|
384
|
+
// Member
|
|
385
|
+
expect.objectContaining({
|
|
386
|
+
firstName: 'John',
|
|
387
|
+
lastName: 'Doe',
|
|
388
|
+
email: 'john@example.com',
|
|
389
|
+
memberId: member.id,
|
|
390
|
+
permissions: expect.any(UserPermissions),
|
|
391
|
+
}),
|
|
392
|
+
expect.objectContaining({
|
|
393
|
+
firstName: 'John',
|
|
394
|
+
lastName: 'Doe',
|
|
395
|
+
email: 'john@work.com',
|
|
396
|
+
memberId: member.id,
|
|
397
|
+
permissions: expect.any(UserPermissions),
|
|
398
|
+
}),
|
|
399
|
+
|
|
400
|
+
// Parents
|
|
401
|
+
expect.objectContaining({
|
|
402
|
+
firstName: 'Linda',
|
|
403
|
+
lastName: 'Potter',
|
|
404
|
+
email: 'linda@example.com',
|
|
405
|
+
memberId: null,
|
|
406
|
+
permissions: null,
|
|
407
|
+
}),
|
|
408
|
+
expect.objectContaining({
|
|
409
|
+
firstName: 'Peter',
|
|
410
|
+
lastName: 'Doe',
|
|
411
|
+
email: 'peter@example.com',
|
|
412
|
+
memberId: null,
|
|
413
|
+
permissions: null,
|
|
414
|
+
}),
|
|
415
|
+
expect.objectContaining({
|
|
416
|
+
firstName: 'Peter',
|
|
417
|
+
lastName: 'Doe',
|
|
418
|
+
email: 'peter@work.com',
|
|
419
|
+
memberId: null,
|
|
420
|
+
permissions: null,
|
|
421
|
+
}),
|
|
422
|
+
|
|
423
|
+
// Unverified
|
|
424
|
+
expect.objectContaining({
|
|
425
|
+
firstName: null,
|
|
426
|
+
lastName: null,
|
|
427
|
+
email: 'untitled@example.com',
|
|
428
|
+
memberId: null,
|
|
429
|
+
permissions: null,
|
|
430
|
+
}),
|
|
431
|
+
]);
|
|
432
|
+
|
|
433
|
+
// Now remove the work address of peter
|
|
434
|
+
member.details.parents[1].alternativeEmails = [];
|
|
435
|
+
member.details.parents[1].email = null;
|
|
436
|
+
member.details.parents[0].email = null;
|
|
437
|
+
member.details.alternativeEmails = [];
|
|
438
|
+
member.details.unverifiedEmails = [];
|
|
439
|
+
|
|
440
|
+
// Save member
|
|
441
|
+
if (!await member.save()) {
|
|
442
|
+
throw new Error('Failed to save member');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Sync again
|
|
446
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
447
|
+
|
|
448
|
+
const users2 = await Member.users.load(member);
|
|
449
|
+
expect(users2).toIncludeSameMembers([
|
|
450
|
+
// Member
|
|
451
|
+
expect.objectContaining({
|
|
452
|
+
firstName: 'John',
|
|
453
|
+
lastName: 'Doe',
|
|
454
|
+
email: 'john@example.com',
|
|
455
|
+
memberId: member.id,
|
|
456
|
+
permissions: expect.any(UserPermissions),
|
|
457
|
+
}),
|
|
458
|
+
]);
|
|
459
|
+
|
|
460
|
+
const userWork = await User.select().where('email', 'john@work.com').first(true);
|
|
461
|
+
expect(userWork).toMatchObject({
|
|
462
|
+
firstName: null,
|
|
463
|
+
lastName: null,
|
|
464
|
+
memberId: null,
|
|
465
|
+
permissions: null,
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
test('Old emails with account are not removed', async () => {
|
|
470
|
+
const user_m1 = await new UserFactory({ email: 'john@example.com' }).create();
|
|
471
|
+
const user_m2 = await new UserFactory({ email: 'john@work.com' }).create();
|
|
472
|
+
const user_p1 = await new UserFactory({ email: 'linda@example.com' }).create();
|
|
473
|
+
const user_p2a = await new UserFactory({ email: 'peter@example.com' }).create();
|
|
474
|
+
const user_p2b = await new UserFactory({ email: 'peter@work.com' }).create();
|
|
475
|
+
const user_unverified = await new UserFactory({ email: 'untitled@example.com' }).create();
|
|
476
|
+
|
|
477
|
+
const member = await new MemberFactory({
|
|
478
|
+
details: MemberDetails.create({
|
|
479
|
+
firstName: 'John',
|
|
480
|
+
lastName: 'Doe',
|
|
481
|
+
parents: [
|
|
482
|
+
Parent.create({
|
|
483
|
+
firstName: 'Linda',
|
|
484
|
+
lastName: 'Potter',
|
|
485
|
+
email: 'linda@example.com',
|
|
486
|
+
}),
|
|
487
|
+
Parent.create({
|
|
488
|
+
firstName: 'Peter',
|
|
489
|
+
lastName: 'Doe',
|
|
490
|
+
email: 'peter@example.com',
|
|
491
|
+
alternativeEmails: [
|
|
492
|
+
'peter@work.com',
|
|
493
|
+
],
|
|
494
|
+
}),
|
|
495
|
+
],
|
|
496
|
+
email: 'john@example.com',
|
|
497
|
+
alternativeEmails: ['john@work.com'],
|
|
498
|
+
unverifiedEmails: ['untitled@example.com', 'peter@example.com'], // Last one should be ignored
|
|
499
|
+
}),
|
|
500
|
+
}).create();
|
|
501
|
+
|
|
502
|
+
await new MemberResponsibilityRecordFactory({
|
|
503
|
+
member,
|
|
504
|
+
}).create();
|
|
505
|
+
|
|
506
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
507
|
+
|
|
508
|
+
const users = await Member.users.load(member);
|
|
509
|
+
expect(users).toIncludeSameMembers([
|
|
510
|
+
// Member
|
|
511
|
+
expect.objectContaining({
|
|
512
|
+
id: user_m1.id,
|
|
513
|
+
firstName: 'John',
|
|
514
|
+
lastName: 'Doe',
|
|
515
|
+
email: 'john@example.com',
|
|
516
|
+
memberId: member.id,
|
|
517
|
+
permissions: expect.any(UserPermissions),
|
|
518
|
+
}),
|
|
519
|
+
expect.objectContaining({
|
|
520
|
+
id: user_m2.id,
|
|
521
|
+
firstName: 'John',
|
|
522
|
+
lastName: 'Doe',
|
|
523
|
+
email: 'john@work.com',
|
|
524
|
+
memberId: member.id,
|
|
525
|
+
permissions: expect.any(UserPermissions),
|
|
526
|
+
}),
|
|
527
|
+
|
|
528
|
+
// Parents
|
|
529
|
+
expect.objectContaining({
|
|
530
|
+
id: user_p1.id,
|
|
531
|
+
firstName: 'Linda',
|
|
532
|
+
lastName: 'Potter',
|
|
533
|
+
email: 'linda@example.com',
|
|
534
|
+
memberId: null,
|
|
535
|
+
permissions: null,
|
|
536
|
+
}),
|
|
537
|
+
expect.objectContaining({
|
|
538
|
+
id: user_p2a.id,
|
|
539
|
+
firstName: 'Peter',
|
|
540
|
+
lastName: 'Doe',
|
|
541
|
+
email: 'peter@example.com',
|
|
542
|
+
memberId: null,
|
|
543
|
+
permissions: null,
|
|
544
|
+
}),
|
|
545
|
+
expect.objectContaining({
|
|
546
|
+
id: user_p2b.id,
|
|
547
|
+
firstName: 'Peter',
|
|
548
|
+
lastName: 'Doe',
|
|
549
|
+
email: 'peter@work.com',
|
|
550
|
+
memberId: null,
|
|
551
|
+
permissions: null,
|
|
552
|
+
}),
|
|
553
|
+
|
|
554
|
+
// Unverified
|
|
555
|
+
expect.objectContaining({
|
|
556
|
+
id: user_unverified.id,
|
|
557
|
+
firstName: null,
|
|
558
|
+
lastName: null,
|
|
559
|
+
email: 'untitled@example.com',
|
|
560
|
+
memberId: null,
|
|
561
|
+
permissions: null,
|
|
562
|
+
}),
|
|
563
|
+
]);
|
|
564
|
+
|
|
565
|
+
// Now remove the work address of peter
|
|
566
|
+
member.details.parents[1].alternativeEmails = [];
|
|
567
|
+
member.details.parents[1].email = null;
|
|
568
|
+
member.details.parents[0].email = null;
|
|
569
|
+
member.details.alternativeEmails = [];
|
|
570
|
+
member.details.unverifiedEmails = [];
|
|
571
|
+
|
|
572
|
+
// Save member
|
|
573
|
+
await member.save();
|
|
574
|
+
|
|
575
|
+
// Sync again
|
|
576
|
+
await MemberUserSyncer.onChangeMember(member);
|
|
577
|
+
|
|
578
|
+
const users2 = await Member.users.load(member);
|
|
579
|
+
expect(users2).toIncludeSameMembers([
|
|
580
|
+
// Member
|
|
581
|
+
expect.objectContaining({
|
|
582
|
+
id: user_m1.id,
|
|
583
|
+
firstName: 'John',
|
|
584
|
+
lastName: 'Doe',
|
|
585
|
+
email: 'john@example.com',
|
|
586
|
+
memberId: member.id,
|
|
587
|
+
permissions: expect.any(UserPermissions),
|
|
588
|
+
}),
|
|
589
|
+
expect.objectContaining({
|
|
590
|
+
id: user_m2.id,
|
|
591
|
+
firstName: null, // this has been reset
|
|
592
|
+
lastName: null, // this has been reset
|
|
593
|
+
email: 'john@work.com',
|
|
594
|
+
memberId: null, // this has been reset
|
|
595
|
+
permissions: null, // this has been reset
|
|
596
|
+
}),
|
|
597
|
+
|
|
598
|
+
// Parents
|
|
599
|
+
expect.objectContaining({
|
|
600
|
+
id: user_p1.id,
|
|
601
|
+
firstName: 'Linda',
|
|
602
|
+
lastName: 'Potter',
|
|
603
|
+
email: 'linda@example.com',
|
|
604
|
+
memberId: null,
|
|
605
|
+
permissions: null,
|
|
606
|
+
}),
|
|
607
|
+
expect.objectContaining({
|
|
608
|
+
id: user_p2a.id,
|
|
609
|
+
firstName: 'Peter',
|
|
610
|
+
lastName: 'Doe',
|
|
611
|
+
email: 'peter@example.com',
|
|
612
|
+
memberId: null,
|
|
613
|
+
permissions: null,
|
|
614
|
+
}),
|
|
615
|
+
expect.objectContaining({
|
|
616
|
+
id: user_p2b.id,
|
|
617
|
+
firstName: 'Peter',
|
|
618
|
+
lastName: 'Doe',
|
|
619
|
+
email: 'peter@work.com',
|
|
620
|
+
memberId: null,
|
|
621
|
+
permissions: null,
|
|
622
|
+
}),
|
|
623
|
+
|
|
624
|
+
// Unverified
|
|
625
|
+
expect.objectContaining({
|
|
626
|
+
id: user_unverified.id,
|
|
627
|
+
firstName: null,
|
|
628
|
+
lastName: null,
|
|
629
|
+
email: 'untitled@example.com',
|
|
630
|
+
memberId: null,
|
|
631
|
+
permissions: null,
|
|
632
|
+
}),
|
|
633
|
+
]);
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
describe('Members with the same email addresses', () => {
|
|
638
|
+
test('The most recent member is linked to a user if both do not have responsibilities', async () => {
|
|
639
|
+
const member1 = await new MemberFactory({
|
|
640
|
+
details: MemberDetails.create({
|
|
641
|
+
firstName: 'John',
|
|
642
|
+
lastName: 'Doe',
|
|
643
|
+
email: 'john@example.com',
|
|
644
|
+
}),
|
|
645
|
+
}).create();
|
|
646
|
+
await MemberUserSyncer.onChangeMember(member1);
|
|
647
|
+
const users1 = await Member.users.load(member1);
|
|
648
|
+
expect(users1).toIncludeSameMembers([
|
|
649
|
+
expect.objectContaining({
|
|
650
|
+
firstName: 'John',
|
|
651
|
+
lastName: 'Doe',
|
|
652
|
+
email: 'john@example.com',
|
|
653
|
+
memberId: member1.id,
|
|
654
|
+
permissions: null,
|
|
655
|
+
}),
|
|
656
|
+
]);
|
|
657
|
+
|
|
658
|
+
// Wait 1 second to make sure we save a new timestamp
|
|
659
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
660
|
+
|
|
661
|
+
// member2 should not seize the memberId
|
|
662
|
+
const member2 = await new MemberFactory({
|
|
663
|
+
details: MemberDetails.create({
|
|
664
|
+
firstName: 'Other',
|
|
665
|
+
lastName: 'Doe',
|
|
666
|
+
email: 'john@example.com',
|
|
667
|
+
}),
|
|
668
|
+
}).create();
|
|
669
|
+
|
|
670
|
+
await MemberUserSyncer.onChangeMember(member2);
|
|
671
|
+
const users2 = await Member.users.load(member2);
|
|
672
|
+
expect(users2).toIncludeSameMembers([
|
|
673
|
+
expect.objectContaining({
|
|
674
|
+
firstName: 'Other',
|
|
675
|
+
lastName: 'Doe',
|
|
676
|
+
email: 'john@example.com',
|
|
677
|
+
memberId: member2.id,
|
|
678
|
+
permissions: null,
|
|
679
|
+
}),
|
|
680
|
+
]);
|
|
681
|
+
|
|
682
|
+
// Even if called sync again on other member
|
|
683
|
+
await MemberUserSyncer.onChangeMember(member1);
|
|
684
|
+
const users3 = await Member.users.load(member1);
|
|
685
|
+
expect(users3).toIncludeSameMembers([
|
|
686
|
+
expect.objectContaining({
|
|
687
|
+
firstName: 'Other',
|
|
688
|
+
lastName: 'Doe',
|
|
689
|
+
email: 'john@example.com',
|
|
690
|
+
memberId: member2.id,
|
|
691
|
+
permissions: null,
|
|
692
|
+
}),
|
|
693
|
+
]);
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
test('The most old member is linked to a user if both have responsibilities', async () => {
|
|
697
|
+
const member1 = await new MemberFactory({
|
|
698
|
+
details: MemberDetails.create({
|
|
699
|
+
firstName: 'John',
|
|
700
|
+
lastName: 'Doe',
|
|
701
|
+
email: 'john@example.com',
|
|
702
|
+
}),
|
|
703
|
+
}).create();
|
|
704
|
+
await new MemberResponsibilityRecordFactory({
|
|
705
|
+
member: member1,
|
|
706
|
+
}).create();
|
|
707
|
+
|
|
708
|
+
await MemberUserSyncer.onChangeMember(member1);
|
|
709
|
+
|
|
710
|
+
const users1 = await Member.users.load(member1);
|
|
711
|
+
expect(users1).toIncludeSameMembers([
|
|
712
|
+
expect.objectContaining({
|
|
713
|
+
firstName: 'John',
|
|
714
|
+
lastName: 'Doe',
|
|
715
|
+
email: 'john@example.com',
|
|
716
|
+
memberId: member1.id,
|
|
717
|
+
permissions: expect.any(UserPermissions),
|
|
718
|
+
}),
|
|
719
|
+
]);
|
|
720
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
721
|
+
|
|
722
|
+
// member2 should not seize the memberId
|
|
723
|
+
const member2 = await new MemberFactory({
|
|
724
|
+
details: MemberDetails.create({
|
|
725
|
+
firstName: 'Other',
|
|
726
|
+
lastName: 'Doe',
|
|
727
|
+
email: 'john@example.com',
|
|
728
|
+
}),
|
|
729
|
+
}).create();
|
|
730
|
+
await new MemberResponsibilityRecordFactory({
|
|
731
|
+
member: member2,
|
|
732
|
+
}).create();
|
|
733
|
+
|
|
734
|
+
await MemberUserSyncer.onChangeMember(member2);
|
|
735
|
+
const users2 = await Member.users.load(member2);
|
|
736
|
+
|
|
737
|
+
// Stayed the same
|
|
738
|
+
expect(users2).toIncludeSameMembers([
|
|
739
|
+
expect.objectContaining({
|
|
740
|
+
firstName: 'John',
|
|
741
|
+
lastName: 'Doe',
|
|
742
|
+
email: 'john@example.com',
|
|
743
|
+
memberId: member1.id,
|
|
744
|
+
permissions: expect.any(UserPermissions),
|
|
745
|
+
}),
|
|
746
|
+
]);
|
|
747
|
+
|
|
748
|
+
// Even if called sync again on other member
|
|
749
|
+
await MemberUserSyncer.onChangeMember(member1);
|
|
750
|
+
const users3 = await Member.users.load(member1);
|
|
751
|
+
expect(users3).toIncludeSameMembers([
|
|
752
|
+
expect.objectContaining({
|
|
753
|
+
firstName: 'John',
|
|
754
|
+
lastName: 'Doe',
|
|
755
|
+
email: 'john@example.com',
|
|
756
|
+
memberId: member1.id,
|
|
757
|
+
permissions: expect.any(UserPermissions),
|
|
758
|
+
}),
|
|
759
|
+
]);
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
test('The member with responsibilities is linked to a user', async () => {
|
|
763
|
+
const member1 = await new MemberFactory({
|
|
764
|
+
details: MemberDetails.create({
|
|
765
|
+
firstName: 'John',
|
|
766
|
+
lastName: 'Doe',
|
|
767
|
+
email: 'john@example.com',
|
|
768
|
+
}),
|
|
769
|
+
}).create();
|
|
770
|
+
await MemberUserSyncer.onChangeMember(member1);
|
|
771
|
+
const users1 = await Member.users.load(member1);
|
|
772
|
+
expect(users1).toIncludeSameMembers([
|
|
773
|
+
expect.objectContaining({
|
|
774
|
+
firstName: 'John',
|
|
775
|
+
lastName: 'Doe',
|
|
776
|
+
email: 'john@example.com',
|
|
777
|
+
memberId: member1.id,
|
|
778
|
+
permissions: null,
|
|
779
|
+
}),
|
|
780
|
+
]);
|
|
781
|
+
|
|
782
|
+
// member2 should not seize the memberId
|
|
783
|
+
const member2 = await new MemberFactory({
|
|
784
|
+
details: MemberDetails.create({
|
|
785
|
+
firstName: 'Other',
|
|
786
|
+
lastName: 'Doe',
|
|
787
|
+
email: 'john@example.com',
|
|
788
|
+
}),
|
|
789
|
+
}).create();
|
|
790
|
+
|
|
791
|
+
// Attach a responsibility to member2
|
|
792
|
+
await new MemberResponsibilityRecordFactory({
|
|
793
|
+
member: member2,
|
|
794
|
+
}).create();
|
|
795
|
+
|
|
796
|
+
await MemberUserSyncer.onChangeMember(member2);
|
|
797
|
+
const users2 = await Member.users.load(member2);
|
|
798
|
+
expect(users2).toIncludeSameMembers([
|
|
799
|
+
expect.objectContaining({
|
|
800
|
+
firstName: 'Other',
|
|
801
|
+
lastName: 'Doe',
|
|
802
|
+
email: 'john@example.com',
|
|
803
|
+
memberId: member2.id,
|
|
804
|
+
permissions: expect.any(UserPermissions),
|
|
805
|
+
}),
|
|
806
|
+
]);
|
|
807
|
+
|
|
808
|
+
// Even if called sync again on other member
|
|
809
|
+
await MemberUserSyncer.onChangeMember(member1);
|
|
810
|
+
const users3 = await Member.users.load(member1);
|
|
811
|
+
expect(users3).toIncludeSameMembers([
|
|
812
|
+
expect.objectContaining({
|
|
813
|
+
firstName: 'Other',
|
|
814
|
+
lastName: 'Doe',
|
|
815
|
+
email: 'john@example.com',
|
|
816
|
+
memberId: member2.id,
|
|
817
|
+
permissions: expect.any(UserPermissions),
|
|
818
|
+
}),
|
|
819
|
+
]);
|
|
820
|
+
});
|
|
821
|
+
});
|
|
822
|
+
});
|