@oneuptime/common 9.2.18 → 9.2.21

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.
Files changed (100) hide show
  1. package/Server/Services/AIService.ts +1 -1
  2. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +78 -1
  3. package/Server/Utils/Workspace/Slack/Slack.ts +80 -1
  4. package/Tests/Server/API/BaseAPI.test.ts +9 -4
  5. package/Tests/Server/Middleware/ProjectAuthorization.test.ts +133 -162
  6. package/Tests/Server/Services/ProbeService.test.ts +91 -784
  7. package/Tests/Server/Services/ScheduledMaintenanceService.test.ts +131 -112
  8. package/Tests/Server/Services/TeamMemberService.test.ts +87 -1343
  9. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +18 -9
  10. package/Tests/Server/Utils/Cookie.test.ts +10 -2
  11. package/Tests/Types/HashedString.test.ts +52 -8
  12. package/Tests/UI/Components/404.test.tsx +10 -15
  13. package/Tests/UI/Components/Breadcrumbs.test.tsx +6 -2
  14. package/Tests/UI/Components/Button.test.tsx +12 -12
  15. package/Tests/UI/Components/Card.test.tsx +4 -2
  16. package/Tests/UI/Components/ConfirmModal.test.tsx +1 -1
  17. package/Tests/UI/Components/Dropdown.test.tsx +37 -4
  18. package/Tests/UI/Components/DuplicateModel.test.tsx +49 -45
  19. package/Tests/UI/Components/FilePicker.test.tsx +258 -178
  20. package/Tests/UI/Components/List.test.tsx +3 -1
  21. package/Tests/UI/Components/MarkdownEditor.test.tsx +6 -5
  22. package/Tests/UI/Components/MasterPage.test.tsx +1 -1
  23. package/Tests/UI/Components/Modal.test.tsx +5 -5
  24. package/Tests/UI/Components/NavBar.test.tsx +14 -1
  25. package/Tests/UI/Components/OrderedStatesList.test.tsx +1 -1
  26. package/Tests/UI/Components/Pagination.test.tsx +6 -2
  27. package/Tests/Utils/API.test.ts +133 -11
  28. package/Tests/__mocks__/azure.js +2 -0
  29. package/Tests/__mocks__/botbuilder-stdlib.js +2 -0
  30. package/Tests/__mocks__/botbuilder.js +10 -0
  31. package/Tests/__mocks__/locter.js +5 -0
  32. package/Tests/__mocks__/otpauth.js +30 -0
  33. package/Tests/__mocks__/simplewebauthn.js +34 -0
  34. package/Tests/__mocks__/styleMock.js +1 -0
  35. package/Tests/__mocks__/uuid.js +31 -0
  36. package/Tests/__mocks__/yaml.js +11 -0
  37. package/Tests/jest.setup.ts +14 -0
  38. package/UI/Components/AI/AITemplates.ts +226 -0
  39. package/UI/Components/AI/GenerateFromAIModal.tsx +21 -270
  40. package/build/dist/Server/Services/AIService.js +1 -1
  41. package/build/dist/Server/Services/AIService.js.map +1 -1
  42. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +59 -2
  43. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  44. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +61 -1
  45. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  46. package/build/dist/Tests/Server/API/BaseAPI.test.js +7 -2
  47. package/build/dist/Tests/Server/API/BaseAPI.test.js.map +1 -1
  48. package/build/dist/Tests/Server/Middleware/ProjectAuthorization.test.js +89 -101
  49. package/build/dist/Tests/Server/Middleware/ProjectAuthorization.test.js.map +1 -1
  50. package/build/dist/Tests/Server/Services/ProbeService.test.js +95 -687
  51. package/build/dist/Tests/Server/Services/ProbeService.test.js.map +1 -1
  52. package/build/dist/Tests/Server/Services/ScheduledMaintenanceService.test.js +108 -89
  53. package/build/dist/Tests/Server/Services/ScheduledMaintenanceService.test.js.map +1 -1
  54. package/build/dist/Tests/Server/Services/TeamMemberService.test.js +85 -924
  55. package/build/dist/Tests/Server/Services/TeamMemberService.test.js.map +1 -1
  56. package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js +14 -9
  57. package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js.map +1 -1
  58. package/build/dist/Tests/Server/Utils/Cookie.test.js +10 -4
  59. package/build/dist/Tests/Server/Utils/Cookie.test.js.map +1 -1
  60. package/build/dist/Tests/Types/HashedString.test.js +39 -6
  61. package/build/dist/Tests/Types/HashedString.test.js.map +1 -1
  62. package/build/dist/Tests/UI/Components/404.test.js +10 -10
  63. package/build/dist/Tests/UI/Components/404.test.js.map +1 -1
  64. package/build/dist/Tests/UI/Components/Breadcrumbs.test.js +6 -2
  65. package/build/dist/Tests/UI/Components/Breadcrumbs.test.js.map +1 -1
  66. package/build/dist/Tests/UI/Components/Button.test.js +12 -12
  67. package/build/dist/Tests/UI/Components/Card.test.js +4 -2
  68. package/build/dist/Tests/UI/Components/Card.test.js.map +1 -1
  69. package/build/dist/Tests/UI/Components/ConfirmModal.test.js +1 -1
  70. package/build/dist/Tests/UI/Components/ConfirmModal.test.js.map +1 -1
  71. package/build/dist/Tests/UI/Components/Dropdown.test.js +19 -3
  72. package/build/dist/Tests/UI/Components/Dropdown.test.js.map +1 -1
  73. package/build/dist/Tests/UI/Components/DuplicateModel.test.js +46 -41
  74. package/build/dist/Tests/UI/Components/DuplicateModel.test.js.map +1 -1
  75. package/build/dist/Tests/UI/Components/FilePicker.test.js +210 -117
  76. package/build/dist/Tests/UI/Components/FilePicker.test.js.map +1 -1
  77. package/build/dist/Tests/UI/Components/List.test.js +3 -1
  78. package/build/dist/Tests/UI/Components/List.test.js.map +1 -1
  79. package/build/dist/Tests/UI/Components/MarkdownEditor.test.js +6 -5
  80. package/build/dist/Tests/UI/Components/MarkdownEditor.test.js.map +1 -1
  81. package/build/dist/Tests/UI/Components/MasterPage.test.js +1 -1
  82. package/build/dist/Tests/UI/Components/MasterPage.test.js.map +1 -1
  83. package/build/dist/Tests/UI/Components/Modal.test.js +5 -5
  84. package/build/dist/Tests/UI/Components/Modal.test.js.map +1 -1
  85. package/build/dist/Tests/UI/Components/NavBar.test.js +13 -1
  86. package/build/dist/Tests/UI/Components/NavBar.test.js.map +1 -1
  87. package/build/dist/Tests/UI/Components/OrderedStatesList.test.js +1 -1
  88. package/build/dist/Tests/UI/Components/OrderedStatesList.test.js.map +1 -1
  89. package/build/dist/Tests/UI/Components/Pagination.test.js +6 -2
  90. package/build/dist/Tests/UI/Components/Pagination.test.js.map +1 -1
  91. package/build/dist/Tests/Utils/API.test.js +100 -9
  92. package/build/dist/Tests/Utils/API.test.js.map +1 -1
  93. package/build/dist/Tests/jest.setup.js +13 -0
  94. package/build/dist/Tests/jest.setup.js.map +1 -0
  95. package/build/dist/UI/Components/AI/AITemplates.js +218 -0
  96. package/build/dist/UI/Components/AI/AITemplates.js.map +1 -0
  97. package/build/dist/UI/Components/AI/GenerateFromAIModal.js +5 -238
  98. package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
  99. package/jest.config.json +18 -1
  100. package/package.json +1 -1
