user-groups.test.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. import type { IGrantedGroup } from '@growi/core';
  2. import { GroupType, getIdForRef, type IPage, PageGrant } from '@growi/core';
  3. import mongoose from 'mongoose';
  4. import { PageActionOnGroupDelete } from '../../../src/interfaces/user-group';
  5. import type Crowi from '../../../src/server/crowi';
  6. import type { PageDocument, PageModel } from '../../../src/server/models/page';
  7. import UserGroup from '../../../src/server/models/user-group';
  8. import UserGroupRelation from '../../../src/server/models/user-group-relation';
  9. import type { IUserGroupService } from '../../../src/server/service/user-group';
  10. import { getInstance } from '../setup-crowi';
  11. describe('UserGroupService', () => {
  12. let crowi: Crowi;
  13. // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
  14. let User;
  15. let Page: PageModel;
  16. let userGroupService: IUserGroupService;
  17. const groupId1 = new mongoose.Types.ObjectId();
  18. const groupId2 = new mongoose.Types.ObjectId();
  19. const groupId3 = new mongoose.Types.ObjectId();
  20. const groupId4 = new mongoose.Types.ObjectId();
  21. const groupId5 = new mongoose.Types.ObjectId();
  22. const groupId6 = new mongoose.Types.ObjectId();
  23. const groupId7 = new mongoose.Types.ObjectId();
  24. const groupId8 = new mongoose.Types.ObjectId();
  25. const groupId9 = new mongoose.Types.ObjectId();
  26. const groupId10 = new mongoose.Types.ObjectId();
  27. const groupId11 = new mongoose.Types.ObjectId();
  28. const groupId12 = new mongoose.Types.ObjectId();
  29. const groupId13 = new mongoose.Types.ObjectId();
  30. const groupId14 = new mongoose.Types.ObjectId();
  31. const groupId15 = new mongoose.Types.ObjectId();
  32. const userId1 = new mongoose.Types.ObjectId();
  33. // biome-ignore lint/suspicious/noImplicitAnyLet: ignore
  34. let user1;
  35. const pageId1 = new mongoose.Types.ObjectId();
  36. const pageId2 = new mongoose.Types.ObjectId();
  37. let rootPage: PageDocument | null;
  38. // normalize for result comparison
  39. const normalizeGrantedGroups = (
  40. grantedGroups: IGrantedGroup[] | undefined,
  41. ) => {
  42. if (grantedGroups == null) {
  43. return null;
  44. }
  45. return grantedGroups.map((group) => {
  46. return { item: getIdForRef(group.item), type: group.type };
  47. });
  48. };
  49. beforeAll(async () => {
  50. crowi = await getInstance();
  51. User = mongoose.model('User');
  52. Page = mongoose.model<IPage, PageModel>('Page');
  53. rootPage = await Page.findOne({ path: '/' });
  54. userGroupService = crowi.userGroupService!;
  55. await User.insertMany([
  56. // ug -> User Group
  57. {
  58. _id: userId1,
  59. name: 'ug_test_user1',
  60. username: 'ug_test_user1',
  61. email: 'ug_test_user1@example.com',
  62. },
  63. ]);
  64. user1 = await User.findOne({ _id: userId1 });
  65. // Create Groups
  66. await UserGroup.insertMany([
  67. // No parent
  68. {
  69. _id: groupId1,
  70. name: 'v5_group1',
  71. description: 'description1',
  72. },
  73. // No parent
  74. {
  75. _id: groupId2,
  76. name: 'v5_group2',
  77. description: 'description2',
  78. },
  79. // No parent
  80. {
  81. _id: groupId3,
  82. name: 'v5_group3',
  83. description: 'description3',
  84. },
  85. // No parent
  86. {
  87. _id: groupId4,
  88. name: 'v5_group4',
  89. description: 'description4',
  90. },
  91. // No parent
  92. {
  93. _id: groupId5,
  94. name: 'v5_group5',
  95. description: 'description5',
  96. },
  97. // No parent
  98. {
  99. _id: groupId6,
  100. name: 'v5_group6',
  101. description: 'description6',
  102. },
  103. // No parent
  104. {
  105. _id: groupId7,
  106. name: 'v5_group7',
  107. description: 'description7',
  108. parent: groupId6,
  109. },
  110. // No parent
  111. {
  112. _id: groupId8,
  113. name: 'v5_group8',
  114. description: 'description8',
  115. },
  116. {
  117. _id: groupId9,
  118. name: 'v5_group9',
  119. description: 'description9',
  120. },
  121. {
  122. _id: groupId10,
  123. name: 'v5_group10',
  124. description: 'description10',
  125. parent: groupId9,
  126. },
  127. {
  128. _id: groupId11,
  129. name: 'v5_group11',
  130. description: 'descriptio11',
  131. },
  132. {
  133. _id: groupId12,
  134. name: 'v5_group12',
  135. description: 'description12',
  136. parent: groupId11,
  137. },
  138. // for removeCompletelyByRootGroupId test
  139. {
  140. _id: groupId13,
  141. name: 'v5_group13',
  142. description: 'description13',
  143. },
  144. {
  145. _id: groupId14,
  146. name: 'v5_group14',
  147. description: 'description14',
  148. parent: groupId13,
  149. },
  150. {
  151. _id: groupId15,
  152. name: 'v5_group15',
  153. description: 'description15',
  154. parent: groupId15,
  155. },
  156. ]);
  157. // Create UserGroupRelations
  158. await UserGroupRelation.insertMany([
  159. {
  160. relatedGroup: groupId4,
  161. relatedUser: userId1,
  162. },
  163. {
  164. relatedGroup: groupId6,
  165. relatedUser: userId1,
  166. },
  167. {
  168. relatedGroup: groupId8,
  169. relatedUser: userId1,
  170. },
  171. {
  172. relatedGroup: groupId9,
  173. relatedUser: userId1,
  174. },
  175. {
  176. relatedGroup: groupId10,
  177. relatedUser: userId1,
  178. },
  179. {
  180. relatedGroup: groupId11,
  181. relatedUser: userId1,
  182. },
  183. {
  184. relatedGroup: groupId12,
  185. relatedUser: userId1,
  186. },
  187. ]);
  188. await Page.insertMany([
  189. {
  190. _id: pageId1,
  191. path: '/canBePublicized',
  192. grant: PageGrant.GRANT_USER_GROUP,
  193. creator: userId1,
  194. lastUpdateUser: userId1,
  195. grantedGroups: [
  196. { item: groupId13, type: GroupType.userGroup },
  197. { item: groupId14, type: GroupType.userGroup },
  198. ],
  199. parent: rootPage?._id,
  200. },
  201. {
  202. _id: pageId2,
  203. path: '/cannotBePublicized',
  204. grant: PageGrant.GRANT_USER_GROUP,
  205. creator: userId1,
  206. lastUpdateUser: userId1,
  207. grantedGroups: [
  208. { item: groupId13, type: GroupType.userGroup },
  209. { item: groupId15, type: GroupType.userGroup },
  210. ],
  211. parent: rootPage?._id,
  212. },
  213. ]);
  214. });
  215. /*
  216. * Update UserGroup
  217. */
  218. describe('updateGroup', () => {
  219. test('Updated values should be reflected. (name, description, parent)', async () => {
  220. const userGroup2 = await UserGroup.findOne({ _id: groupId2 });
  221. const newGroupName = 'v5_group1_new';
  222. const newGroupDescription = 'description1_new';
  223. const newParentId = userGroup2?._id;
  224. const updatedUserGroup = await userGroupService.updateGroup(
  225. groupId1,
  226. newGroupName,
  227. newGroupDescription,
  228. newParentId,
  229. );
  230. expect(updatedUserGroup.name).toBe(newGroupName);
  231. expect(updatedUserGroup.description).toBe(newGroupDescription);
  232. expect(updatedUserGroup.parent).toStrictEqual(newParentId);
  233. });
  234. test('Should throw an error when trying to set existing group name', async () => {
  235. const userGroup2 = await UserGroup.findOne({ _id: groupId2 });
  236. const result = userGroupService.updateGroup(groupId1, userGroup2?.name);
  237. await expect(result).rejects.toThrow('The group name is already taken');
  238. });
  239. test('Parent should be null when parent group is released', async () => {
  240. const userGroup = await UserGroup.findOne({ _id: groupId3 });
  241. const updatedUserGroup = await userGroupService.updateGroup(
  242. userGroup?._id,
  243. userGroup?.name,
  244. userGroup?.description,
  245. null,
  246. );
  247. expect(updatedUserGroup.parent).toBeNull();
  248. });
  249. /*
  250. * forceUpdateParents: false
  251. */
  252. test('Should throw an error when users in child group do not exist in parent group', async () => {
  253. const userGroup4 = await UserGroup.findOne({
  254. _id: groupId4,
  255. parent: null,
  256. });
  257. const result = userGroupService.updateGroup(
  258. userGroup4?._id,
  259. userGroup4?.name,
  260. userGroup4?.description,
  261. groupId5,
  262. );
  263. await expect(result).rejects.toThrow(
  264. 'The parent group does not contain the users in this group.',
  265. );
  266. });
  267. /*
  268. * forceUpdateParents: true
  269. */
  270. test('User should be included to parent group (2 groups ver)', async () => {
  271. const userGroup4 = await UserGroup.findOne({
  272. _id: groupId4,
  273. parent: null,
  274. });
  275. const userGroup5 = await UserGroup.findOne({
  276. _id: groupId5,
  277. parent: null,
  278. });
  279. // userGroup4 has userId1
  280. const userGroupRelation4BeforeUpdate = await UserGroupRelation.findOne({
  281. relatedGroup: userGroup4,
  282. relatedUser: userId1,
  283. });
  284. expect(userGroupRelation4BeforeUpdate).not.toBeNull();
  285. // userGroup5 has not userId1
  286. const userGroupRelation5BeforeUpdate = await UserGroupRelation.findOne({
  287. relatedGroup: userGroup5,
  288. relatedUser: userId1,
  289. });
  290. expect(userGroupRelation5BeforeUpdate).toBeNull();
  291. // update userGroup4's parent with userGroup5 (forceUpdate: true)
  292. const forceUpdateParents = true;
  293. const updatedUserGroup = await userGroupService.updateGroup(
  294. userGroup4?._id,
  295. userGroup4?.name,
  296. userGroup4?.description,
  297. groupId5,
  298. forceUpdateParents,
  299. );
  300. expect(updatedUserGroup.parent).toStrictEqual(groupId5);
  301. // userGroup5 should have userId1
  302. const userGroupRelation5AfterUpdate = await UserGroupRelation.findOne({
  303. relatedGroup: groupId5,
  304. relatedUser: userGroupRelation4BeforeUpdate?.relatedUser,
  305. });
  306. expect(userGroupRelation5AfterUpdate).not.toBeNull();
  307. });
  308. test('User should be included to parent group (3 groups ver)', async () => {
  309. const userGroup8 = await UserGroup.findOne({
  310. _id: groupId8,
  311. parent: null,
  312. });
  313. // userGroup7 has not userId1
  314. const userGroupRelation6BeforeUpdate = await UserGroupRelation.findOne({
  315. relatedGroup: groupId6,
  316. relatedUser: userId1,
  317. });
  318. const userGroupRelation7BeforeUpdate = await UserGroupRelation.findOne({
  319. relatedGroup: groupId7,
  320. relatedUser: userId1,
  321. });
  322. const userGroupRelation8BeforeUpdate = await UserGroupRelation.findOne({
  323. relatedGroup: groupId8,
  324. relatedUser: userId1,
  325. });
  326. expect(userGroupRelation6BeforeUpdate).not.toBeNull();
  327. // userGroup7 does not have userId1
  328. expect(userGroupRelation7BeforeUpdate).toBeNull();
  329. expect(userGroupRelation8BeforeUpdate).not.toBeNull();
  330. // update userGroup8's parent with userGroup7 (forceUpdate: true)
  331. const forceUpdateParents = true;
  332. await userGroupService.updateGroup(
  333. userGroup8?._id,
  334. userGroup8?.name,
  335. userGroup8?.description,
  336. groupId7,
  337. forceUpdateParents,
  338. );
  339. const userGroupRelation6AfterUpdate = await UserGroupRelation.findOne({
  340. relatedGroup: groupId6,
  341. relatedUser: userId1,
  342. });
  343. const userGroupRelation7AfterUpdate = await UserGroupRelation.findOne({
  344. relatedGroup: groupId7,
  345. relatedUser: userId1,
  346. });
  347. const userGroupRelation8AfterUpdate = await UserGroupRelation.findOne({
  348. relatedGroup: groupId8,
  349. relatedUser: userId1,
  350. });
  351. expect(userGroupRelation6AfterUpdate).not.toBeNull();
  352. // userGroup7 should have userId1
  353. expect(userGroupRelation7AfterUpdate).not.toBeNull();
  354. expect(userGroupRelation8AfterUpdate).not.toBeNull();
  355. });
  356. test('Should throw an error when trying to choose parent from descendant groups.', async () => {
  357. const userGroup9 = await UserGroup.findOne({
  358. _id: groupId9,
  359. parent: null,
  360. });
  361. const userGroup10 = await UserGroup.findOne({
  362. _id: groupId10,
  363. parent: groupId9,
  364. });
  365. const userGroupRelation9BeforeUpdate = await UserGroupRelation.findOne({
  366. relatedGroup: userGroup9?._id,
  367. relatedUser: userId1,
  368. });
  369. const userGroupRelation10BeforeUpdate = await UserGroupRelation.findOne({
  370. relatedGroup: userGroup10?._id,
  371. relatedUser: userId1,
  372. });
  373. expect(userGroupRelation9BeforeUpdate).not.toBeNull();
  374. expect(userGroupRelation10BeforeUpdate).not.toBeNull();
  375. const result = userGroupService.updateGroup(
  376. userGroup9?._id,
  377. userGroup9?.name,
  378. userGroup9?.description,
  379. userGroup10?._id,
  380. );
  381. await expect(result).rejects.toThrow(
  382. 'It is not allowed to choose parent from descendant groups.',
  383. );
  384. });
  385. });
  386. describe('removeUserByUsername', () => {
  387. test('User should be deleted from child groups when the user excluded from the parent group', async () => {
  388. const userGroup11 = await UserGroup.findOne({
  389. _id: groupId11,
  390. parent: null,
  391. });
  392. const userGroup12 = await UserGroup.findOne({
  393. _id: groupId12,
  394. parent: groupId11,
  395. });
  396. // Both groups have user1
  397. const userGroupRelation11BeforeRemove = await UserGroupRelation.findOne({
  398. relatedGroup: userGroup11?._id,
  399. relatedUser: userId1,
  400. });
  401. const userGroupRelation12BeforeRemove = await UserGroupRelation.findOne({
  402. relatedGroup: userGroup12?._id,
  403. relatedUser: userId1,
  404. });
  405. expect(userGroupRelation11BeforeRemove).not.toBeNull();
  406. expect(userGroupRelation12BeforeRemove).not.toBeNull();
  407. // remove user1 from the parent group
  408. await userGroupService.removeUserByUsername(
  409. userGroup11?._id,
  410. 'ug_test_user1',
  411. );
  412. // Both groups have not user1
  413. const userGroupRelation11AfterRemove = await UserGroupRelation.findOne({
  414. relatedGroup: userGroup11?._id,
  415. relatedUser: userId1,
  416. });
  417. const userGroupRelation12AfterRemove = await UserGroupRelation.findOne({
  418. relatedGroup: userGroup12?._id,
  419. relatedUser: userId1,
  420. });
  421. await expect(userGroupRelation11AfterRemove).toBeNull();
  422. await expect(userGroupRelation12AfterRemove).toBeNull();
  423. });
  424. });
  425. describe('removeCompletelyByRootGroupId', () => {
  426. describe('when action is public', () => {
  427. test('Should remove the group and its descendants and publicize pages that are only visible to the groups to be removed', async () => {
  428. const userGroup13 = await UserGroup.findOne({ _id: groupId13 });
  429. const userGroup14 = await UserGroup.findOne({ _id: groupId14 });
  430. expect(userGroup13).not.toBeNull();
  431. expect(userGroup14).not.toBeNull();
  432. const canBePublicized = await Page.findOne({ _id: pageId1 });
  433. const cannotBePublicized = await Page.findOne({ _id: pageId2 });
  434. expect(canBePublicized?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  435. expect(normalizeGrantedGroups(canBePublicized?.grantedGroups)).toEqual(
  436. expect.arrayContaining([
  437. { item: groupId13, type: GroupType.userGroup },
  438. { item: groupId14, type: GroupType.userGroup },
  439. ]),
  440. );
  441. expect(
  442. normalizeGrantedGroups(canBePublicized?.grantedGroups)?.length,
  443. ).toBe(2);
  444. expect(cannotBePublicized?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  445. expect(
  446. normalizeGrantedGroups(cannotBePublicized?.grantedGroups),
  447. ).toEqual(
  448. expect.arrayContaining([
  449. { item: groupId13, type: GroupType.userGroup },
  450. { item: groupId15, type: GroupType.userGroup },
  451. ]),
  452. );
  453. expect(
  454. normalizeGrantedGroups(cannotBePublicized?.grantedGroups)?.length,
  455. ).toBe(2);
  456. await userGroupService.removeCompletelyByRootGroupId(
  457. groupId13,
  458. PageActionOnGroupDelete.publicize,
  459. user1,
  460. );
  461. const userGroup13AfterDeleteProcess = await UserGroup.findOne({
  462. _id: groupId13,
  463. });
  464. const userGroup14AfterDeleteProcess = await UserGroup.findOne({
  465. _id: groupId14,
  466. });
  467. expect(userGroup13AfterDeleteProcess).toBeNull();
  468. expect(userGroup14AfterDeleteProcess).toBeNull();
  469. const canBePublicizedAfterDeleteProcess = await Page.findOne({
  470. _id: pageId1,
  471. });
  472. const cannotBePublicizedAfterDeleteProcess = await Page.findOne({
  473. _id: pageId2,
  474. });
  475. expect(canBePublicizedAfterDeleteProcess?.grant).toBe(
  476. PageGrant.GRANT_PUBLIC,
  477. );
  478. expect(
  479. normalizeGrantedGroups(
  480. canBePublicizedAfterDeleteProcess?.grantedGroups,
  481. ),
  482. ).toEqual([]);
  483. expect(cannotBePublicizedAfterDeleteProcess?.grant).toBe(
  484. PageGrant.GRANT_USER_GROUP,
  485. );
  486. expect(
  487. normalizeGrantedGroups(
  488. cannotBePublicizedAfterDeleteProcess?.grantedGroups,
  489. ),
  490. ).toEqual(
  491. expect.arrayContaining([
  492. { item: groupId15, type: GroupType.userGroup },
  493. ]),
  494. );
  495. expect(
  496. normalizeGrantedGroups(
  497. cannotBePublicizedAfterDeleteProcess?.grantedGroups,
  498. )?.length,
  499. ).toBe(1);
  500. });
  501. });
  502. });
  503. });