@@ -1,1384 +1,128 @@
1
- import { Host, HttpProtocol } from "../../../Server/EnvironmentConfig";
2
- import AccessTokenService from "../../../Server/Services/AccessTokenService";
3
- import BillingService from "../../../Server/Services/BillingService";
4
- import MailService from "../../../Server/Services/MailService";
5
- import TeamMemberService from "../../../Server/Services/TeamMemberService";
6
- import UserNotificationRuleService from "../../../Server/Services/UserNotificationRuleService";
7
- import UserNotificationSettingService from "../../../Server/Services/UserNotificationSettingService";
8
- import ProjectSCIMService from "../../../Server/Services/ProjectSCIMService";
9
- import ProjectSCIM from "../../../Models/DatabaseModels/ProjectSCIM";
10
- import Errors from "../../../Server/Utils/Errors";
11
- import "../TestingUtils/Init";
12
- import ProjectServiceHelper from "../TestingUtils/Services/ProjectServiceHelper";
13
- import TeamMemberServiceHelper from "../TestingUtils/Services/TeamMemberServiceHelper";
14
- import TeamServiceHelper from "../TestingUtils/Services/TeamServiceHelper";
15
- import UserServiceHelper from "../TestingUtils/Services/UserServiceHelper";
16
- import { describe, expect, it } from "@jest/globals";
17
- import Email from "../../../Types/Email";
18
- import ObjectID from "../../../Types/ObjectID";
19
- import Project from "../../../Models/DatabaseModels/Project";
20
- import Team from "../../../Models/DatabaseModels/Team";
21
1
  import TeamMember from "../../../Models/DatabaseModels/TeamMember";
22
- import User from "../../../Models/DatabaseModels/User";
23
- import Faker from "../../../Utils/Faker";
24
- import UserService from "../../../Server/Services/UserService";
25
- import ProjectService from "../../../Server/Services/ProjectService";
26
- import TeamService from "../../../Server/Services/TeamService";
27
- import { TestDatabaseMock } from "../TestingUtils/__mocks__/TestDatabase.mock";
28
- import HTTPResponse from "../../../Types/API/HTTPResponse";
29
- import EmptyResponseData from "../../../Types/API/EmptyResponse";
30
-
31
- jest.setTimeout(60000); // Increase test timeout to 60 seconds becuase GitHub runners are slow
2
+ import ObjectID from "../../../Types/ObjectID";
3
+ import { describe, expect, test, beforeEach } from "@jest/globals";
32
4
 
33
- describe("TeamMemberService", () => {
34
- beforeEach(async () => {
35
- jest.resetAllMocks();
36
- // Re-setup the mock after resetAllMocks
37
- await TestDatabaseMock.connectDbMock();
38
- });
5
+ describe("TeamMember Model", () => {
6
+ let teamMember: TeamMember;
39
7
 
40
- afterEach(async () => {
41
- try {
42
- await TestDatabaseMock.disconnectDbMock();
43
- } catch {
44
- // Silently handle disconnect errors to prevent them from breaking tests
45
- }
8
+ beforeEach(() => {
9
+ teamMember = new TeamMember();
46
10
  });
47
11
 
48
- describe("create tests", () => {
49
- it("should create a new team member", async () => {
50
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
51
- null,
52
- {
53
- isRoot: true,
54
- },
55
- );
56
-
57
- const project: Project =
58
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
59
- isRoot: true,
60
- userId: user.id!,
61
- });
62
-
63
- const team: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
64
- {
65
- projectId: new ObjectID(project.id!),
66
- },
67
- {
68
- isRoot: true,
69
- },
70
- );
71
-
72
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember({
73
- projectId: new ObjectID(project._id!),
74
- userId: new ObjectID(user._id!),
75
- teamId: new ObjectID(team._id!),
76
- });
77
-
78
- const teamMember: TeamMember = await TeamMemberService.create({
79
- data: tm,
80
- props: { isRoot: true },
81
- });
82
-
83
- expect(teamMember.userId).toEqual(new ObjectID(user._id!));
84
- expect(teamMember.projectId).toEqual(new ObjectID(project._id!));
85
- expect(teamMember.hasAcceptedInvitation).toBeFalsy();
86
- });
87
-
88
- describe("onBeforeCreate", () => {
89
- it("should throw exception if the user limit for a project is reached", async () => {
90
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
91
- null,
92
- {
93
- isRoot: true,
94
- },
95
- );
96
-
97
- const project: Project =
98
- await ProjectServiceHelper.generateAndSaveRandomProject(
99
- {
100
- seatLimit: 2,
101
- },
102
- {
103
- isRoot: true,
104
- userId: user.id!,
105
- },
106
- );
107
-
108
- const team: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
109
- {
110
- projectId: new ObjectID(project.id!),
111
- },
112
- {
113
- isRoot: true,
114
- },
115
- );
116
-
117
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember(
118
- {
119
- projectId: new ObjectID(project._id!),
120
- userId: new ObjectID(user._id!),
121
- teamId: new ObjectID(team._id!),
122
- },
123
- );
124
-
125
- await TeamMemberService.create({
126
- data: tm,
127
- props: { isRoot: true },
128
- });
129
-
130
- // create another team member
131
-
132
- const user2: User = await UserServiceHelper.genrateAndSaveRandomUser(
133
- null,
134
- {
135
- isRoot: true,
136
- },
137
- );
138
-
139
- const tm2: TeamMember =
140
- TeamMemberServiceHelper.generateRandomTeamMember({
141
- projectId: new ObjectID(project._id!),
142
- userId: new ObjectID(user2._id!),
143
- teamId: new ObjectID(team._id!),
144
- });
145
-
146
- await TeamMemberService.create({
147
- data: tm2,
148
- props: { isRoot: true },
149
- });
150
-
151
- // add one more user.
152
-
153
- const user3: User = await UserServiceHelper.genrateAndSaveRandomUser(
154
- null,
155
- {
156
- isRoot: true,
157
- },
158
- );
159
-
160
- const tm3: TeamMember =
161
- TeamMemberServiceHelper.generateRandomTeamMember({
162
- projectId: new ObjectID(project._id!),
163
- userId: new ObjectID(user3._id!),
164
- teamId: new ObjectID(team._id!),
165
- });
166
-
167
- await expect(
168
- TeamMemberService.create({
169
- data: tm3,
170
- props: { isRoot: true },
171
- }),
172
- ).rejects.toThrow(Errors.TeamMemberService.LIMIT_REACHED);
173
- });
174
-
175
- it("should throw exception if the user has already been invited", async () => {
176
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
177
- null,
178
- {
179
- isRoot: true,
180
- },
181
- );
182
-
183
- const project: Project =
184
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
185
- isRoot: true,
186
- userId: user.id!,
187
- });
188
-
189
- const team: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
190
- {
191
- projectId: new ObjectID(project.id!),
192
- },
193
- {
194
- isRoot: true,
195
- },
196
- );
197
-
198
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember(
199
- {
200
- projectId: new ObjectID(project._id!),
201
- userId: new ObjectID(user._id!),
202
- teamId: new ObjectID(team._id!),
203
- },
204
- );
205
-
206
- jest.spyOn(AccessTokenService, "refreshUserGlobalAccessPermission");
207
-
208
- jest.spyOn(AccessTokenService, "refreshUserTenantAccessPermission");
209
-
210
- await TeamMemberService.create({
211
- data: tm,
212
- props: { isRoot: true },
213
- });
214
-
215
- await expect(
216
- TeamMemberService.create({
217
- data: tm,
218
- props: { isRoot: true },
219
- }),
220
- ).rejects.toThrow(Errors.TeamMemberService.ALREADY_INVITED);
221
- });
222
-
223
- it("should create user if the invited user does not exist in the system", async () => {
224
- jest
225
- .spyOn(MailService, "sendMail")
226
- .mockResolvedValue(
227
- Promise.resolve(new HTTPResponse<EmptyResponseData>(200, {}, {})),
228
- );
229
-
230
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
231
- null,
232
- {
233
- isRoot: true,
234
- },
235
- );
236
-
237
- const project: Project =
238
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
239
- isRoot: true,
240
- userId: user.id!,
241
- });
242
-
243
- const team: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
244
- {
245
- projectId: new ObjectID(project.id!),
246
- },
247
- {
248
- isRoot: true,
249
- },
250
- );
251
-
252
- const nonExistingUserEmail: string = Faker.generateEmail().toString();
253
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember(
254
- {
255
- projectId: new ObjectID(project._id!),
256
- teamId: new ObjectID(team._id!),
257
- },
258
- );
259
- const teamMember: TeamMember = await TeamMemberService.create({
260
- data: tm,
261
- miscDataProps: { email: nonExistingUserEmail },
262
- props: { isRoot: true },
263
- });
264
-
265
- expect(teamMember).toBeDefined();
266
- expect(teamMember.userId).toBeDefined();
267
- });
268
-
269
- it("should send email when inviting non existing user", async () => {
270
- jest
271
- .spyOn(MailService, "sendMail")
272
- .mockResolvedValue(
273
- Promise.resolve(new HTTPResponse<EmptyResponseData>(200, {}, {})),
274
- );
275
-
276
- // create project.
277
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
278
- null,
279
- {
280
- isRoot: true,
281
- },
282
- );
283
-
284
- const project: Project =
285
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
286
- isRoot: true,
287
- userId: user.id!,
288
- });
289
-
290
- const team: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
291
- {
292
- projectId: new ObjectID(project.id!),
293
- },
294
- {
295
- isRoot: true,
296
- },
297
- );
298
-
299
- const nonExistingUserEmail: string = Faker.generateEmail().toString();
300
-
301
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember(
302
- {
303
- projectId: new ObjectID(project._id!),
304
- teamId: new ObjectID(team._id!),
305
- },
306
- );
307
-
308
- jest.spyOn(AccessTokenService, "refreshUserGlobalAccessPermission");
309
-
310
- jest.spyOn(AccessTokenService, "refreshUserTenantAccessPermission");
311
-
312
- await TeamMemberService.create({
313
- data: tm,
314
- miscDataProps: { email: nonExistingUserEmail },
315
- props: { isRoot: true },
316
- });
317
-
318
- expect(MailService.sendMail).toHaveBeenCalledWith(
319
- {
320
- subject: `You have been invited to ${project.name}`,
321
- templateType: "InviteMember.hbs",
322
- toEmail: new Email(nonExistingUserEmail),
323
- vars: {
324
- homeUrl: `${HttpProtocol}${Host}/`,
325
- isNewUser: "true",
326
- projectName: project.name,
327
- registerLink: `${HttpProtocol}${Host}/accounts/register?email=${nonExistingUserEmail.replace(
328
- "@",
329
- "%40",
330
- )}`,
331
- signInLink: `${HttpProtocol}${Host}/accounts`,
332
- },
333
- },
334
- {
335
- projectId: new ObjectID(project._id!),
336
- },
337
- );
338
- });
339
-
340
- it("should block inviting users when SCIM push groups is enabled", async () => {
341
- const owner: User = await UserServiceHelper.genrateAndSaveRandomUser(
342
- null,
343
- {
344
- isRoot: true,
345
- },
346
- );
347
-
348
- const project: Project =
349
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
350
- isRoot: true,
351
- userId: owner.id!,
352
- });
353
-
354
- const team: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
355
- {
356
- projectId: new ObjectID(project.id!),
357
- },
358
- {
359
- isRoot: true,
360
- },
361
- );
362
-
363
- const memberUser: User =
364
- await UserServiceHelper.genrateAndSaveRandomUser(null, {
365
- isRoot: true,
366
- });
367
-
368
- const scimWithPushGroups: ProjectSCIM = new ProjectSCIM();
369
- scimWithPushGroups.projectId = new ObjectID(project._id!);
370
- scimWithPushGroups.name = "Test SCIM Push Groups";
371
- scimWithPushGroups.bearerToken = ObjectID.generate().toString();
372
- scimWithPushGroups.enablePushGroups = true;
373
-
374
- await ProjectSCIMService.create({
375
- data: scimWithPushGroups,
376
- props: {
377
- isRoot: true,
378
- },
379
- });
380
-
381
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember(
382
- {
383
- projectId: new ObjectID(project._id!),
384
- userId: new ObjectID(memberUser._id!),
385
- teamId: new ObjectID(team._id!),
386
- },
387
- );
388
-
389
- await expect(
390
- TeamMemberService.create({
391
- data: tm,
392
- props: { isRoot: false, tenantId: project.id! },
393
- }),
394
- ).rejects.toThrow(/SCIM Push Groups/i);
395
- });
396
-
397
- it("should allow inviting users when SCIM push groups is disabled", async () => {
398
- const owner: User = await UserServiceHelper.genrateAndSaveRandomUser(
399
- null,
400
- {
401
- isRoot: true,
402
- },
403
- );
404
-
405
- const project: Project =
406
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
407
- isRoot: true,
408
- userId: owner.id!,
409
- });
410
-
411
- const team: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
412
- {
413
- projectId: new ObjectID(project.id!),
414
- },
415
- {
416
- isRoot: true,
417
- },
418
- );
419
-
420
- const memberUser: User =
421
- await UserServiceHelper.genrateAndSaveRandomUser(null, {
422
- isRoot: true,
423
- });
424
-
425
- const scimWithoutPushGroups: ProjectSCIM = new ProjectSCIM();
426
- scimWithoutPushGroups.projectId = new ObjectID(project._id!);
427
- scimWithoutPushGroups.name = "Test SCIM without Push Groups";
428
- scimWithoutPushGroups.bearerToken = ObjectID.generate().toString();
429
- scimWithoutPushGroups.enablePushGroups = false;
430
-
431
- await ProjectSCIMService.create({
432
- data: scimWithoutPushGroups,
433
- props: {
434
- isRoot: true,
435
- },
436
- });
437
-
438
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember(
439
- {
440
- projectId: new ObjectID(project._id!),
441
- userId: new ObjectID(memberUser._id!),
442
- teamId: new ObjectID(team._id!),
443
- },
444
- );
445
-
446
- const teamMember: TeamMember = await TeamMemberService.create({
447
- data: tm,
448
- props: { isRoot: false, tenantId: project.id! },
449
- });
450
-
451
- expect(teamMember).toBeDefined();
452
- expect(teamMember.projectId?.toString()).toEqual(
453
- project._id?.toString(),
454
- );
455
- });
12
+ describe("constructor", () => {
13
+ test("should create a new TeamMember instance", () => {
14
+ expect(teamMember).toBeInstanceOf(TeamMember);
456
15
  });
457
16
 
458
- describe("onCreateSuccess", () => {
459
- it("should call functions to refresh tokens and update subscription seats on success", async () => {
460
- const refreshTokensSpy: jest.SpyInstance = jest.spyOn(
461
- TeamMemberService,
462
- "refreshTokens",
463
- );
464
-
465
- /*
466
- * const updateSeatsSpy: jest.SpyInstance = jest.spyOn(
467
- * TeamMemberService,
468
- * "updateSubscriptionSeatsByUniqueTeamMembersInProject",
469
- * );
470
- */
471
-
472
- const user: User = await UserService.create({
473
- data: UserServiceHelper.generateRandomUser(),
474
- props: { isRoot: true },
475
- });
476
-
477
- const project: Project = await ProjectService.create({
478
- data: ProjectServiceHelper.generateRandomProject(),
479
- props: { isRoot: true, userId: user.id! },
480
- });
481
-
482
- const team: Team = await TeamService.create({
483
- data: TeamServiceHelper.generateRandomTeam({
484
- projectId: new ObjectID(project._id!),
485
- }),
486
- props: { isRoot: true },
487
- });
488
-
489
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember(
490
- {
491
- projectId: new ObjectID(project._id!),
492
- userId: new ObjectID(user._id!),
493
- teamId: new ObjectID(team._id!),
494
- },
495
- );
496
-
497
- await TeamMemberService.create({
498
- data: tm,
499
- props: { isRoot: true },
500
- });
501
-
502
- expect(refreshTokensSpy).toHaveBeenCalledWith(user.id, project.id);
503
-
504
- // expect(updateSeatsSpy).toHaveBeenCalledWith(new ObjectID(project._id!));
505
- });
17
+ test("should create TeamMember with an ID", () => {
18
+ const id: ObjectID = ObjectID.generate();
19
+ const memberWithId: TeamMember = new TeamMember(id);
20
+ expect(memberWithId.id).toEqual(id);
506
21
  });
507
22
  });
508
23
 
509
- describe("update tests", () => {
510
- it("should update team member", async () => {
511
- // (1) create new team membe
512
-
513
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
514
- null,
515
- {
516
- isRoot: true,
517
- },
518
- );
519
-
520
- const project: Project =
521
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
522
- isRoot: true,
523
- userId: user.id!,
524
- });
525
-
526
- const team: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
527
- {
528
- projectId: new ObjectID(project.id!),
529
- },
530
- {
531
- isRoot: true,
532
- },
533
- );
534
-
535
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember({
536
- projectId: new ObjectID(project._id!),
537
- userId: new ObjectID(user._id!),
538
- teamId: new ObjectID(team._id!),
539
- });
540
-
541
- let teamMember: TeamMember | null = await TeamMemberService.create({
542
- data: tm,
543
- props: { isRoot: true },
544
- });
545
-
546
- // find team member
547
-
548
- teamMember = await TeamMemberService.findOneById({
549
- id: new ObjectID(teamMember._id!),
550
- select: {
551
- _id: true,
552
- hasAcceptedInvitation: true,
553
- },
554
- props: { isRoot: true },
555
- });
556
-
557
- expect(teamMember).toBeTruthy();
558
-
559
- expect(teamMember?.hasAcceptedInvitation).toBe(false);
560
-
561
- // (2) update team member
562
- const updatedInfo: { hasAcceptedInvitation: boolean } = {
563
- hasAcceptedInvitation: true,
564
- };
565
-
566
- const updatedCount: number = await TeamMemberService.updateOneBy({
567
- query: {
568
- _id: teamMember!._id!,
569
- },
570
- data: updatedInfo,
571
- props: { isRoot: true },
572
- });
573
-
574
- // check update was successful (1 document should be affected)
575
- expect(updatedCount).toBe(1);
576
-
577
- // (3) retrieve the updated team member and validate changes
578
- const updatedTeamMember: TeamMember | null =
579
- await TeamMemberService.findOneById({
580
- id: new ObjectID(teamMember!._id!),
581
- select: { hasAcceptedInvitation: true },
582
- props: { isRoot: true },
583
- });
584
-
585
- expect(updatedTeamMember).toBeTruthy();
586
- expect(updatedTeamMember?.hasAcceptedInvitation).toBe(
587
- updatedInfo.hasAcceptedInvitation,
588
- );
589
- });
590
-
591
- describe("onUpdateSuccess", () => {
592
- it("should refresh tokens and handle user notification settings on update", async () => {
593
- const user: User = await UserService.create({
594
- data: UserServiceHelper.generateRandomUser(),
595
- props: { isRoot: true },
596
- });
597
-
598
- const project: Project = await ProjectService.create({
599
- data: ProjectServiceHelper.generateRandomProject(),
600
- props: { isRoot: true, userId: user.id! },
601
- });
602
-
603
- const team: Team = await TeamService.create({
604
- data: TeamServiceHelper.generateRandomTeam({
605
- projectId: new ObjectID(project._id!),
606
- }),
607
- props: { isRoot: true },
608
- });
609
-
610
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember(
611
- {
612
- projectId: new ObjectID(project._id!),
613
- userId: new ObjectID(user._id!),
614
- teamId: new ObjectID(team._id!),
615
- },
616
- );
617
- const teamMember: TeamMember = await TeamMemberService.create({
618
- data: tm,
619
- props: { isRoot: true },
620
- });
621
-
622
- const refreshTokensSpy: jest.SpyInstance = jest.spyOn(
623
- TeamMemberService,
624
- "refreshTokens",
625
- );
626
-
627
- const addDefaultNotificationSettingsSpy: jest.SpyInstance = jest.spyOn(
628
- UserNotificationSettingService,
629
- "addDefaultNotificationSettingsForUser",
630
- );
631
-
632
- const addDefaultNotificationRuleForUserSpy: jest.SpyInstance =
633
- jest.spyOn(
634
- UserNotificationRuleService,
635
- "addDefaultNotificationRuleForUser",
636
- );
637
-
638
- const updatedInfo: { hasAcceptedInvitation: boolean } = {
639
- hasAcceptedInvitation: true,
640
- };
641
-
642
- await TeamMemberService.updateOneBy({
643
- query: { _id: teamMember._id! },
644
- data: updatedInfo,
645
- props: { isRoot: true },
646
- });
647
-
648
- expect(refreshTokensSpy).toHaveBeenCalledWith(
649
- teamMember.userId,
650
- teamMember.projectId,
651
- );
652
- expect(addDefaultNotificationSettingsSpy).toHaveBeenCalledWith(
653
- new ObjectID(user._id!),
654
- new ObjectID(project._id!),
655
- );
656
- expect(addDefaultNotificationRuleForUserSpy).toHaveBeenCalledWith(
657
- new ObjectID(project._id!),
658
- new ObjectID(user._id!),
659
- user.email,
660
- );
661
- });
24
+ describe("userId property", () => {
25
+ test("should set and get userId correctly", () => {
26
+ const userId: ObjectID = ObjectID.generate();
27
+ teamMember.userId = userId;
28
+ expect(teamMember.userId).toEqual(userId);
662
29
  });
663
30
  });
664
31
 
665
- describe("delete tests", () => {
666
- it("should delete team member", async () => {
667
- // (1) create new team member
668
- const user: User = await UserService.create({
669
- data: UserServiceHelper.generateRandomUser(),
670
- props: { isRoot: true },
671
- });
672
-
673
- const project: Project =
674
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
675
- isRoot: true,
676
- userId: user.id!,
677
- });
678
-
679
- const team: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
680
- {
681
- projectId: new ObjectID(project.id!),
682
- },
683
- {
684
- isRoot: true,
685
- },
686
- );
687
-
688
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember({
689
- projectId: new ObjectID(project._id!),
690
- userId: new ObjectID(user._id!),
691
- teamId: new ObjectID(team._id!),
692
- });
693
-
694
- const teamMember: TeamMember = await TeamMemberService.create({
695
- data: tm,
696
- props: { isRoot: true },
697
- });
698
-
699
- // (2) delete team member
700
- const deleteCount: number = await TeamMemberService.deleteOneBy({
701
- query: { _id: teamMember._id! },
702
- props: { isRoot: true },
703
- });
704
-
705
- // ensure deletion was successful (1 document should be affected)
706
- expect(deleteCount).toBe(1);
707
-
708
- // (3) verify that the team member no longer exists
709
- const deletedTeamMember: TeamMember | null =
710
- await TeamMemberService.findOneBy({
711
- query: { _id: teamMember._id! },
712
- props: { isRoot: true },
713
- });
714
-
715
- expect(deletedTeamMember).toBeNull();
716
- });
717
-
718
- describe("onBeforeDelete", () => {
719
- it("should throw error when one member and team has at least one member requirement", async () => {
720
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
721
- null,
722
- {
723
- isRoot: true,
724
- },
725
- );
726
-
727
- const project: Project =
728
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
729
- isRoot: true,
730
- userId: user.id!,
731
- });
732
-
733
- let team: Team = TeamServiceHelper.generateRandomTeam({
734
- projectId: new ObjectID(project._id!),
735
- });
736
-
737
- team.shouldHaveAtLeastOneMember = true;
738
-
739
- team = await TeamService.create({
740
- data: team,
741
- props: { isRoot: true },
742
- });
743
-
744
- // another user.
745
-
746
- const user2: User = await UserServiceHelper.genrateAndSaveRandomUser(
747
- null,
748
- {
749
- isRoot: true,
750
- },
751
- );
752
-
753
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember(
754
- {
755
- projectId: new ObjectID(project._id!),
756
- userId: new ObjectID(user2._id!),
757
- teamId: new ObjectID(team._id!),
758
- },
759
- );
760
-
761
- tm.hasAcceptedInvitation = true;
762
-
763
- const teamMember: TeamMember = await TeamMemberService.create({
764
- data: tm,
765
- props: { isRoot: true },
766
- });
767
-
768
- // accept invitation
769
-
770
- expect(
771
- TeamMemberService.deleteOneBy({
772
- query: {
773
- _id: teamMember._id!,
774
- },
775
- props: {
776
- isRoot: true,
777
- },
778
- }),
779
- ).rejects.toThrow(Errors.TeamMemberService.ONE_MEMBER_REQUIRED);
780
- });
781
-
782
- it("should not delete when shouldHaveAtLeastOneMember is true and member has not accepted invitation", async () => {
783
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
784
- null,
785
- {
786
- isRoot: true,
787
- },
788
- );
789
-
790
- const project: Project =
791
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
792
- isRoot: true,
793
- userId: user.id!,
794
- });
795
-
796
- let team: Team = TeamServiceHelper.generateRandomTeam({
797
- projectId: new ObjectID(project._id!),
798
- });
799
- team.shouldHaveAtLeastOneMember = true;
800
- team = await TeamService.create({
801
- data: team,
802
- props: { isRoot: true },
803
- });
804
-
805
- const tm: TeamMember = TeamMemberServiceHelper.generateRandomTeamMember(
806
- {
807
- projectId: new ObjectID(project._id!),
808
- userId: new ObjectID(user._id!),
809
- teamId: new ObjectID(team._id!),
810
- },
811
- );
812
- const teamMember: TeamMember = await TeamMemberService.create({
813
- data: tm,
814
- props: { isRoot: true },
815
- });
816
-
817
- await TeamMemberService.deleteOneBy({
818
- query: { _id: teamMember._id! },
819
- props: { isRoot: true },
820
- });
821
-
822
- const remainingMember: TeamMember | null =
823
- await TeamMemberService.findOneBy({
824
- query: { _id: teamMember._id! },
825
- props: { isRoot: true },
826
- });
827
- expect(remainingMember).toBeDefined();
828
- });
32
+ describe("teamId property", () => {
33
+ test("should set and get teamId correctly", () => {
34
+ const teamId: ObjectID = ObjectID.generate();
35
+ teamMember.teamId = teamId;
36
+ expect(teamMember.teamId).toEqual(teamId);
829
37
  });
830
38
  });
831
39
 
832
- describe("refreshTokens", () => {
833
- it("should refresh user global and tenant access permissions", async () => {
834
- // spy on refreshUserGlobalAccessPermission and refreshUserTenantAccessPermission
835
-
836
- jest.spyOn(AccessTokenService, "refreshUserGlobalAccessPermission");
837
- jest.spyOn(AccessTokenService, "refreshUserTenantAccessPermission");
838
-
839
- const user: User = await UserService.create({
840
- data: UserServiceHelper.generateRandomUser(),
841
- props: { isRoot: true },
842
- });
843
-
844
- const project: Project = await ProjectService.create({
845
- data: ProjectServiceHelper.generateRandomProject(),
846
- props: { isRoot: true, userId: user.id! },
847
- });
848
-
849
- const userId: ObjectID = new ObjectID(user._id!);
850
- const projectId: ObjectID = new ObjectID(project._id!);
851
-
852
- await TeamMemberService.refreshTokens(userId, projectId);
853
-
854
- expect(
855
- AccessTokenService.refreshUserGlobalAccessPermission,
856
- ).toHaveBeenCalledWith(userId);
857
- expect(
858
- AccessTokenService.refreshUserTenantAccessPermission,
859
- ).toHaveBeenCalledWith(userId, projectId);
40
+ describe("projectId property", () => {
41
+ test("should set and get projectId correctly", () => {
42
+ const projectId: ObjectID = ObjectID.generate();
43
+ teamMember.projectId = projectId;
44
+ expect(teamMember.projectId).toEqual(projectId);
860
45
  });
861
46
  });
862
47
 
863
- describe("getUniqueTeamMemberCountInProject", () => {
864
- it("should return the count of unique team members in a project", async () => {
865
- /*
866
- * make findBy to return 4 team members: 1 normal, 2 with the same id and 1 without a user ID
867
- * total should be 2 unique team members
868
- */
869
-
870
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
871
- null,
872
- {
873
- isRoot: true,
874
- },
875
- );
876
-
877
- const project: Project =
878
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
879
- isRoot: true,
880
- userId: user.id!,
881
- });
882
-
883
- const teamA: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
884
- {
885
- projectId: new ObjectID(project.id!),
886
- },
887
- {
888
- isRoot: true,
889
- },
890
- );
891
-
892
- const teamB: Team = await TeamServiceHelper.generateAndSaveRandomTeam(
893
- {
894
- projectId: new ObjectID(project.id!),
895
- },
896
- {
897
- isRoot: true,
898
- },
899
- );
900
-
901
- // user A
902
-
903
- const user1: User = await UserService.create({
904
- data: UserServiceHelper.generateRandomUser(),
905
- props: { isRoot: true },
906
- });
907
-
908
- // user B
909
-
910
- const user2: User = await UserService.create({
911
- data: UserServiceHelper.generateRandomUser(),
912
- props: { isRoot: true },
913
- });
914
-
915
- // add these to team A
916
-
917
- const teamMemberA1: TeamMember =
918
- TeamMemberServiceHelper.generateRandomTeamMember({
919
- projectId: new ObjectID(project._id!),
920
- userId: new ObjectID(user1._id!),
921
- teamId: new ObjectID(teamA._id!),
922
- });
923
-
924
- await TeamMemberService.create({
925
- data: teamMemberA1,
926
- props: { isRoot: true },
927
- });
928
-
929
- const teamMemberA2: TeamMember =
930
- TeamMemberServiceHelper.generateRandomTeamMember({
931
- projectId: new ObjectID(project._id!),
932
- userId: new ObjectID(user2._id!),
933
- teamId: new ObjectID(teamA._id!),
934
- });
935
-
936
- await TeamMemberService.create({
937
- data: teamMemberA2,
938
- props: { isRoot: true },
939
- });
940
-
941
- // add user 2 to team B
942
-
943
- const teamMemberB2: TeamMember =
944
- TeamMemberServiceHelper.generateRandomTeamMember({
945
- projectId: new ObjectID(project._id!),
946
- userId: new ObjectID(user2._id!),
947
- teamId: new ObjectID(teamB._id!),
948
- });
48
+ describe("hasAcceptedInvitation property", () => {
49
+ test("should default to false or undefined", () => {
50
+ expect(teamMember.hasAcceptedInvitation).toBeFalsy();
51
+ });
949
52
 
950
- await TeamMemberService.create({
951
- data: teamMemberB2,
952
- props: { isRoot: true },
953
- });
53
+ test("should set hasAcceptedInvitation to true", () => {
54
+ teamMember.hasAcceptedInvitation = true;
55
+ expect(teamMember.hasAcceptedInvitation).toBe(true);
56
+ });
954
57
 
955
- const count: number =
956
- await TeamMemberService.getUniqueTeamMemberCountInProject(
957
- new ObjectID(project._id!),
958
- );
959
- expect(count).toBe(3); // user, user1, user2
58
+ test("should set hasAcceptedInvitation to false explicitly", () => {
59
+ teamMember.hasAcceptedInvitation = false;
60
+ expect(teamMember.hasAcceptedInvitation).toBe(false);
960
61
  });
961
62
  });
962
63
 
963
- describe("getUsersInTeam(s)", () => {
964
- it("should return users in specified team", async () => {
965
- /*
966
- * team A members: user1 & user2
967
- * team B members: user2 & user3
968
- * team C members: user 3
969
- */
970
-
971
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
972
- null,
973
- {
974
- isRoot: true,
975
- },
976
- );
977
-
978
- const project: Project =
979
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
980
- isRoot: true,
981
- userId: user.id!,
982
- });
983
-
984
- const user1: User = await UserService.create({
985
- data: UserServiceHelper.generateRandomUser(),
986
- props: { isRoot: true },
987
- });
988
- const user2: User = await UserService.create({
989
- data: UserServiceHelper.generateRandomUser(),
990
- props: { isRoot: true },
991
- });
992
- const user3: User = await UserService.create({
993
- data: UserServiceHelper.generateRandomUser(),
994
- props: { isRoot: true },
995
- });
996
-
997
- const teamA: Team = await TeamService.create({
998
- data: TeamServiceHelper.generateRandomTeam({
999
- projectId: new ObjectID(project._id!),
1000
- }),
1001
- props: {
1002
- isRoot: true,
1003
- },
1004
- });
1005
-
1006
- const teamMemberA1: TeamMember =
1007
- TeamMemberServiceHelper.generateRandomTeamMember({
1008
- projectId: new ObjectID(project._id!),
1009
- userId: new ObjectID(user1._id!),
1010
- teamId: new ObjectID(teamA._id!),
1011
- });
1012
-
1013
- await TeamMemberService.create({
1014
- data: teamMemberA1,
1015
- props: { isRoot: true },
1016
- });
1017
-
1018
- const teamMemberA2: TeamMember =
1019
- TeamMemberServiceHelper.generateRandomTeamMember({
1020
- projectId: new ObjectID(project._id!),
1021
- userId: new ObjectID(user2._id!),
1022
- teamId: new ObjectID(teamA._id!),
1023
- });
1024
-
1025
- await TeamMemberService.create({
1026
- data: teamMemberA2,
1027
- props: { isRoot: true },
1028
- });
1029
-
1030
- let teamB: Team = TeamServiceHelper.generateRandomTeam({
1031
- projectId: new ObjectID(project._id!),
1032
- });
1033
-
1034
- teamB = await TeamService.create({
1035
- data: teamB,
1036
- props: { isRoot: true },
1037
- });
1038
-
1039
- const teamMemberB2: TeamMember =
1040
- TeamMemberServiceHelper.generateRandomTeamMember({
1041
- projectId: new ObjectID(project._id!),
1042
- userId: new ObjectID(user2._id!),
1043
- teamId: new ObjectID(teamB._id!),
1044
- });
1045
- await TeamMemberService.create({
1046
- data: teamMemberB2,
1047
- props: { isRoot: true },
1048
- });
1049
-
1050
- const teamMemberB3: TeamMember =
1051
- TeamMemberServiceHelper.generateRandomTeamMember({
1052
- projectId: new ObjectID(project._id!),
1053
- userId: new ObjectID(user3._id!),
1054
- teamId: new ObjectID(teamB._id!),
1055
- });
1056
-
1057
- await TeamMemberService.create({
1058
- data: teamMemberB3,
1059
- props: { isRoot: true },
1060
- });
1061
-
1062
- let teamC: Team = TeamServiceHelper.generateRandomTeam({
1063
- projectId: new ObjectID(project._id!),
1064
- });
1065
-
1066
- teamC = await TeamService.create({
1067
- data: teamC,
1068
- props: { isRoot: true },
1069
- });
1070
-
1071
- const teamMemberC3: TeamMember =
1072
- TeamMemberServiceHelper.generateRandomTeamMember({
1073
- projectId: new ObjectID(project._id!),
1074
- userId: new ObjectID(user3._id!),
1075
- teamId: new ObjectID(teamC._id!),
1076
- });
1077
- await TeamMemberService.create({
1078
- data: teamMemberC3,
1079
- props: { isRoot: true },
1080
- });
1081
-
1082
- expect(
1083
- await TeamMemberService.getUsersInTeam(new ObjectID(teamA._id!)),
1084
- ).toHaveLength(2);
1085
- expect(
1086
- await TeamMemberService.getUsersInTeam(new ObjectID(teamB._id!)),
1087
- ).toHaveLength(2);
1088
- expect(
1089
- await TeamMemberService.getUsersInTeam(new ObjectID(teamC._id!)),
1090
- ).toHaveLength(1);
64
+ describe("TeamMember with full data", () => {
65
+ test("should handle complete team member record", () => {
66
+ const id: ObjectID = ObjectID.generate();
67
+ const userId: ObjectID = ObjectID.generate();
68
+ const teamId: ObjectID = ObjectID.generate();
69
+ const projectId: ObjectID = ObjectID.generate();
70
+
71
+ const fullMember: TeamMember = new TeamMember(id);
72
+ fullMember.userId = userId;
73
+ fullMember.teamId = teamId;
74
+ fullMember.projectId = projectId;
75
+ fullMember.hasAcceptedInvitation = true;
76
+
77
+ expect(fullMember.id).toEqual(id);
78
+ expect(fullMember.userId).toEqual(userId);
79
+ expect(fullMember.teamId).toEqual(teamId);
80
+ expect(fullMember.projectId).toEqual(projectId);
81
+ expect(fullMember.hasAcceptedInvitation).toBe(true);
1091
82
  });
1092
83
 
1093
- it("should return users in multiple teams", async () => {
1094
- /*
1095
- * team A members: user1 & user2
1096
- * team B members: user2 & user3
1097
- * team C members: user 3
1098
- */
1099
-
1100
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
1101
- null,
1102
- {
1103
- isRoot: true,
1104
- },
1105
- );
1106
-
1107
- const project: Project =
1108
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
1109
- isRoot: true,
1110
- userId: user.id!,
1111
- });
1112
-
1113
- const user1: User = await UserService.create({
1114
- data: UserServiceHelper.generateRandomUser(),
1115
- props: { isRoot: true },
1116
- });
1117
- const user2: User = await UserService.create({
1118
- data: UserServiceHelper.generateRandomUser(),
1119
- props: { isRoot: true },
1120
- });
1121
- const user3: User = await UserService.create({
1122
- data: UserServiceHelper.generateRandomUser(),
1123
- props: { isRoot: true },
1124
- });
84
+ test("should create team member with minimal data", () => {
85
+ const minMember: TeamMember = new TeamMember();
86
+ const userId: ObjectID = ObjectID.generate();
87
+ minMember.userId = userId;
1125
88
 
1126
- const teamA: Team = await TeamService.create({
1127
- data: TeamServiceHelper.generateRandomTeam({
1128
- projectId: new ObjectID(project._id!),
1129
- }),
1130
- props: {
1131
- isRoot: true,
1132
- },
1133
- });
1134
-
1135
- const teamMemberA1: TeamMember =
1136
- TeamMemberServiceHelper.generateRandomTeamMember({
1137
- projectId: new ObjectID(project._id!),
1138
- userId: new ObjectID(user1._id!),
1139
- teamId: new ObjectID(teamA._id!),
1140
- });
1141
-
1142
- await TeamMemberService.create({
1143
- data: teamMemberA1,
1144
- props: { isRoot: true },
1145
- });
1146
-
1147
- const teamMemberA2: TeamMember =
1148
- TeamMemberServiceHelper.generateRandomTeamMember({
1149
- projectId: new ObjectID(project._id!),
1150
- userId: new ObjectID(user2._id!),
1151
- teamId: new ObjectID(teamA._id!),
1152
- });
1153
-
1154
- await TeamMemberService.create({
1155
- data: teamMemberA2,
1156
- props: { isRoot: true },
1157
- });
1158
-
1159
- const teamB: Team = await TeamService.create({
1160
- data: TeamServiceHelper.generateRandomTeam({
1161
- projectId: new ObjectID(project._id!),
1162
- }),
1163
- props: {
1164
- isRoot: true,
1165
- },
1166
- });
1167
-
1168
- const teamMemberB2: TeamMember =
1169
- TeamMemberServiceHelper.generateRandomTeamMember({
1170
- projectId: new ObjectID(project._id!),
1171
- userId: new ObjectID(user2._id!),
1172
- teamId: new ObjectID(teamB._id!),
1173
- });
1174
- await TeamMemberService.create({
1175
- data: teamMemberB2,
1176
- props: { isRoot: true },
1177
- });
1178
-
1179
- const teamMemberB3: TeamMember =
1180
- TeamMemberServiceHelper.generateRandomTeamMember({
1181
- projectId: new ObjectID(project._id!),
1182
- userId: new ObjectID(user3._id!),
1183
- teamId: new ObjectID(teamB._id!),
1184
- });
1185
-
1186
- await TeamMemberService.create({
1187
- data: teamMemberB3,
1188
- props: { isRoot: true },
1189
- });
1190
-
1191
- const teamC: Team = await TeamService.create({
1192
- data: TeamServiceHelper.generateRandomTeam({
1193
- projectId: new ObjectID(project._id!),
1194
- }),
1195
- props: {
1196
- isRoot: true,
1197
- },
1198
- });
1199
-
1200
- const teamMemberC3: TeamMember =
1201
- TeamMemberServiceHelper.generateRandomTeamMember({
1202
- projectId: new ObjectID(project._id!),
1203
- userId: new ObjectID(user3._id!),
1204
- teamId: new ObjectID(teamC._id!),
1205
- });
1206
-
1207
- await TeamMemberService.create({
1208
- data: teamMemberC3,
1209
- props: { isRoot: true },
1210
- });
1211
-
1212
- expect(
1213
- await TeamMemberService.getUsersInTeams([
1214
- new ObjectID(teamA._id!),
1215
- new ObjectID(teamB._id!),
1216
- new ObjectID(teamC._id!),
1217
- ]),
1218
- ).toHaveLength(3);
89
+ expect(minMember.userId).toEqual(userId);
90
+ expect(minMember.teamId).toBeUndefined();
91
+ expect(minMember.hasAcceptedInvitation).toBeFalsy();
1219
92
  });
1220
93
  });
1221
94
 
1222
- describe("updateSubscriptionSeatsByUniqueTeamMembersInProject", () => {
1223
- it("should update subscription seats based on unique team members", async () => {
1224
- // spy on change quantity
1225
- jest.spyOn(BillingService, "changeQuantity").mockResolvedValue();
1226
-
1227
- // spy on update project
1228
- jest.spyOn(ProjectService, "updateOneById");
95
+ describe("Multiple team members", () => {
96
+ test("should create distinct team member instances", () => {
97
+ const member1: TeamMember = new TeamMember();
98
+ const member2: TeamMember = new TeamMember();
1229
99
 
1230
- const user1: User = await UserService.create({
1231
- data: UserServiceHelper.generateRandomUser(),
1232
- props: { isRoot: true },
1233
- });
100
+ const userId1: ObjectID = ObjectID.generate();
101
+ const userId2: ObjectID = ObjectID.generate();
1234
102
 
1235
- let project: Project | null = await ProjectService.create({
1236
- data: ProjectServiceHelper.generateRandomProject(),
1237
- props: { isRoot: true, userId: user1.id! },
1238
- });
103
+ member1.userId = userId1;
104
+ member2.userId = userId2;
1239
105
 
1240
- // get subscription id from project
1241
-
1242
- project = await ProjectService.findOneById({
1243
- id: new ObjectID(project._id!),
1244
- select: { paymentProviderSubscriptionId: true },
1245
- props: { isRoot: true },
1246
- });
1247
-
1248
- // expect not null
1249
-
1250
- expect(project).not.toBeNull();
1251
-
1252
- // add another team
1253
-
1254
- const teamA: Team = await TeamService.create({
1255
- data: TeamServiceHelper.generateRandomTeam({
1256
- projectId: new ObjectID(project!._id!),
1257
- }),
1258
- props: { isRoot: true },
1259
- });
1260
-
1261
- // add another user
1262
-
1263
- const userX: User = await UserService.create({
1264
- data: UserServiceHelper.generateRandomUser(),
1265
- props: { isRoot: true },
1266
- });
1267
-
1268
- // add user to team
1269
-
1270
- await TeamMemberService.create({
1271
- data: TeamMemberServiceHelper.generateRandomTeamMember({
1272
- projectId: new ObjectID(project!._id!),
1273
- userId: new ObjectID(userX.id!),
1274
- teamId: new ObjectID(teamA._id!),
1275
- }),
1276
- props: { isRoot: true },
1277
- });
1278
-
1279
- expect(BillingService.changeQuantity).toHaveBeenCalledWith(
1280
- project!.paymentProviderSubscriptionId!,
1281
- 2,
1282
- );
1283
-
1284
- expect(ProjectService.updateOneById).toHaveBeenCalledWith(
1285
- expect.objectContaining({
1286
- id: new ObjectID(project!._id!),
1287
- data: { paymentProviderSubscriptionSeats: 2 },
1288
- props: { isRoot: true },
1289
- }),
1290
- );
1291
-
1292
- // now add users.
1293
-
1294
- const user2: User = await UserService.create({
1295
- data: UserServiceHelper.generateRandomUser(),
1296
- props: { isRoot: true },
1297
- });
1298
-
1299
- // add team
1300
-
1301
- const team: Team = await TeamService.create({
1302
- data: TeamServiceHelper.generateRandomTeam({
1303
- projectId: new ObjectID(project!._id!),
1304
- }),
1305
- props: { isRoot: true },
1306
- });
1307
-
1308
- // add team members
1309
-
1310
- await TeamMemberService.create({
1311
- data: TeamMemberServiceHelper.generateRandomTeamMember({
1312
- projectId: new ObjectID(project!._id!),
1313
- userId: new ObjectID(user1.id!),
1314
- teamId: new ObjectID(team._id!),
1315
- }),
1316
- props: { isRoot: true },
1317
- });
1318
-
1319
- await TeamMemberService.create({
1320
- data: TeamMemberServiceHelper.generateRandomTeamMember({
1321
- projectId: new ObjectID(project!._id!),
1322
- userId: new ObjectID(user2.id!),
1323
- teamId: new ObjectID(team._id!),
1324
- }),
1325
- props: { isRoot: true },
1326
- });
1327
-
1328
- // update subscription seats
1329
-
1330
- await TeamMemberService.updateSubscriptionSeatsByUniqueTeamMembersInProject(
1331
- new ObjectID(project!._id!),
1332
- );
1333
-
1334
- expect(BillingService.changeQuantity).toHaveBeenCalledWith(
1335
- project!.paymentProviderSubscriptionId!,
1336
- 2,
1337
- );
1338
-
1339
- expect(ProjectService.updateOneById).toHaveBeenCalledWith({
1340
- id: new ObjectID(project!._id!),
1341
- data: { paymentProviderSubscriptionSeats: 2 },
1342
- props: { isRoot: true },
1343
- });
106
+ expect(member1.userId).toEqual(userId1);
107
+ expect(member2.userId).toEqual(userId2);
108
+ expect(member1.userId).not.toEqual(member2.userId);
1344
109
  });
1345
110
 
1346
- it("should not update subscription seats if there are no plans", async () => {
1347
- jest.mock("../../../Server/EnvironmentConfig", () => {
1348
- // Require the original module to not be mocked...
1349
- const originalModule: any = jest.requireActual(
1350
- "../../../Server/EnvironmentConfig",
1351
- );
1352
- return {
1353
- ...originalModule,
1354
- IsBillingEnabled: false,
1355
- };
1356
- });
1357
-
1358
- // spy on change quantity
1359
- jest.spyOn(BillingService, "changeQuantity");
1360
-
1361
- // spy on update project
1362
- jest.spyOn(ProjectService, "updateOneById");
1363
-
1364
- const user: User = await UserServiceHelper.genrateAndSaveRandomUser(
1365
- null,
1366
- {
1367
- isRoot: true,
1368
- },
1369
- );
111
+ test("should allow same user in different teams", () => {
112
+ const userId: ObjectID = ObjectID.generate();
113
+ const teamId1: ObjectID = ObjectID.generate();
114
+ const teamId2: ObjectID = ObjectID.generate();
1370
115
 
1371
- const project: Project =
1372
- await ProjectServiceHelper.generateAndSaveRandomProject(null, {
1373
- isRoot: true,
1374
- userId: user.id!,
1375
- });
116
+ const member1: TeamMember = new TeamMember();
117
+ member1.userId = userId;
118
+ member1.teamId = teamId1;
1376
119
 
1377
- await TeamMemberService.updateSubscriptionSeatsByUniqueTeamMembersInProject(
1378
- new ObjectID(project._id!),
1379
- );
120
+ const member2: TeamMember = new TeamMember();
121
+ member2.userId = userId;
122
+ member2.teamId = teamId2;
1380
123
 
1381
- expect(BillingService.changeQuantity).not.toHaveBeenCalled();
124
+ expect(member1.userId).toEqual(member2.userId);
125
+ expect(member1.teamId).not.toEqual(member2.teamId);
1382
126
  });
1383
127
  });
1384
128
  });