v5.page.integ.ts 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275
  1. import assert from 'node:assert';
  2. import {
  3. GroupType,
  4. getIdForRef,
  5. type IGrantedGroup,
  6. type IRevision,
  7. type IUser,
  8. PageGrant,
  9. } from '@growi/core/dist/interfaces';
  10. import mongoose, { type HydratedDocument, type Model } from 'mongoose';
  11. import { getInstance } from '^/test/setup/crowi';
  12. import type { CommentModel } from '~/features/comment/server';
  13. import { ExternalGroupProviderType } from '~/features/external-user-group/interfaces/external-user-group';
  14. import ExternalUserGroup from '~/features/external-user-group/server/models/external-user-group';
  15. import ExternalUserGroupRelation from '~/features/external-user-group/server/models/external-user-group-relation';
  16. import type { IBookmark } from '~/interfaces/bookmark-info';
  17. import type { IComment } from '~/interfaces/comment';
  18. import { PageActionType } from '~/interfaces/page-operation';
  19. import type { IShareLink } from '~/interfaces/share-link';
  20. import type Crowi from '~/server/crowi';
  21. import type { PageDocument, PageModel } from '~/server/models/page';
  22. import type {
  23. IPageOperation,
  24. PageOperationModel,
  25. } from '~/server/models/page-operation';
  26. import UserGroup from '~/server/models/user-group';
  27. import UserGroupRelation from '~/server/models/user-group-relation';
  28. import type { IPageService } from '../service/page';
  29. import type { BookmarkModel } from './bookmark';
  30. import type { IPageRedirect, PageRedirectModel } from './page-redirect';
  31. import type { ShareLinkModel } from './share-link';
  32. describe('Page', () => {
  33. let crowi: Crowi;
  34. let pageService: IPageService;
  35. let Page: PageModel;
  36. let Revision: Model<IRevision>;
  37. let Bookmark: BookmarkModel;
  38. let Comment: CommentModel;
  39. let User: Model<IUser>;
  40. let ShareLink: ShareLinkModel;
  41. let PageRedirect: PageRedirectModel;
  42. let PageOperation: PageOperationModel;
  43. let rootPage: PageDocument;
  44. let dummyUser1: HydratedDocument<IUser>;
  45. let pModelUser1: HydratedDocument<IUser>;
  46. let pModelUser2: HydratedDocument<IUser>;
  47. let pModelUser3: HydratedDocument<IUser>;
  48. let userGroupIdPModelIsolate: mongoose.Types.ObjectId;
  49. let userGroupIdPModelA: mongoose.Types.ObjectId;
  50. let userGroupIdPModelB: mongoose.Types.ObjectId;
  51. let userGroupIdPModelC: mongoose.Types.ObjectId;
  52. let externalUserGroupIdPModelIsolate: mongoose.Types.ObjectId;
  53. let externalUserGroupIdPModelA: mongoose.Types.ObjectId;
  54. let externalUserGroupIdPModelB: mongoose.Types.ObjectId;
  55. let externalUserGroupIdPModelC: mongoose.Types.ObjectId;
  56. // To test updatePage overwriting descendants (prefix `upod`)
  57. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  58. let upodUserA: any;
  59. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  60. let upodUserB: any;
  61. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  62. let upodUserC: any;
  63. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  64. let upodGroupAB: any;
  65. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  66. let upodGroupA: any;
  67. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  68. let upodGroupAIsolated: any;
  69. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  70. let upodGroupB: any;
  71. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  72. let upodGroupC: any;
  73. const upodUserGroupIdA = new mongoose.Types.ObjectId();
  74. const upodUserGroupIdAIsolated = new mongoose.Types.ObjectId();
  75. const upodUserGroupIdB = new mongoose.Types.ObjectId();
  76. const upodUserGroupIdC = new mongoose.Types.ObjectId();
  77. const upodUserGroupIdAB = new mongoose.Types.ObjectId();
  78. const upodExternalUserGroupIdA = new mongoose.Types.ObjectId();
  79. const upodExternalUserGroupIdAIsolated = new mongoose.Types.ObjectId();
  80. const upodExternalUserGroupIdB = new mongoose.Types.ObjectId();
  81. const upodExternalUserGroupIdC = new mongoose.Types.ObjectId();
  82. const upodExternalUserGroupIdAB = new mongoose.Types.ObjectId();
  83. const upodPageIdgAB1 = new mongoose.Types.ObjectId();
  84. const upodPageIdPublic2 = new mongoose.Types.ObjectId();
  85. const upodPageIdPublic3 = new mongoose.Types.ObjectId();
  86. const upodPageIdPublic4 = new mongoose.Types.ObjectId();
  87. const upodPageIdPublic5 = new mongoose.Types.ObjectId();
  88. const upodPageIdPublic6 = new mongoose.Types.ObjectId();
  89. // Since updatePageSubOperation is asynchronously called from updatePage,
  90. // we use a polling pattern to wait for the async operation to complete.
  91. // The PageOperation document is deleted when updatePageSubOperation finishes.
  92. const updatePage = async (
  93. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  94. page: any,
  95. newRevisionBody: string,
  96. oldRevisionBody: string,
  97. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  98. user: any,
  99. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  100. options: any = {},
  101. ) => {
  102. const fromPath = page.path;
  103. const savedPage = await pageService.updatePage(
  104. page,
  105. newRevisionBody,
  106. oldRevisionBody,
  107. user,
  108. options,
  109. );
  110. // Wait for the async updatePageSubOperation to complete by polling PageOperation
  111. const startTime = Date.now();
  112. const maxWaitMs = 5000;
  113. while (Date.now() - startTime < maxWaitMs) {
  114. const op = await PageOperation.findOne({
  115. fromPath,
  116. actionType: PageActionType.Update,
  117. });
  118. if (op == null) {
  119. break; // Operation completed
  120. }
  121. await new Promise((resolve) => setTimeout(resolve, 50));
  122. }
  123. return savedPage;
  124. };
  125. const createDocumentsToTestUpdatePageOverwritingDescendants = async () => {
  126. // Users
  127. await User.insertMany([
  128. {
  129. name: 'upodUserA',
  130. username: 'upodUserA',
  131. email: 'upoduserA@example.com',
  132. },
  133. {
  134. name: 'upodUserB',
  135. username: 'upodUserB',
  136. email: 'upoduserB@example.com',
  137. },
  138. {
  139. name: 'upodUserC',
  140. username: 'upodUserC',
  141. email: 'upodUserC@example.com',
  142. },
  143. ]);
  144. upodUserA = await User.findOne({ username: 'upodUserA' });
  145. upodUserB = await User.findOne({ username: 'upodUserB' });
  146. upodUserC = await User.findOne({ username: 'upodUserC' });
  147. await UserGroup.insertMany([
  148. {
  149. _id: upodUserGroupIdAB,
  150. name: 'upodGroupAB',
  151. parent: null,
  152. },
  153. {
  154. _id: upodUserGroupIdA,
  155. name: 'upodGroupA',
  156. parent: upodUserGroupIdAB,
  157. },
  158. {
  159. _id: upodUserGroupIdAIsolated,
  160. name: 'upodGroupAIsolated',
  161. parent: null,
  162. },
  163. {
  164. _id: upodUserGroupIdB,
  165. name: 'upodGroupB',
  166. parent: upodUserGroupIdAB,
  167. },
  168. {
  169. _id: upodUserGroupIdC,
  170. name: 'upodGroupC',
  171. parent: null,
  172. },
  173. ]);
  174. upodGroupAB = await UserGroup.findOne({ name: 'upodGroupAB' });
  175. upodGroupA = await UserGroup.findOne({ name: 'upodGroupA' });
  176. upodGroupAIsolated = await UserGroup.findOne({
  177. name: 'upodGroupAIsolated',
  178. });
  179. upodGroupB = await UserGroup.findOne({ name: 'upodGroupB' });
  180. upodGroupC = await UserGroup.findOne({ name: 'upodGroupC' });
  181. // UserGroupRelations
  182. await UserGroupRelation.insertMany([
  183. {
  184. relatedGroup: upodUserGroupIdAB,
  185. relatedUser: upodUserA._id,
  186. },
  187. {
  188. relatedGroup: upodUserGroupIdAB,
  189. relatedUser: upodUserB._id,
  190. },
  191. {
  192. relatedGroup: upodUserGroupIdA,
  193. relatedUser: upodUserA._id,
  194. },
  195. {
  196. relatedGroup: upodUserGroupIdAIsolated,
  197. relatedUser: upodUserA._id,
  198. },
  199. {
  200. relatedGroup: upodUserGroupIdB,
  201. relatedUser: upodUserB._id,
  202. },
  203. {
  204. relatedGroup: upodUserGroupIdC,
  205. relatedUser: upodUserC._id,
  206. },
  207. ]);
  208. // Insert ExternalUserGroups with the same group structure as UserGroups
  209. // Use to test
  210. // - ExternalUserGroup
  211. // - Case of multiple grantedGroups for Page
  212. await ExternalUserGroup.insertMany([
  213. {
  214. _id: upodExternalUserGroupIdAB,
  215. name: 'upodExternalGroupAB',
  216. parent: null,
  217. externalId: 'upodExternalGroupAB',
  218. provider: ExternalGroupProviderType.ldap,
  219. },
  220. {
  221. _id: upodExternalUserGroupIdA,
  222. name: 'upodExternalGroupA',
  223. parent: upodExternalUserGroupIdAB,
  224. externalId: 'upodExternalGroupA',
  225. provider: ExternalGroupProviderType.ldap,
  226. },
  227. {
  228. _id: upodExternalUserGroupIdAIsolated,
  229. name: 'upodExternalGroupAIsolated',
  230. parent: null,
  231. externalId: 'upodExternalGroupAIsolated',
  232. provider: ExternalGroupProviderType.ldap,
  233. },
  234. {
  235. _id: upodExternalUserGroupIdB,
  236. name: 'upodExternalGroupB',
  237. parent: upodExternalUserGroupIdAB,
  238. externalId: 'upodExternalGroupB',
  239. provider: ExternalGroupProviderType.ldap,
  240. },
  241. {
  242. _id: upodExternalUserGroupIdC,
  243. name: 'upodExternalGroupC',
  244. parent: null,
  245. externalId: 'upodExternalGroupC',
  246. provider: ExternalGroupProviderType.ldap,
  247. },
  248. ]);
  249. // ExternalUserGroupRelations
  250. await ExternalUserGroupRelation.insertMany([
  251. {
  252. relatedGroup: upodExternalUserGroupIdAB,
  253. relatedUser: upodUserA._id,
  254. },
  255. {
  256. relatedGroup: upodExternalUserGroupIdAB,
  257. relatedUser: upodUserB._id,
  258. },
  259. {
  260. relatedGroup: upodExternalUserGroupIdA,
  261. relatedUser: upodUserA._id,
  262. },
  263. {
  264. relatedGroup: upodExternalUserGroupIdAIsolated,
  265. relatedUser: upodUserA._id,
  266. },
  267. {
  268. relatedGroup: upodExternalUserGroupIdB,
  269. relatedUser: upodUserB._id,
  270. },
  271. {
  272. relatedGroup: upodExternalUserGroupIdC,
  273. relatedUser: upodUserC._id,
  274. },
  275. ]);
  276. // Pages
  277. await Page.insertMany([
  278. // case 1
  279. {
  280. _id: upodPageIdgAB1,
  281. path: '/gAB_upod_1', // to GRANT_PUBLIC
  282. grant: PageGrant.GRANT_USER_GROUP,
  283. creator: upodUserA,
  284. lastUpdateUser: upodUserA,
  285. grantedUsers: null,
  286. grantedGroups: [
  287. { item: upodUserGroupIdAB, type: GroupType.userGroup },
  288. {
  289. item: upodExternalUserGroupIdAB,
  290. type: GroupType.externalUserGroup,
  291. },
  292. ],
  293. parent: rootPage._id,
  294. },
  295. {
  296. path: '/gAB_upod_1/gB_upod_1',
  297. grant: PageGrant.GRANT_USER_GROUP,
  298. creator: upodUserB,
  299. lastUpdateUser: upodUserB,
  300. grantedUsers: null,
  301. grantedGroups: [
  302. { item: upodUserGroupIdB, type: GroupType.userGroup },
  303. { item: upodExternalUserGroupIdB, type: GroupType.externalUserGroup },
  304. ],
  305. parent: upodPageIdgAB1,
  306. },
  307. {
  308. path: '/gAB_upod_1/onlyB_upod_1',
  309. grant: PageGrant.GRANT_OWNER,
  310. creator: upodUserB,
  311. lastUpdateUser: upodUserB,
  312. grantedUsers: [upodUserB._id],
  313. grantedGroups: [],
  314. parent: upodPageIdgAB1,
  315. },
  316. // grant user A and B with independent groups
  317. {
  318. path: '/gAB_upod_1/gA_gB_upod_1',
  319. grant: PageGrant.GRANT_USER_GROUP,
  320. creator: upodUserA,
  321. lastUpdateUser: upodUserA,
  322. grantedUsers: null,
  323. grantedGroups: [
  324. { item: upodUserGroupIdA, type: GroupType.userGroup },
  325. { item: upodExternalUserGroupIdA, type: GroupType.externalUserGroup },
  326. { item: upodUserGroupIdB, type: GroupType.userGroup },
  327. { item: upodExternalUserGroupIdB, type: GroupType.externalUserGroup },
  328. ],
  329. parent: upodPageIdgAB1,
  330. },
  331. // case 2
  332. {
  333. _id: upodPageIdPublic2,
  334. path: '/public_upod_2', // to Anything
  335. grant: PageGrant.GRANT_PUBLIC,
  336. creator: upodUserA,
  337. lastUpdateUser: upodUserA,
  338. grantedUsers: null,
  339. grantedGroups: [],
  340. parent: rootPage._id,
  341. },
  342. {
  343. path: '/public_upod_2/gA_upod_2',
  344. grant: PageGrant.GRANT_USER_GROUP,
  345. creator: upodUserA,
  346. lastUpdateUser: upodUserA,
  347. grantedUsers: null,
  348. grantedGroups: [
  349. { item: upodUserGroupIdA, type: GroupType.userGroup },
  350. { item: upodExternalUserGroupIdA, type: GroupType.externalUserGroup },
  351. ],
  352. parent: upodPageIdPublic2,
  353. },
  354. {
  355. path: '/public_upod_2/gAIsolated_upod_2',
  356. grant: PageGrant.GRANT_USER_GROUP,
  357. creator: upodUserA,
  358. lastUpdateUser: upodUserA,
  359. grantedUsers: null,
  360. grantedGroups: [
  361. { item: upodUserGroupIdAIsolated, type: GroupType.userGroup },
  362. {
  363. item: upodExternalUserGroupIdAIsolated,
  364. type: GroupType.externalUserGroup,
  365. },
  366. ],
  367. parent: upodPageIdPublic2,
  368. },
  369. {
  370. path: '/public_upod_2/onlyA_upod_2',
  371. grant: PageGrant.GRANT_OWNER,
  372. creator: upodUserA,
  373. lastUpdateUser: upodUserA,
  374. grantedUsers: [upodUserA._id],
  375. grantedGroups: [],
  376. parent: upodPageIdPublic2,
  377. },
  378. // case 3
  379. {
  380. _id: upodPageIdPublic3,
  381. path: '/public_upod_3', // to GRANT_USER_GROUP with upodGroupAB
  382. grant: PageGrant.GRANT_PUBLIC,
  383. creator: upodUserA,
  384. lastUpdateUser: upodUserA,
  385. grantedUsers: null,
  386. grantedGroups: [],
  387. parent: rootPage._id,
  388. },
  389. // grant user A and B with a single group
  390. // (external group is extra for testing external groups)
  391. {
  392. path: '/public_upod_3/gAB_upod_3',
  393. grant: PageGrant.GRANT_USER_GROUP,
  394. creator: upodUserA,
  395. lastUpdateUser: upodUserA,
  396. grantedUsers: null,
  397. grantedGroups: [
  398. { item: upodUserGroupIdAB, type: GroupType.userGroup },
  399. {
  400. item: upodExternalUserGroupIdAB,
  401. type: GroupType.externalUserGroup,
  402. },
  403. ],
  404. parent: upodPageIdPublic3,
  405. },
  406. // grant user A and B with independent groups
  407. {
  408. path: '/public_upod_3/gA_gB_upod_3',
  409. grant: PageGrant.GRANT_USER_GROUP,
  410. creator: upodUserA,
  411. lastUpdateUser: upodUserA,
  412. grantedUsers: null,
  413. grantedGroups: [
  414. { item: upodUserGroupIdA, type: GroupType.userGroup },
  415. { item: upodExternalUserGroupIdA, type: GroupType.externalUserGroup },
  416. { item: upodUserGroupIdB, type: GroupType.userGroup },
  417. { item: upodExternalUserGroupIdB, type: GroupType.externalUserGroup },
  418. ],
  419. parent: upodPageIdPublic3,
  420. },
  421. {
  422. path: '/public_upod_3/gB_upod_3',
  423. grant: PageGrant.GRANT_USER_GROUP,
  424. creator: upodUserB,
  425. lastUpdateUser: upodUserB,
  426. grantedUsers: null,
  427. grantedGroups: [
  428. { item: upodUserGroupIdB, type: GroupType.userGroup },
  429. { item: upodExternalUserGroupIdB, type: GroupType.externalUserGroup },
  430. ],
  431. parent: upodPageIdPublic3,
  432. },
  433. {
  434. path: '/public_upod_3/onlyB_upod_3',
  435. grant: PageGrant.GRANT_OWNER,
  436. creator: upodUserB,
  437. lastUpdateUser: upodUserB,
  438. grantedUsers: [upodUserB._id],
  439. grantedGroups: [],
  440. parent: upodPageIdPublic3,
  441. },
  442. // case 4
  443. {
  444. _id: upodPageIdPublic4,
  445. path: '/public_upod_4', // to GRANT_USER_GROUP with upodGroupAB
  446. grant: PageGrant.GRANT_PUBLIC,
  447. creator: upodUserA,
  448. lastUpdateUser: upodUserA,
  449. grantedUsers: null,
  450. grantedGroups: [],
  451. parent: rootPage._id,
  452. },
  453. {
  454. path: '/public_upod_4/gA_upod_4',
  455. grant: PageGrant.GRANT_USER_GROUP,
  456. creator: upodUserA,
  457. lastUpdateUser: upodUserA,
  458. grantedUsers: null,
  459. grantedGroups: [
  460. { item: upodUserGroupIdA, type: GroupType.userGroup },
  461. { item: upodExternalUserGroupIdA, type: GroupType.externalUserGroup },
  462. ],
  463. parent: upodPageIdPublic4,
  464. },
  465. {
  466. path: '/public_upod_4/gC_upod_4',
  467. grant: PageGrant.GRANT_USER_GROUP,
  468. creator: upodUserC,
  469. lastUpdateUser: upodUserC,
  470. grantedUsers: null,
  471. grantedGroups: [
  472. { item: upodUserGroupIdC, type: GroupType.userGroup },
  473. { item: upodExternalUserGroupIdC, type: GroupType.externalUserGroup },
  474. ],
  475. parent: upodPageIdPublic4,
  476. },
  477. // case 5
  478. {
  479. _id: upodPageIdPublic5,
  480. path: '/public_upod_5', // to GRANT_USER_GROUP with upodGroupAB
  481. grant: PageGrant.GRANT_PUBLIC,
  482. creator: upodUserA,
  483. lastUpdateUser: upodUserA,
  484. grantedUsers: null,
  485. grantedGroups: [],
  486. parent: rootPage._id,
  487. },
  488. {
  489. path: '/public_upod_5/gA_upod_5',
  490. grant: PageGrant.GRANT_USER_GROUP,
  491. creator: upodUserA,
  492. lastUpdateUser: upodUserA,
  493. grantedUsers: null,
  494. grantedGroups: [
  495. { item: upodUserGroupIdA, type: GroupType.userGroup },
  496. { item: upodExternalUserGroupIdA, type: GroupType.externalUserGroup },
  497. ],
  498. parent: upodPageIdPublic5,
  499. },
  500. {
  501. path: '/public_upod_5/onlyC_upod_5',
  502. grant: PageGrant.GRANT_OWNER,
  503. creator: upodUserC,
  504. lastUpdateUser: upodUserC,
  505. grantedUsers: [upodUserC._id],
  506. grantedGroups: [],
  507. parent: upodPageIdPublic5,
  508. },
  509. // case 6
  510. {
  511. _id: upodPageIdPublic6,
  512. path: '/public_upod_6', // to GRANT_USER_GROUP with upodGroupAB
  513. grant: PageGrant.GRANT_PUBLIC,
  514. creator: upodUserA,
  515. lastUpdateUser: upodUserA,
  516. grantedUsers: null,
  517. grantedGroups: [],
  518. parent: rootPage._id,
  519. },
  520. {
  521. path: '/public_upod_6/onlyC_upod_6',
  522. grant: PageGrant.GRANT_OWNER,
  523. creator: upodUserC,
  524. lastUpdateUser: upodUserC,
  525. grantedUsers: [upodUserC._id],
  526. grantedGroups: [],
  527. parent: upodPageIdPublic6,
  528. },
  529. ]);
  530. };
  531. // normalize for result comparison
  532. const normalizeGrantedGroups = (
  533. grantedGroups: IGrantedGroup[] | undefined,
  534. ) => {
  535. return grantedGroups?.map((group) => {
  536. return { item: getIdForRef(group.item), type: group.type };
  537. });
  538. };
  539. beforeAll(async () => {
  540. crowi = await getInstance();
  541. pageService = crowi.pageService;
  542. await crowi.configManager.updateConfig('app:isV5Compatible', true);
  543. vi.restoreAllMocks();
  544. User = mongoose.model('User');
  545. Page = mongoose.model('Page') as PageModel;
  546. Revision = mongoose.model('Revision');
  547. Bookmark = mongoose.model<IBookmark, BookmarkModel>('Bookmark');
  548. Comment = mongoose.model<IComment, CommentModel>('Comment');
  549. ShareLink = mongoose.model<IShareLink, ShareLinkModel>('ShareLink');
  550. PageRedirect = mongoose.model<IPageRedirect, PageRedirectModel>(
  551. 'PageRedirect',
  552. );
  553. PageOperation = mongoose.model<IPageOperation, PageOperationModel>(
  554. 'PageOperation',
  555. );
  556. // Create dummy user if it doesn't exist
  557. const existingUser1 = await User.findOne({ username: 'v5DummyUser1' });
  558. if (existingUser1 == null) {
  559. await User.insertMany([
  560. {
  561. name: 'v5DummyUser1',
  562. username: 'v5DummyUser1',
  563. email: 'v5dummyuser1@example.com',
  564. },
  565. ]);
  566. }
  567. const foundDummyUser1 = await User.findOne({ username: 'v5DummyUser1' });
  568. assert(foundDummyUser1 != null);
  569. dummyUser1 = foundDummyUser1;
  570. // Ensure root page exists
  571. const existingRootPage = await Page.findOne({ path: '/' });
  572. if (existingRootPage == null) {
  573. const rootPageId = new mongoose.Types.ObjectId();
  574. rootPage = await Page.create({
  575. _id: rootPageId,
  576. path: '/',
  577. grant: PageGrant.GRANT_PUBLIC,
  578. });
  579. } else {
  580. rootPage = existingRootPage;
  581. }
  582. const pModelUserId1 = new mongoose.Types.ObjectId();
  583. const pModelUserId2 = new mongoose.Types.ObjectId();
  584. const pModelUserId3 = new mongoose.Types.ObjectId();
  585. await User.insertMany([
  586. {
  587. _id: pModelUserId1,
  588. name: 'pmodelUser1',
  589. username: 'pmodelUser1',
  590. email: 'pmodelUser1@example.com',
  591. },
  592. {
  593. _id: pModelUserId2,
  594. name: 'pmodelUser2',
  595. username: 'pmodelUser2',
  596. email: 'pmodelUser2@example.com',
  597. },
  598. {
  599. _id: pModelUserId3,
  600. name: 'pModelUser3',
  601. username: 'pModelUser3',
  602. email: 'pModelUser3@example.com',
  603. },
  604. ]);
  605. const foundPModelUser1 = await User.findOne({ _id: pModelUserId1 });
  606. const foundPModelUser2 = await User.findOne({ _id: pModelUserId2 });
  607. const foundPModelUser3 = await User.findOne({ _id: pModelUserId3 });
  608. assert(foundPModelUser1 != null);
  609. assert(foundPModelUser2 != null);
  610. assert(foundPModelUser3 != null);
  611. pModelUser1 = foundPModelUser1;
  612. pModelUser2 = foundPModelUser2;
  613. pModelUser3 = foundPModelUser3;
  614. userGroupIdPModelIsolate = new mongoose.Types.ObjectId();
  615. userGroupIdPModelA = new mongoose.Types.ObjectId();
  616. userGroupIdPModelB = new mongoose.Types.ObjectId();
  617. userGroupIdPModelC = new mongoose.Types.ObjectId();
  618. await UserGroup.insertMany([
  619. {
  620. _id: userGroupIdPModelIsolate,
  621. name: 'pModel_groupIsolate',
  622. },
  623. {
  624. _id: userGroupIdPModelA,
  625. name: 'pModel_groupA',
  626. },
  627. {
  628. _id: userGroupIdPModelB,
  629. name: 'pModel_groupB',
  630. parent: userGroupIdPModelA,
  631. },
  632. {
  633. _id: userGroupIdPModelC,
  634. name: 'pModel_groupC',
  635. parent: userGroupIdPModelB,
  636. },
  637. ]);
  638. await UserGroupRelation.insertMany([
  639. {
  640. relatedGroup: userGroupIdPModelIsolate,
  641. relatedUser: pModelUserId1,
  642. createdAt: new Date(),
  643. },
  644. {
  645. relatedGroup: userGroupIdPModelIsolate,
  646. relatedUser: pModelUserId2,
  647. createdAt: new Date(),
  648. },
  649. {
  650. relatedGroup: userGroupIdPModelA,
  651. relatedUser: pModelUserId1,
  652. createdAt: new Date(),
  653. },
  654. {
  655. relatedGroup: userGroupIdPModelA,
  656. relatedUser: pModelUserId2,
  657. createdAt: new Date(),
  658. },
  659. {
  660. relatedGroup: userGroupIdPModelA,
  661. relatedUser: pModelUserId3,
  662. createdAt: new Date(),
  663. },
  664. {
  665. relatedGroup: userGroupIdPModelB,
  666. relatedUser: pModelUserId2,
  667. createdAt: new Date(),
  668. },
  669. {
  670. relatedGroup: userGroupIdPModelB,
  671. relatedUser: pModelUserId3,
  672. createdAt: new Date(),
  673. },
  674. {
  675. relatedGroup: userGroupIdPModelC,
  676. relatedUser: pModelUserId3,
  677. createdAt: new Date(),
  678. },
  679. ]);
  680. // Insert ExternalUserGroups with the same group structure as UserGroups
  681. // Use to test
  682. // - ExternalUserGroup
  683. // - Case of multiple grantedGroups for Page
  684. externalUserGroupIdPModelIsolate = new mongoose.Types.ObjectId();
  685. externalUserGroupIdPModelA = new mongoose.Types.ObjectId();
  686. externalUserGroupIdPModelB = new mongoose.Types.ObjectId();
  687. externalUserGroupIdPModelC = new mongoose.Types.ObjectId();
  688. await ExternalUserGroup.insertMany([
  689. {
  690. _id: externalUserGroupIdPModelIsolate,
  691. name: 'pModel_externalGroupIsolate',
  692. externalId: 'pModel_externalGroupIsolate',
  693. provider: ExternalGroupProviderType.ldap,
  694. },
  695. {
  696. _id: externalUserGroupIdPModelA,
  697. name: 'pModel_externalGroupA',
  698. externalId: 'pModel_externalGroupA',
  699. provider: ExternalGroupProviderType.ldap,
  700. },
  701. {
  702. _id: externalUserGroupIdPModelB,
  703. name: 'pModel_externalGroupB',
  704. parent: externalUserGroupIdPModelA,
  705. externalId: 'pModel_externalGroupB',
  706. provider: ExternalGroupProviderType.ldap,
  707. },
  708. {
  709. _id: externalUserGroupIdPModelC,
  710. name: 'pModel_externalGroupC',
  711. parent: externalUserGroupIdPModelB,
  712. externalId: 'pModel_externalGroupC',
  713. provider: ExternalGroupProviderType.ldap,
  714. },
  715. ]);
  716. await ExternalUserGroupRelation.insertMany([
  717. {
  718. relatedGroup: externalUserGroupIdPModelIsolate,
  719. relatedUser: pModelUserId1,
  720. createdAt: new Date(),
  721. },
  722. {
  723. relatedGroup: externalUserGroupIdPModelIsolate,
  724. relatedUser: pModelUserId2,
  725. createdAt: new Date(),
  726. },
  727. {
  728. relatedGroup: externalUserGroupIdPModelA,
  729. relatedUser: pModelUserId1,
  730. createdAt: new Date(),
  731. },
  732. {
  733. relatedGroup: externalUserGroupIdPModelA,
  734. relatedUser: pModelUserId2,
  735. createdAt: new Date(),
  736. },
  737. {
  738. relatedGroup: externalUserGroupIdPModelA,
  739. relatedUser: pModelUserId3,
  740. createdAt: new Date(),
  741. },
  742. {
  743. relatedGroup: externalUserGroupIdPModelB,
  744. relatedUser: pModelUserId2,
  745. createdAt: new Date(),
  746. },
  747. {
  748. relatedGroup: externalUserGroupIdPModelB,
  749. relatedUser: pModelUserId3,
  750. createdAt: new Date(),
  751. },
  752. {
  753. relatedGroup: externalUserGroupIdPModelC,
  754. relatedUser: pModelUserId3,
  755. createdAt: new Date(),
  756. },
  757. ]);
  758. /**
  759. * update
  760. * mup_ => model update
  761. * emp => empty => page with isEmpty: true
  762. * pub => public => GRANT_PUBLIC
  763. * awl => Anyone with the link => GRANT_RESTRICTED
  764. */
  765. const pageIdUpd1 = new mongoose.Types.ObjectId();
  766. const pageIdUpd2 = new mongoose.Types.ObjectId();
  767. const pageIdUpd3 = new mongoose.Types.ObjectId();
  768. const pageIdUpd4 = new mongoose.Types.ObjectId();
  769. const pageIdUpd5 = new mongoose.Types.ObjectId();
  770. const pageIdUpd6 = new mongoose.Types.ObjectId();
  771. const pageIdUpd7 = new mongoose.Types.ObjectId();
  772. const pageIdUpd8 = new mongoose.Types.ObjectId();
  773. const pageIdUpd9 = new mongoose.Types.ObjectId();
  774. const pageIdUpd10 = new mongoose.Types.ObjectId();
  775. const pageIdUpd11 = new mongoose.Types.ObjectId();
  776. const pageIdUpd12 = new mongoose.Types.ObjectId();
  777. const pageIdUpd13 = new mongoose.Types.ObjectId();
  778. const pageIdUpd14 = new mongoose.Types.ObjectId();
  779. const pageIdUpd15 = new mongoose.Types.ObjectId();
  780. const pageIdUpd16 = new mongoose.Types.ObjectId();
  781. const pageIdUpd17 = new mongoose.Types.ObjectId();
  782. const pageIdUpd18 = new mongoose.Types.ObjectId();
  783. const pageIdUpd19 = new mongoose.Types.ObjectId();
  784. await Page.insertMany([
  785. {
  786. _id: pageIdUpd1,
  787. path: '/mup13_top/mup1_emp',
  788. grant: Page.GRANT_PUBLIC,
  789. parent: pageIdUpd8._id,
  790. isEmpty: true,
  791. },
  792. {
  793. _id: pageIdUpd2,
  794. path: '/mup13_top/mup1_emp/mup2_pub',
  795. grant: Page.GRANT_PUBLIC,
  796. parent: pageIdUpd1._id,
  797. creator: dummyUser1,
  798. lastUpdateUser: dummyUser1._id,
  799. isEmpty: false,
  800. },
  801. {
  802. _id: pageIdUpd3,
  803. path: '/mup14_top/mup6_pub',
  804. grant: Page.GRANT_PUBLIC,
  805. creator: dummyUser1,
  806. lastUpdateUser: dummyUser1._id,
  807. parent: pageIdUpd9,
  808. isEmpty: false,
  809. descendantCount: 1,
  810. },
  811. {
  812. path: '/mup14_top/mup6_pub/mup7_pub',
  813. grant: Page.GRANT_PUBLIC,
  814. creator: dummyUser1,
  815. lastUpdateUser: dummyUser1._id,
  816. parent: pageIdUpd3,
  817. isEmpty: false,
  818. descendantCount: 0,
  819. },
  820. {
  821. _id: pageIdUpd4,
  822. path: '/mup15_top/mup8_pub',
  823. grant: Page.GRANT_PUBLIC,
  824. creator: dummyUser1,
  825. lastUpdateUser: dummyUser1._id,
  826. parent: pageIdUpd10._id,
  827. isEmpty: false,
  828. },
  829. {
  830. _id: pageIdUpd5,
  831. path: '/mup16_top/mup9_pub/mup10_pub/mup11_awl',
  832. grant: Page.GRANT_RESTRICTED,
  833. creator: dummyUser1,
  834. lastUpdateUser: dummyUser1._id,
  835. isEmpty: false,
  836. },
  837. {
  838. _id: pageIdUpd6,
  839. path: '/mup17_top/mup12_emp',
  840. isEmpty: true,
  841. parent: pageIdUpd12._id,
  842. descendantCount: 1,
  843. },
  844. {
  845. _id: pageIdUpd7,
  846. path: '/mup17_top/mup12_emp',
  847. grant: Page.GRANT_RESTRICTED,
  848. creator: dummyUser1,
  849. lastUpdateUser: dummyUser1._id,
  850. isEmpty: false,
  851. },
  852. {
  853. path: '/mup17_top/mup12_emp/mup18_pub',
  854. isEmpty: false,
  855. creator: dummyUser1,
  856. lastUpdateUser: dummyUser1._id,
  857. parent: pageIdUpd6._id,
  858. },
  859. {
  860. _id: pageIdUpd8,
  861. path: '/mup13_top',
  862. grant: Page.GRANT_PUBLIC,
  863. creator: dummyUser1,
  864. lastUpdateUser: dummyUser1._id,
  865. isEmpty: false,
  866. parent: rootPage._id,
  867. descendantCount: 2,
  868. },
  869. {
  870. _id: pageIdUpd9,
  871. path: '/mup14_top',
  872. grant: Page.GRANT_PUBLIC,
  873. creator: dummyUser1,
  874. lastUpdateUser: dummyUser1._id,
  875. isEmpty: false,
  876. parent: rootPage._id,
  877. descendantCount: 2,
  878. },
  879. {
  880. _id: pageIdUpd10,
  881. path: '/mup15_top',
  882. grant: Page.GRANT_PUBLIC,
  883. creator: dummyUser1,
  884. lastUpdateUser: dummyUser1._id,
  885. isEmpty: false,
  886. parent: rootPage._id,
  887. descendantCount: 1,
  888. },
  889. {
  890. _id: pageIdUpd11,
  891. path: '/mup16_top',
  892. grant: Page.GRANT_PUBLIC,
  893. creator: dummyUser1,
  894. lastUpdateUser: dummyUser1._id,
  895. isEmpty: false,
  896. parent: rootPage._id,
  897. descendantCount: 0,
  898. },
  899. {
  900. _id: pageIdUpd12,
  901. path: '/mup17_top',
  902. grant: Page.GRANT_PUBLIC,
  903. creator: dummyUser1,
  904. lastUpdateUser: dummyUser1._id,
  905. isEmpty: false,
  906. parent: rootPage._id,
  907. descendantCount: 1,
  908. },
  909. {
  910. path: '/mup19',
  911. grant: Page.GRANT_PUBLIC,
  912. creator: dummyUser1,
  913. lastUpdateUser: dummyUser1._id,
  914. isEmpty: false,
  915. parent: rootPage._id,
  916. descendantCount: 0,
  917. },
  918. {
  919. path: '/mup20',
  920. grant: Page.GRANT_USER_GROUP,
  921. grantedGroups: [
  922. { item: userGroupIdPModelA, type: GroupType.userGroup },
  923. {
  924. item: externalUserGroupIdPModelA,
  925. type: GroupType.externalUserGroup,
  926. },
  927. ],
  928. creator: pModelUserId1,
  929. lastUpdateUser: pModelUserId1,
  930. isEmpty: false,
  931. parent: rootPage._id,
  932. descendantCount: 0,
  933. },
  934. {
  935. path: '/mup21',
  936. grant: Page.GRANT_RESTRICTED,
  937. creator: dummyUser1,
  938. lastUpdateUser: dummyUser1._id,
  939. isEmpty: false,
  940. descendantCount: 0,
  941. },
  942. {
  943. _id: pageIdUpd13,
  944. path: '/mup22',
  945. grant: Page.GRANT_PUBLIC,
  946. creator: pModelUser1,
  947. lastUpdateUser: pModelUser1._id,
  948. isEmpty: false,
  949. parent: rootPage._id,
  950. descendantCount: 1,
  951. },
  952. {
  953. path: '/mup22/mup23',
  954. grant: Page.GRANT_USER_GROUP,
  955. grantedGroups: [
  956. { item: userGroupIdPModelA, type: GroupType.userGroup },
  957. {
  958. item: externalUserGroupIdPModelA,
  959. type: GroupType.externalUserGroup,
  960. },
  961. ],
  962. creator: pModelUserId1,
  963. lastUpdateUser: pModelUserId1,
  964. isEmpty: false,
  965. parent: pageIdUpd13,
  966. descendantCount: 0,
  967. },
  968. {
  969. _id: pageIdUpd14,
  970. path: '/mup24_pub',
  971. grant: Page.GRANT_PUBLIC,
  972. creator: pModelUserId1,
  973. lastUpdateUser: pModelUserId1,
  974. isEmpty: false,
  975. parent: rootPage,
  976. descendantCount: 1,
  977. },
  978. {
  979. path: '/mup24_pub/mup25_pub',
  980. grant: Page.GRANT_PUBLIC,
  981. creator: pModelUserId1,
  982. lastUpdateUser: pModelUserId1,
  983. isEmpty: false,
  984. parent: pageIdUpd14,
  985. descendantCount: 0,
  986. },
  987. {
  988. path: '/mup26_awl',
  989. grant: Page.GRANT_RESTRICTED,
  990. creator: pModelUserId1,
  991. lastUpdateUser: pModelUserId1,
  992. isEmpty: false,
  993. descendantCount: 0,
  994. },
  995. {
  996. _id: pageIdUpd15,
  997. path: '/mup27_pub',
  998. grant: Page.GRANT_PUBLIC,
  999. creator: pModelUserId1,
  1000. lastUpdateUser: pModelUserId1,
  1001. isEmpty: false,
  1002. parent: rootPage,
  1003. descendantCount: 1,
  1004. },
  1005. {
  1006. path: '/mup27_pub/mup28_owner',
  1007. grant: Page.GRANT_OWNER,
  1008. creator: pModelUserId1,
  1009. lastUpdateUser: pModelUserId1,
  1010. isEmpty: false,
  1011. parent: pageIdUpd15,
  1012. grantedUsers: [pModelUserId1],
  1013. descendantCount: 0,
  1014. },
  1015. {
  1016. _id: pageIdUpd16,
  1017. path: '/mup29_A',
  1018. grant: Page.GRANT_USER_GROUP,
  1019. grantedGroups: [
  1020. { item: userGroupIdPModelA, type: GroupType.userGroup },
  1021. {
  1022. item: externalUserGroupIdPModelA,
  1023. type: GroupType.externalUserGroup,
  1024. },
  1025. ],
  1026. creator: pModelUserId1,
  1027. lastUpdateUser: pModelUserId1,
  1028. isEmpty: false,
  1029. parent: rootPage,
  1030. descendantCount: 1,
  1031. },
  1032. {
  1033. path: '/mup29_A/mup30_owner',
  1034. grant: Page.GRANT_OWNER,
  1035. grantedUsers: [pModelUserId1],
  1036. creator: pModelUserId1,
  1037. lastUpdateUser: pModelUserId1,
  1038. isEmpty: false,
  1039. parent: pageIdUpd16,
  1040. descendantCount: 0,
  1041. },
  1042. {
  1043. _id: pageIdUpd17,
  1044. path: '/mup31_A',
  1045. grant: Page.GRANT_USER_GROUP,
  1046. grantedGroups: [
  1047. { item: userGroupIdPModelA, type: GroupType.userGroup },
  1048. {
  1049. item: externalUserGroupIdPModelA,
  1050. type: GroupType.externalUserGroup,
  1051. },
  1052. ],
  1053. creator: pModelUserId1,
  1054. lastUpdateUser: pModelUserId1,
  1055. isEmpty: false,
  1056. parent: rootPage,
  1057. descendantCount: 1,
  1058. },
  1059. {
  1060. path: '/mup31_A/mup32_owner',
  1061. grant: Page.GRANT_OWNER,
  1062. grantedUsers: [pModelUserId1],
  1063. creator: pModelUserId1,
  1064. lastUpdateUser: pModelUserId1,
  1065. isEmpty: false,
  1066. parent: pageIdUpd17,
  1067. descendantCount: 0,
  1068. },
  1069. {
  1070. _id: pageIdUpd18,
  1071. path: '/mup33_C',
  1072. grant: Page.GRANT_USER_GROUP,
  1073. grantedGroups: [
  1074. { item: userGroupIdPModelC, type: GroupType.userGroup },
  1075. {
  1076. item: externalUserGroupIdPModelC,
  1077. type: GroupType.externalUserGroup,
  1078. },
  1079. ],
  1080. creator: pModelUserId3,
  1081. lastUpdateUser: pModelUserId3,
  1082. isEmpty: false,
  1083. parent: rootPage,
  1084. descendantCount: 1,
  1085. },
  1086. {
  1087. path: '/mup33_C/mup34_owner',
  1088. grant: Page.GRANT_OWNER,
  1089. grantedUsers: [pModelUserId3],
  1090. creator: pModelUserId3,
  1091. lastUpdateUser: pModelUserId3,
  1092. isEmpty: false,
  1093. parent: pageIdUpd18,
  1094. descendantCount: 0,
  1095. },
  1096. {
  1097. _id: pageIdUpd19,
  1098. path: '/mup35_owner',
  1099. grant: Page.GRANT_OWNER,
  1100. grantedUsers: [pModelUserId1],
  1101. creator: pModelUserId1,
  1102. lastUpdateUser: pModelUserId1,
  1103. isEmpty: false,
  1104. parent: rootPage,
  1105. descendantCount: 1,
  1106. },
  1107. {
  1108. path: '/mup35_owner/mup36_owner',
  1109. grant: Page.GRANT_OWNER,
  1110. grantedUsers: [pModelUserId1],
  1111. creator: pModelUserId1,
  1112. lastUpdateUser: pModelUserId1,
  1113. isEmpty: false,
  1114. parent: pageIdUpd19,
  1115. descendantCount: 0,
  1116. },
  1117. {
  1118. path: '/mup40', // used this number to resolve conflict
  1119. grant: Page.GRANT_OWNER,
  1120. grantedUsers: [dummyUser1._id],
  1121. creator: dummyUser1,
  1122. lastUpdateUser: dummyUser1._id,
  1123. isEmpty: false,
  1124. parent: rootPage._id,
  1125. descendantCount: 0,
  1126. },
  1127. {
  1128. path: '/with_multiple_individual_granted_groups',
  1129. grant: Page.GRANT_USER_GROUP,
  1130. grantedGroups: [
  1131. { item: userGroupIdPModelA, type: GroupType.userGroup },
  1132. { item: userGroupIdPModelB, type: GroupType.userGroup },
  1133. ],
  1134. creator: pModelUserId1,
  1135. lastUpdateUser: pModelUserId1,
  1136. isEmpty: false,
  1137. parent: rootPage,
  1138. },
  1139. ]);
  1140. await createDocumentsToTestUpdatePageOverwritingDescendants();
  1141. });
  1142. describe('updatePage with overwriteScopesOfDescendants false', () => {
  1143. describe('Changing grant from PUBLIC to RESTRICTED of', () => {
  1144. it('an only-child page will delete its empty parent page', async () => {
  1145. const pathT = '/mup13_top';
  1146. const path1 = '/mup13_top/mup1_emp';
  1147. const path2 = '/mup13_top/mup1_emp/mup2_pub';
  1148. const pageT = await Page.findOne({ path: pathT, descendantCount: 2 });
  1149. const page1 = await Page.findOne({ path: path1, isEmpty: true });
  1150. const page2 = await Page.findOne({
  1151. path: path2,
  1152. grant: Page.GRANT_PUBLIC,
  1153. });
  1154. expect(pageT).toBeTruthy();
  1155. expect(page1).toBeTruthy();
  1156. expect(page2).toBeTruthy();
  1157. const options = {
  1158. grant: Page.GRANT_RESTRICTED,
  1159. userRelatedGrantUserGroupIds: null,
  1160. };
  1161. await updatePage(
  1162. page2,
  1163. 'newRevisionBody',
  1164. 'oldRevisionBody',
  1165. dummyUser1,
  1166. options,
  1167. );
  1168. const _pageT = await Page.findOne({ path: pathT });
  1169. const _page1 = await Page.findOne({ path: path1 });
  1170. const _page2 = await Page.findOne({
  1171. path: path2,
  1172. grant: Page.GRANT_RESTRICTED,
  1173. });
  1174. expect(_pageT).toBeTruthy();
  1175. expect(_page1).toBeNull();
  1176. expect(_page2).toBeTruthy();
  1177. expect(_pageT?.descendantCount).toBe(1);
  1178. });
  1179. it('a page that has children will create an empty page with the same path and it becomes a new parent', async () => {
  1180. const pathT = '/mup14_top';
  1181. const path1 = '/mup14_top/mup6_pub';
  1182. const path2 = '/mup14_top/mup6_pub/mup7_pub';
  1183. const top = await Page.findOne({ path: pathT, descendantCount: 2 });
  1184. const page1 = await Page.findOne({
  1185. path: path1,
  1186. grant: Page.GRANT_PUBLIC,
  1187. });
  1188. const page2 = await Page.findOne({
  1189. path: path2,
  1190. grant: Page.GRANT_PUBLIC,
  1191. });
  1192. expect(top).toBeTruthy();
  1193. expect(page1).toBeTruthy();
  1194. expect(page2).toBeTruthy();
  1195. await updatePage(
  1196. page1,
  1197. 'newRevisionBody',
  1198. 'oldRevisionBody',
  1199. dummyUser1,
  1200. { grant: Page.GRANT_RESTRICTED },
  1201. );
  1202. const _top = await Page.findOne({ path: pathT });
  1203. const _page1 = await Page.findOne({
  1204. path: path1,
  1205. grant: Page.GRANT_RESTRICTED,
  1206. });
  1207. const _page2 = await Page.findOne({ path: path2 });
  1208. const _pageN = await Page.findOne({
  1209. path: path1,
  1210. grant: Page.GRANT_PUBLIC,
  1211. });
  1212. expect(_page1).toBeTruthy();
  1213. expect(_page2).toBeTruthy();
  1214. expect(_pageN).toBeTruthy();
  1215. expect(_page1?.parent).toBeNull();
  1216. expect(_page2?.parent).toStrictEqual(_pageN?._id);
  1217. expect(_pageN?.parent).toStrictEqual(top?._id);
  1218. expect(_pageN?.isEmpty).toBe(true);
  1219. expect(_pageN?.descendantCount).toBe(1);
  1220. expect(_top?.descendantCount).toBe(1);
  1221. });
  1222. it('of a leaf page will NOT have an empty page with the same path', async () => {
  1223. const pathT = '/mup15_top';
  1224. const path1 = '/mup15_top/mup8_pub';
  1225. const pageT = await Page.findOne({ path: pathT, descendantCount: 1 });
  1226. const page1 = await Page.findOne({
  1227. path: path1,
  1228. grant: Page.GRANT_PUBLIC,
  1229. });
  1230. const count = await Page.countDocuments({ path: path1 });
  1231. expect(pageT).toBeTruthy();
  1232. expect(page1).toBeTruthy();
  1233. expect(count).toBe(1);
  1234. await updatePage(
  1235. page1,
  1236. 'newRevisionBody',
  1237. 'oldRevisionBody',
  1238. dummyUser1,
  1239. { grant: Page.GRANT_RESTRICTED },
  1240. );
  1241. const _pageT = await Page.findOne({ path: pathT });
  1242. const _page1 = await Page.findOne({
  1243. path: path1,
  1244. grant: Page.GRANT_RESTRICTED,
  1245. });
  1246. const _pageNotExist = await Page.findOne({
  1247. path: path1,
  1248. isEmpty: true,
  1249. });
  1250. expect(_pageT).toBeTruthy();
  1251. expect(_page1).toBeTruthy();
  1252. expect(_pageNotExist).toBeNull();
  1253. expect(_pageT?.descendantCount).toBe(0);
  1254. });
  1255. });
  1256. describe('Changing grant to GRANT_RESTRICTED', () => {
  1257. it('successfully change to GRANT_RESTRICTED from GRANT_OWNER', async () => {
  1258. const path = '/mup40';
  1259. const _page = await Page.findOne({
  1260. path,
  1261. grant: Page.GRANT_OWNER,
  1262. grantedUsers: [dummyUser1._id],
  1263. });
  1264. expect(_page).toBeTruthy();
  1265. await updatePage(
  1266. _page,
  1267. 'newRevisionBody',
  1268. 'oldRevisionBody',
  1269. dummyUser1,
  1270. { grant: Page.GRANT_RESTRICTED },
  1271. );
  1272. const page = await Page.findOne({ path });
  1273. expect(page).toBeTruthy();
  1274. expect(page?.grant).toBe(Page.GRANT_RESTRICTED);
  1275. expect(page?.grantedUsers).toStrictEqual([]);
  1276. });
  1277. });
  1278. describe('Changing grant from RESTRICTED to PUBLIC of', () => {
  1279. it('a page will create ancestors if they do not exist', async () => {
  1280. const pathT = '/mup16_top';
  1281. const path1 = '/mup16_top/mup9_pub';
  1282. const path2 = '/mup16_top/mup9_pub/mup10_pub';
  1283. const path3 = '/mup16_top/mup9_pub/mup10_pub/mup11_awl';
  1284. const top = await Page.findOne({ path: pathT });
  1285. const page1 = await Page.findOne({ path: path1 });
  1286. const page2 = await Page.findOne({ path: path2 });
  1287. const page3 = await Page.findOne({
  1288. path: path3,
  1289. grant: Page.GRANT_RESTRICTED,
  1290. });
  1291. expect(top).toBeTruthy();
  1292. expect(page3).toBeTruthy();
  1293. expect(page1).toBeNull();
  1294. expect(page2).toBeNull();
  1295. await updatePage(
  1296. page3,
  1297. 'newRevisionBody',
  1298. 'oldRevisionBody',
  1299. dummyUser1,
  1300. { grant: Page.GRANT_PUBLIC },
  1301. );
  1302. const _pageT = await Page.findOne({ path: pathT });
  1303. const _page1 = await Page.findOne({ path: path1, isEmpty: true });
  1304. const _page2 = await Page.findOne({ path: path2, isEmpty: true });
  1305. const _page3 = await Page.findOne({
  1306. path: path3,
  1307. grant: Page.GRANT_PUBLIC,
  1308. });
  1309. expect(_page1).toBeTruthy();
  1310. expect(_page2).toBeTruthy();
  1311. expect(_page3).toBeTruthy();
  1312. expect(_page1?.parent).toStrictEqual(top?._id);
  1313. expect(_page2?.parent).toStrictEqual(_page1?._id);
  1314. expect(_page3?.parent).toStrictEqual(_page2?._id);
  1315. expect(_pageT?.descendantCount).toBe(1);
  1316. });
  1317. it('a page will replace an empty page with the same path if any', async () => {
  1318. const pathT = '/mup17_top';
  1319. const path1 = '/mup17_top/mup12_emp';
  1320. const path2 = '/mup17_top/mup12_emp/mup18_pub';
  1321. const pageT = await Page.findOne({ path: pathT, descendantCount: 1 });
  1322. const page1 = await Page.findOne({ path: path1, isEmpty: true });
  1323. const page2 = await Page.findOne({
  1324. path: path1,
  1325. grant: Page.GRANT_RESTRICTED,
  1326. isEmpty: false,
  1327. });
  1328. const page3 = await Page.findOne({ path: path2 });
  1329. expect(pageT).toBeTruthy();
  1330. expect(page1).toBeTruthy();
  1331. expect(page2).toBeTruthy();
  1332. expect(page3).toBeTruthy();
  1333. await updatePage(
  1334. page2,
  1335. 'newRevisionBody',
  1336. 'oldRevisionBody',
  1337. dummyUser1,
  1338. { grant: Page.GRANT_PUBLIC },
  1339. );
  1340. const _pageT = await Page.findOne({ path: pathT });
  1341. const _page1 = await Page.findOne({ path: path1, isEmpty: true }); // should be replaced
  1342. const _page2 = await Page.findOne({
  1343. path: path1,
  1344. grant: Page.GRANT_PUBLIC,
  1345. });
  1346. const _page3 = await Page.findOne({ path: path2 });
  1347. expect(_pageT).toBeTruthy();
  1348. expect(_page1).toBeNull();
  1349. expect(_page2).toBeTruthy();
  1350. expect(_page3).toBeTruthy();
  1351. expect(_page2?.grant).toBe(Page.GRANT_PUBLIC);
  1352. expect(_page2?.parent).toStrictEqual(_pageT?._id);
  1353. expect(_page3?.parent).toStrictEqual(_page2?._id);
  1354. expect(_pageT?.descendantCount).toBe(2);
  1355. });
  1356. });
  1357. describe('Changing grant to GRANT_OWNER(onlyme)', () => {
  1358. it('successfully change to GRANT_OWNER from GRANT_PUBLIC', async () => {
  1359. const path = '/mup19';
  1360. const _page = await Page.findOne({ path, grant: Page.GRANT_PUBLIC });
  1361. expect(_page).toBeTruthy();
  1362. await updatePage(
  1363. _page,
  1364. 'newRevisionBody',
  1365. 'oldRevisionBody',
  1366. dummyUser1,
  1367. { grant: Page.GRANT_OWNER },
  1368. );
  1369. const page = await Page.findOne({ path });
  1370. expect(page?.grant).toBe(Page.GRANT_OWNER);
  1371. expect(page?.grantedUsers).toStrictEqual([dummyUser1._id]);
  1372. });
  1373. it('successfully change to GRANT_OWNER from GRANT_USER_GROUP', async () => {
  1374. const path = '/mup20';
  1375. const _page = await Page.findOne({
  1376. path,
  1377. grant: Page.GRANT_USER_GROUP,
  1378. grantedGroups: { $elemMatch: { item: userGroupIdPModelA } },
  1379. });
  1380. expect(_page).toBeTruthy();
  1381. await updatePage(
  1382. _page,
  1383. 'newRevisionBody',
  1384. 'oldRevisionBody',
  1385. pModelUser1,
  1386. { grant: Page.GRANT_OWNER },
  1387. );
  1388. const page = await Page.findOne({ path });
  1389. expect(page?.grant).toBe(Page.GRANT_OWNER);
  1390. expect(page?.grantedUsers).toStrictEqual([pModelUser1._id]);
  1391. expect(page?.grantedGroups?.length).toBe(0);
  1392. });
  1393. it('successfully change to GRANT_OWNER from GRANT_RESTRICTED', async () => {
  1394. const path = '/mup21';
  1395. const _page = await Page.findOne({
  1396. path,
  1397. grant: Page.GRANT_RESTRICTED,
  1398. });
  1399. expect(_page).toBeTruthy();
  1400. await updatePage(
  1401. _page,
  1402. 'newRevisionBody',
  1403. 'oldRevisionBody',
  1404. dummyUser1,
  1405. { grant: Page.GRANT_OWNER },
  1406. );
  1407. const page = await Page.findOne({ path });
  1408. expect(page?.grant).toBe(Page.GRANT_OWNER);
  1409. expect(page?.grantedUsers).toStrictEqual([dummyUser1._id]);
  1410. });
  1411. it('Failed to change to GRANT_OWNER if one of the ancestors is GRANT_USER_GROUP page', async () => {
  1412. const path1 = '/mup22';
  1413. const path2 = '/mup22/mup23';
  1414. const _page1 = await Page.findOne({
  1415. path: path1,
  1416. grant: Page.GRANT_PUBLIC,
  1417. });
  1418. const _page2 = await Page.findOne({
  1419. path: path2,
  1420. grant: Page.GRANT_USER_GROUP,
  1421. grantedGroups: { $elemMatch: { item: userGroupIdPModelA } },
  1422. });
  1423. expect(_page1).toBeTruthy();
  1424. expect(_page2).toBeTruthy();
  1425. await expect(
  1426. updatePage(_page1, 'newRevisionBody', 'oldRevisionBody', dummyUser1, {
  1427. grant: Page.GRANT_OWNER,
  1428. }),
  1429. ).rejects.toThrow(
  1430. new Error(
  1431. 'The selected grant or grantedGroup is not assignable to this page.',
  1432. ),
  1433. );
  1434. const page1 = await Page.findOne({ path1 });
  1435. expect(page1).toBeTruthy();
  1436. expect(page1?.grant).toBe(Page.GRANT_PUBLIC);
  1437. expect(page1?.grantedUsers).not.toStrictEqual([dummyUser1._id]);
  1438. });
  1439. });
  1440. describe('Changing grant to GRANT_USER_GROUP', () => {
  1441. describe('update grant of a page under a page with GRANT_PUBLIC', () => {
  1442. it('successfully change to GRANT_USER_GROUP from GRANT_PUBLIC if parent page is GRANT_PUBLIC', async () => {
  1443. // path
  1444. const path1 = '/mup24_pub';
  1445. const path2 = '/mup24_pub/mup25_pub';
  1446. // page
  1447. const _page1 = await Page.findOne({
  1448. path: path1,
  1449. grant: Page.GRANT_PUBLIC,
  1450. }); // out of update scope
  1451. const _page2 = await Page.findOne({
  1452. path: path2,
  1453. grant: Page.GRANT_PUBLIC,
  1454. parent: _page1?._id,
  1455. }); // update target
  1456. expect(_page1).toBeTruthy();
  1457. expect(_page2).toBeTruthy();
  1458. const newGrantedGroups = [
  1459. { item: userGroupIdPModelA, type: GroupType.userGroup },
  1460. {
  1461. item: externalUserGroupIdPModelA,
  1462. type: GroupType.externalUserGroup,
  1463. },
  1464. ];
  1465. const options = {
  1466. grant: Page.GRANT_USER_GROUP,
  1467. userRelatedGrantUserGroupIds: newGrantedGroups,
  1468. };
  1469. const updatedPage = await updatePage(
  1470. _page2,
  1471. 'new',
  1472. 'old',
  1473. pModelUser1,
  1474. options,
  1475. ); // from GRANT_PUBLIC to GRANT_USER_GROUP(userGroupIdPModelA)
  1476. const page1 = await Page.findById(_page1?._id);
  1477. const page2 = await Page.findById(_page2?._id);
  1478. expect(page1).toBeTruthy();
  1479. expect(page2).toBeTruthy();
  1480. expect(updatedPage).toBeTruthy();
  1481. expect(updatedPage._id).toStrictEqual(page2?._id);
  1482. // check page2 grant and group
  1483. expect(page2?.grant).toBe(Page.GRANT_USER_GROUP);
  1484. expect(normalizeGrantedGroups(page2?.grantedGroups)).toStrictEqual(
  1485. newGrantedGroups,
  1486. );
  1487. });
  1488. it('successfully change to GRANT_USER_GROUP from GRANT_RESTRICTED if parent page is GRANT_PUBLIC', async () => {
  1489. // path
  1490. const _path1 = '/mup26_awl';
  1491. // page
  1492. const _page1 = await Page.findOne({
  1493. path: _path1,
  1494. grant: Page.GRANT_RESTRICTED,
  1495. });
  1496. expect(_page1).toBeTruthy();
  1497. const newGrantedGroups = [
  1498. { item: userGroupIdPModelA, type: GroupType.userGroup },
  1499. {
  1500. item: externalUserGroupIdPModelA,
  1501. type: GroupType.externalUserGroup,
  1502. },
  1503. ];
  1504. const options = {
  1505. grant: Page.GRANT_USER_GROUP,
  1506. userRelatedGrantUserGroupIds: newGrantedGroups,
  1507. };
  1508. const updatedPage = await updatePage(
  1509. _page1,
  1510. 'new',
  1511. 'old',
  1512. pModelUser1,
  1513. options,
  1514. ); // from GRANT_RESTRICTED to GRANT_USER_GROUP(userGroupIdPModelA)
  1515. const page1 = await Page.findById(_page1?._id);
  1516. expect(page1).toBeTruthy();
  1517. expect(updatedPage).toBeTruthy();
  1518. expect(updatedPage._id).toStrictEqual(page1?._id);
  1519. // updated page
  1520. expect(page1?.grant).toBe(Page.GRANT_USER_GROUP);
  1521. expect(normalizeGrantedGroups(page1?.grantedGroups)).toStrictEqual(
  1522. newGrantedGroups,
  1523. );
  1524. // parent's grant check
  1525. const parent = await Page.findById(page1?.parent);
  1526. expect(parent?.grant).toBe(Page.GRANT_PUBLIC);
  1527. });
  1528. it('successfully change to GRANT_USER_GROUP from GRANT_OWNER if parent page is GRANT_PUBLIC', async () => {
  1529. // path
  1530. const path1 = '/mup27_pub';
  1531. const path2 = '/mup27_pub/mup28_owner';
  1532. // page
  1533. const _page1 = await Page.findOne({
  1534. path: path1,
  1535. grant: Page.GRANT_PUBLIC,
  1536. }); // out of update scope
  1537. const _page2 = await Page.findOne({
  1538. path: path2,
  1539. grant: Page.GRANT_OWNER,
  1540. grantedUsers: [pModelUser1],
  1541. parent: _page1?._id,
  1542. }); // update target
  1543. expect(_page1).toBeTruthy();
  1544. expect(_page2).toBeTruthy();
  1545. const newGrantedGroups = [
  1546. { item: userGroupIdPModelA, type: GroupType.userGroup },
  1547. {
  1548. item: externalUserGroupIdPModelA,
  1549. type: GroupType.externalUserGroup,
  1550. },
  1551. ];
  1552. const options = {
  1553. grant: Page.GRANT_USER_GROUP,
  1554. userRelatedGrantUserGroupIds: newGrantedGroups,
  1555. };
  1556. const updatedPage = await updatePage(
  1557. _page2,
  1558. 'new',
  1559. 'old',
  1560. pModelUser1,
  1561. options,
  1562. ); // from GRANT_OWNER to GRANT_USER_GROUP(userGroupIdPModelA)
  1563. const page1 = await Page.findById(_page1?._id);
  1564. const page2 = await Page.findById(_page2?._id);
  1565. expect(page1).toBeTruthy();
  1566. expect(page2).toBeTruthy();
  1567. expect(updatedPage).toBeTruthy();
  1568. expect(updatedPage._id).toStrictEqual(page2?._id);
  1569. // grant check
  1570. expect(page2?.grant).toBe(Page.GRANT_USER_GROUP);
  1571. expect(normalizeGrantedGroups(page2?.grantedGroups)).toStrictEqual(
  1572. newGrantedGroups,
  1573. );
  1574. expect(page2?.grantedUsers?.length).toBe(0);
  1575. });
  1576. });
  1577. describe('update grant of a page under a page with GRANT_USER_GROUP', () => {
  1578. it('successfully change to GRANT_USER_GROUP if the group to set is the child or descendant of the parent page group', async () => {
  1579. // path
  1580. const _path1 = '/mup29_A';
  1581. const _path2 = '/mup29_A/mup30_owner';
  1582. // page
  1583. const _page1 = await Page.findOne({
  1584. path: _path1,
  1585. grant: Page.GRANT_USER_GROUP,
  1586. grantedGroups: { $elemMatch: { item: userGroupIdPModelA } },
  1587. }); // out of update scope
  1588. const _page2 = await Page.findOne({
  1589. // update target
  1590. path: _path2,
  1591. grant: Page.GRANT_OWNER,
  1592. grantedUsers: [pModelUser1],
  1593. parent: _page1?._id,
  1594. });
  1595. expect(_page1).toBeTruthy();
  1596. expect(_page2).toBeTruthy();
  1597. // First round
  1598. // Group relation(parent -> child): userGroupIdPModelA -> userGroupIdPModelB -> userGroupIdPModelC
  1599. const newGrantedGroups = [
  1600. { item: userGroupIdPModelB, type: GroupType.userGroup },
  1601. {
  1602. item: externalUserGroupIdPModelB,
  1603. type: GroupType.externalUserGroup,
  1604. },
  1605. ];
  1606. const options = {
  1607. grant: Page.GRANT_USER_GROUP,
  1608. userRelatedGrantUserGroupIds: newGrantedGroups,
  1609. };
  1610. const updatedPage = await updatePage(
  1611. _page2,
  1612. 'new',
  1613. 'old',
  1614. pModelUser3,
  1615. options,
  1616. ); // from GRANT_OWNER to GRANT_USER_GROUP(userGroupIdPModelB)
  1617. const page1 = await Page.findById(_page1?._id);
  1618. const page2 = await Page.findById(_page2?._id);
  1619. expect(page1).toBeTruthy();
  1620. expect(page2).toBeTruthy();
  1621. expect(updatedPage).toBeTruthy();
  1622. expect(updatedPage._id).toStrictEqual(page2?._id);
  1623. expect(page2?.grant).toBe(Page.GRANT_USER_GROUP);
  1624. expect(normalizeGrantedGroups(page2?.grantedGroups)).toStrictEqual(
  1625. newGrantedGroups,
  1626. );
  1627. expect(page2?.grantedUsers?.length).toBe(0);
  1628. // Second round
  1629. // Update group to groupC which is a grandchild from pageA's point of view
  1630. const secondRoundNewGrantedGroups = [
  1631. { item: userGroupIdPModelC, type: GroupType.userGroup },
  1632. {
  1633. item: externalUserGroupIdPModelC,
  1634. type: GroupType.externalUserGroup,
  1635. },
  1636. ];
  1637. const secondRoundOptions = {
  1638. grant: Page.GRANT_USER_GROUP,
  1639. userRelatedGrantUserGroupIds: secondRoundNewGrantedGroups,
  1640. }; // from GRANT_USER_GROUP(userGroupIdPModelB) to GRANT_USER_GROUP(userGroupIdPModelC)
  1641. // undo grantedGroups populate to prevent Page.hydrate error
  1642. _page2?.grantedGroups?.forEach((group) => {
  1643. // biome-ignore lint/suspicious/noExplicitAny: <explanation>
  1644. (group as any).item = (group.item as any)._id;
  1645. });
  1646. const secondRoundUpdatedPage = await updatePage(
  1647. _page2,
  1648. 'new',
  1649. 'new',
  1650. pModelUser3,
  1651. secondRoundOptions,
  1652. );
  1653. expect(secondRoundUpdatedPage).toBeTruthy();
  1654. expect(secondRoundUpdatedPage.grant).toBe(Page.GRANT_USER_GROUP);
  1655. expect(
  1656. normalizeGrantedGroups(secondRoundUpdatedPage.grantedGroups),
  1657. ).toStrictEqual(secondRoundNewGrantedGroups);
  1658. });
  1659. it('Fail to change to GRANT_USER_GROUP if the group to set is NOT the child or descendant of the parent page group', async () => {
  1660. // path
  1661. const _path1 = '/mup31_A';
  1662. const _path2 = '/mup31_A/mup32_owner';
  1663. // page
  1664. const _page1 = await Page.findOne({
  1665. path: _path1,
  1666. grant: Page.GRANT_USER_GROUP,
  1667. grantedGroups: { $elemMatch: { item: userGroupIdPModelA } },
  1668. });
  1669. const _page2 = await Page.findOne({
  1670. // update target
  1671. path: _path2,
  1672. grant: Page.GRANT_OWNER,
  1673. grantedUsers: [pModelUser1._id],
  1674. parent: _page1?._id,
  1675. });
  1676. expect(_page1).toBeTruthy();
  1677. expect(_page2).toBeTruthy();
  1678. // group
  1679. const _groupIsolated = await UserGroup.findById(
  1680. userGroupIdPModelIsolate,
  1681. );
  1682. expect(_groupIsolated).toBeTruthy();
  1683. // group parent check
  1684. expect(_groupIsolated?.parent).toBeUndefined(); // should have no parent
  1685. const options = {
  1686. grant: Page.GRANT_USER_GROUP,
  1687. userRelatedGrantUserGroupIds: [
  1688. { item: userGroupIdPModelIsolate, type: GroupType.userGroup },
  1689. {
  1690. item: externalUserGroupIdPModelIsolate,
  1691. type: GroupType.externalUserGroup,
  1692. },
  1693. ],
  1694. };
  1695. await expect(updatePage(_page2, 'new', 'old', pModelUser1, options)) // from GRANT_OWNER to GRANT_USER_GROUP(userGroupIdPModelIsolate)
  1696. .rejects.toThrow(
  1697. new Error(
  1698. 'The selected grant or grantedGroup is not assignable to this page.',
  1699. ),
  1700. );
  1701. const page1 = await Page.findById(_page1?._id);
  1702. const page2 = await Page.findById(_page2?._id);
  1703. expect(page1).toBeTruthy();
  1704. expect(page1).toBeTruthy();
  1705. expect(page2?.grant).toBe(Page.GRANT_OWNER); // should be the same before the update
  1706. expect(page2?.grantedUsers).toStrictEqual([pModelUser1._id]); // should be the same before the update
  1707. expect(page2?.grantedGroups?.length).toBe(0); // no group should be set
  1708. });
  1709. it('Fail to change to GRANT_USER_GROUP if the group to set is an ancestor of the parent page group', async () => {
  1710. // path
  1711. const _path1 = '/mup33_C';
  1712. const _path2 = '/mup33_C/mup34_owner';
  1713. // page
  1714. const _page1 = await Page.findOne({
  1715. path: _path1,
  1716. grant: Page.GRANT_USER_GROUP,
  1717. grantedGroups: { $elemMatch: { item: userGroupIdPModelC } },
  1718. }); // groupC
  1719. const _page2 = await Page.findOne({
  1720. // update target
  1721. path: _path2,
  1722. grant: Page.GRANT_OWNER,
  1723. grantedUsers: [pModelUser3],
  1724. parent: _page1?._id,
  1725. });
  1726. expect(_page1).toBeTruthy();
  1727. expect(_page2).toBeTruthy();
  1728. const options = {
  1729. grant: Page.GRANT_USER_GROUP,
  1730. userRelatedGrantUserGroupIds: [
  1731. { item: userGroupIdPModelA, type: GroupType.userGroup },
  1732. {
  1733. item: externalUserGroupIdPModelA,
  1734. type: GroupType.externalUserGroup,
  1735. },
  1736. ],
  1737. };
  1738. // Group relation(parent -> child): userGroupIdPModelA -> userGroupIdPModelB -> userGroupIdPModelC
  1739. // this should fail because the groupC is a descendant of groupA
  1740. await expect(updatePage(_page2, 'new', 'old', pModelUser3, options)) // from GRANT_OWNER to GRANT_USER_GROUP(userGroupIdPModelA)
  1741. .rejects.toThrow(
  1742. new Error(
  1743. 'The selected grant or grantedGroup is not assignable to this page.',
  1744. ),
  1745. );
  1746. const page1 = await Page.findById(_page1?._id);
  1747. const page2 = await Page.findById(_page2?._id);
  1748. expect(page1).toBeTruthy();
  1749. expect(page2).toBeTruthy();
  1750. expect(page2?.grant).toBe(Page.GRANT_OWNER); // should be the same before the update
  1751. expect(page2?.grantedUsers).toStrictEqual([pModelUser3._id]); // should be the same before the update
  1752. expect(page2?.grantedGroups?.length).toBe(0); // no group should be set
  1753. });
  1754. });
  1755. describe('update grant of a page under a page with GRANT_OWNER', () => {
  1756. it('Fail to change from GRNAT_OWNER', async () => {
  1757. // path
  1758. const path1 = '/mup35_owner';
  1759. const path2 = '/mup35_owner/mup36_owner';
  1760. // page
  1761. const _page1 = await Page.findOne({
  1762. path: path1,
  1763. grant: Page.GRANT_OWNER,
  1764. grantedUsers: [pModelUser1],
  1765. });
  1766. const _page2 = await Page.findOne({
  1767. // update target
  1768. path: path2,
  1769. grant: Page.GRANT_OWNER,
  1770. grantedUsers: [pModelUser1],
  1771. parent: _page1?._id,
  1772. });
  1773. expect(_page1).toBeTruthy();
  1774. expect(_page2).toBeTruthy();
  1775. const options = {
  1776. grant: Page.GRANT_USER_GROUP,
  1777. userRelatedGrantUserGroupIds: [
  1778. { item: userGroupIdPModelA, type: GroupType.userGroup },
  1779. ],
  1780. };
  1781. await expect(updatePage(_page2, 'new', 'old', pModelUser1, options)) // from GRANT_OWNER to GRANT_USER_GROUP(userGroupIdPModelA)
  1782. .rejects.toThrow(
  1783. new Error(
  1784. 'The selected grant or grantedGroup is not assignable to this page.',
  1785. ),
  1786. );
  1787. const page1 = await Page.findById(_page1?.id);
  1788. const page2 = await Page.findById(_page2?.id);
  1789. expect(page1).toBeTruthy();
  1790. expect(page2).toBeTruthy();
  1791. expect(page2?.grant).toBe(Page.GRANT_OWNER); // should be the same before the update
  1792. expect(page2?.grantedUsers).toStrictEqual([pModelUser1._id]); // should be the same before the update
  1793. expect(page2?.grantedGroups?.length).toBe(0); // no group should be set
  1794. });
  1795. });
  1796. describe('update grant of a page from GRANT_USER_GROUP to GRANT_USER_GROUP', () => {
  1797. it('successfully change the granted groups, with the previous groups wich user is not related to remaining', async () => {
  1798. // path
  1799. const path = '/with_multiple_individual_granted_groups';
  1800. // page
  1801. const _page = await Page.findOne({
  1802. path,
  1803. grant: Page.GRANT_USER_GROUP,
  1804. });
  1805. expect(_page).toBeTruthy();
  1806. const newUserRelatedGrantedGroups = [
  1807. { item: userGroupIdPModelA, type: GroupType.userGroup },
  1808. {
  1809. item: externalUserGroupIdPModelA,
  1810. type: GroupType.externalUserGroup,
  1811. },
  1812. ];
  1813. const options = {
  1814. grant: Page.GRANT_USER_GROUP,
  1815. userRelatedGrantUserGroupIds: newUserRelatedGrantedGroups,
  1816. };
  1817. const updatedPage = await updatePage(
  1818. _page,
  1819. 'new',
  1820. 'old',
  1821. pModelUser1,
  1822. options,
  1823. ); // from GRANT_PUBLIC to GRANT_USER_GROUP(userGroupIdPModelA)
  1824. const page = await Page.findById(_page?._id);
  1825. expect(page).toBeTruthy();
  1826. expect(updatedPage).toBeTruthy();
  1827. expect(updatedPage._id).toStrictEqual(page?._id);
  1828. // check page grant and group
  1829. expect(page?.grant).toBe(Page.GRANT_USER_GROUP);
  1830. expect(normalizeGrantedGroups(page?.grantedGroups)).toEqual(
  1831. expect.arrayContaining([
  1832. ...newUserRelatedGrantedGroups,
  1833. // userB group remains, although options does not include it
  1834. { item: userGroupIdPModelB, type: GroupType.userGroup },
  1835. ]),
  1836. );
  1837. expect(normalizeGrantedGroups(page?.grantedGroups)?.length).toBe(3);
  1838. });
  1839. });
  1840. });
  1841. });
  1842. // see: https://dev.growi.org/635a314eac6bcd85cbf359fc about the specification
  1843. describe('updatePage with overwriteScopesOfDescendants true', () => {
  1844. it('(case 1) it should update all granted descendant pages when update grant is GRANT_PUBLIC', async () => {
  1845. const upodPagegAB = await Page.findOne({ path: '/gAB_upod_1' });
  1846. const upodPagegB = await Page.findOne({ path: '/gAB_upod_1/gB_upod_1' });
  1847. const upodPageonlyB = await Page.findOne({
  1848. path: '/gAB_upod_1/onlyB_upod_1',
  1849. });
  1850. const upodPagegAgB = await Page.findOne({
  1851. path: '/gAB_upod_1/gA_gB_upod_1',
  1852. });
  1853. expect(upodPagegAB).not.toBeNull();
  1854. expect(upodPagegB).not.toBeNull();
  1855. expect(upodPageonlyB).not.toBeNull();
  1856. expect(upodPagegAgB).not.toBeNull();
  1857. expect(upodPagegAB?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  1858. expect(upodPagegB?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  1859. expect(upodPageonlyB?.grant).toBe(PageGrant.GRANT_OWNER);
  1860. expect(upodPagegAgB?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  1861. // Update
  1862. const options = {
  1863. grant: PageGrant.GRANT_PUBLIC,
  1864. overwriteScopesOfDescendants: true,
  1865. };
  1866. const updatedPage = await updatePage(
  1867. upodPagegAB,
  1868. 'newRevisionBody',
  1869. 'oldRevisionBody',
  1870. upodUserA,
  1871. options,
  1872. );
  1873. const upodPagegBUpdated = await Page.findOne({
  1874. path: '/gAB_upod_1/gB_upod_1',
  1875. });
  1876. const upodPageonlyBUpdated = await Page.findOne({
  1877. path: '/gAB_upod_1/onlyB_upod_1',
  1878. });
  1879. const upodPagegAgBUpdated = await Page.findOne({
  1880. path: '/gAB_upod_1/gA_gB_upod_1',
  1881. });
  1882. // Changed
  1883. const newGrant = PageGrant.GRANT_PUBLIC;
  1884. expect(updatedPage.grant).toBe(newGrant);
  1885. // Not changed
  1886. expect(upodPagegBUpdated?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  1887. expect(upodPagegBUpdated?.grantedGroups).toStrictEqual(
  1888. upodPagegB?.grantedGroups,
  1889. );
  1890. expect(upodPageonlyBUpdated?.grant).toBe(PageGrant.GRANT_OWNER);
  1891. expect(upodPageonlyBUpdated?.grantedUsers).toStrictEqual(
  1892. upodPageonlyB?.grantedUsers,
  1893. );
  1894. expect(upodPagegAgBUpdated?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  1895. expect(upodPagegAgBUpdated?.grantedGroups).toStrictEqual(
  1896. upodPagegAgB?.grantedGroups,
  1897. );
  1898. });
  1899. it('(case 2) it should update all granted descendant pages when all descendant pages are granted to the operator', async () => {
  1900. const upodPagePublic = await Page.findOne({ path: '/public_upod_2' });
  1901. const upodPagegA = await Page.findOne({
  1902. path: '/public_upod_2/gA_upod_2',
  1903. });
  1904. const upodPagegAIsolated = await Page.findOne({
  1905. path: '/public_upod_2/gAIsolated_upod_2',
  1906. });
  1907. const upodPageonlyA = await Page.findOne({
  1908. path: '/public_upod_2/onlyA_upod_2',
  1909. });
  1910. expect(upodPagePublic).not.toBeNull();
  1911. expect(upodPagegA).not.toBeNull();
  1912. expect(upodPagegAIsolated).not.toBeNull();
  1913. expect(upodPageonlyA).not.toBeNull();
  1914. expect(upodPagePublic?.grant).toBe(PageGrant.GRANT_PUBLIC);
  1915. expect(upodPagegA?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  1916. expect(upodPagegAIsolated?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  1917. expect(upodPageonlyA?.grant).toBe(PageGrant.GRANT_OWNER);
  1918. // Update
  1919. const options = {
  1920. grant: PageGrant.GRANT_OWNER,
  1921. overwriteScopesOfDescendants: true,
  1922. };
  1923. const updatedPage = await updatePage(
  1924. upodPagePublic,
  1925. 'newRevisionBody',
  1926. 'oldRevisionBody',
  1927. upodUserA,
  1928. options,
  1929. );
  1930. const upodPagegAUpdated = await Page.findOne({
  1931. path: '/public_upod_2/gA_upod_2',
  1932. });
  1933. const upodPagegAIsolatedUpdated = await Page.findOne({
  1934. path: '/public_upod_2/gAIsolated_upod_2',
  1935. });
  1936. const upodPageonlyAUpdated = await Page.findOne({
  1937. path: '/public_upod_2/onlyA_upod_2',
  1938. });
  1939. // Changed
  1940. const newGrant = PageGrant.GRANT_OWNER;
  1941. const newGrantedUsers = [upodUserA._id];
  1942. expect(updatedPage.grant).toBe(newGrant);
  1943. expect(updatedPage.grantedUsers).toStrictEqual(newGrantedUsers);
  1944. expect(upodPagegAUpdated?.grant).toBe(newGrant);
  1945. expect(upodPagegAUpdated?.grantedUsers).toStrictEqual(newGrantedUsers);
  1946. expect(upodPagegAIsolatedUpdated?.grant).toBe(newGrant);
  1947. expect(upodPagegAIsolatedUpdated?.grantedUsers).toStrictEqual(
  1948. newGrantedUsers,
  1949. );
  1950. expect(upodPageonlyAUpdated?.grant).toBe(newGrant);
  1951. expect(upodPageonlyAUpdated?.grantedUsers).toStrictEqual(newGrantedUsers);
  1952. });
  1953. it(`(case 3) it should update all granted descendant pages when update grant is GRANT_USER_GROUP
  1954. , all user groups of descendants are the children or itself of the update user group
  1955. , and all users of descendants belong to the update user group`, async () => {
  1956. const upodPagePublic = await Page.findOne({ path: '/public_upod_3' });
  1957. const upodPagegAB = await Page.findOne({
  1958. path: '/public_upod_3/gAB_upod_3',
  1959. });
  1960. const upodPagegAgB = await Page.findOne({
  1961. path: '/public_upod_3/gA_gB_upod_3',
  1962. });
  1963. const upodPagegB = await Page.findOne({
  1964. path: '/public_upod_3/gB_upod_3',
  1965. });
  1966. const upodPageonlyB = await Page.findOne({
  1967. path: '/public_upod_3/onlyB_upod_3',
  1968. });
  1969. expect(upodPagePublic).not.toBeNull();
  1970. expect(upodPagegAB).not.toBeNull();
  1971. expect(upodPagegAgB).not.toBeNull();
  1972. expect(upodPagegB).not.toBeNull();
  1973. expect(upodPageonlyB).not.toBeNull();
  1974. expect(upodPagePublic?.grant).toBe(PageGrant.GRANT_PUBLIC);
  1975. expect(upodPagegAB?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  1976. expect(upodPagegAgB?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  1977. expect(upodPagegB?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  1978. expect(upodPageonlyB?.grant).toBe(PageGrant.GRANT_OWNER);
  1979. // Update
  1980. const options = {
  1981. grant: PageGrant.GRANT_USER_GROUP,
  1982. userRelatedGrantUserGroupIds: [
  1983. { item: upodUserGroupIdAB, type: GroupType.userGroup },
  1984. {
  1985. item: upodExternalUserGroupIdAB,
  1986. type: GroupType.externalUserGroup,
  1987. },
  1988. ],
  1989. overwriteScopesOfDescendants: true,
  1990. };
  1991. const updatedPage = await updatePage(
  1992. upodPagePublic,
  1993. 'newRevisionBody',
  1994. 'oldRevisionBody',
  1995. upodUserA,
  1996. options,
  1997. );
  1998. const upodPagegABUpdated = await Page.findOne({
  1999. path: '/public_upod_3/gAB_upod_3',
  2000. });
  2001. const upodPagegAgBUpdated = await Page.findOne({
  2002. path: '/public_upod_3/gA_gB_upod_3',
  2003. });
  2004. const upodPagegBUpdated = await Page.findOne({
  2005. path: '/public_upod_3/gB_upod_3',
  2006. });
  2007. const upodPageonlyBUpdated = await Page.findOne({
  2008. path: '/public_upod_3/onlyB_upod_3',
  2009. });
  2010. // Changed
  2011. const newGrant = PageGrant.GRANT_USER_GROUP;
  2012. const newGrantedGroups = [
  2013. { item: upodUserGroupIdAB, type: GroupType.userGroup },
  2014. { item: upodExternalUserGroupIdAB, type: GroupType.externalUserGroup },
  2015. ];
  2016. expect(updatedPage.grant).toBe(newGrant);
  2017. expect(normalizeGrantedGroups(updatedPage.grantedGroups)).toStrictEqual(
  2018. newGrantedGroups,
  2019. );
  2020. expect(upodPagegABUpdated?.grant).toBe(newGrant);
  2021. expect(
  2022. normalizeGrantedGroups(upodPagegABUpdated?.grantedGroups),
  2023. ).toStrictEqual(newGrantedGroups);
  2024. expect(upodPagegAgBUpdated?.grant).toBe(newGrant);
  2025. // For multi group granted pages, the grant update will only add/remove groups that the user belongs to,
  2026. // and groups that the user doesn't belong to will stay as it was before the update.
  2027. expect(
  2028. normalizeGrantedGroups(upodPagegAgBUpdated?.grantedGroups),
  2029. ).toEqual(
  2030. expect.arrayContaining([
  2031. ...newGrantedGroups,
  2032. { item: upodUserGroupIdB, type: GroupType.userGroup },
  2033. { item: upodExternalUserGroupIdB, type: GroupType.externalUserGroup },
  2034. ]),
  2035. );
  2036. expect(
  2037. normalizeGrantedGroups(upodPagegAgBUpdated?.grantedGroups)?.length,
  2038. ).toBe(4);
  2039. // Not changed
  2040. expect(upodPagegBUpdated?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  2041. expect(upodPagegBUpdated?.grantedGroups).toStrictEqual(
  2042. upodPagegB?.grantedGroups,
  2043. );
  2044. expect(upodPageonlyBUpdated?.grant).toBe(PageGrant.GRANT_OWNER);
  2045. expect(upodPageonlyBUpdated?.grantedUsers).toStrictEqual(
  2046. upodPageonlyB?.grantedUsers,
  2047. );
  2048. });
  2049. it(`(case 4) it should throw when some of descendants is not granted
  2050. , update grant is GRANT_USER_GROUP
  2051. , and some of user groups of descendants are not children or itself of the update user group`, async () => {
  2052. const upodPagePublic = await Page.findOne({ path: '/public_upod_4' });
  2053. const upodPagegA = await Page.findOne({
  2054. path: '/public_upod_4/gA_upod_4',
  2055. });
  2056. const upodPagegC = await Page.findOne({
  2057. path: '/public_upod_4/gC_upod_4',
  2058. });
  2059. expect(upodPagePublic).not.toBeNull();
  2060. expect(upodPagegA).not.toBeNull();
  2061. expect(upodPagegC).not.toBeNull();
  2062. expect(upodPagePublic?.grant).toBe(PageGrant.GRANT_PUBLIC);
  2063. expect(upodPagegA?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  2064. expect(upodPagegC?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  2065. // Update
  2066. const options = {
  2067. grant: PageGrant.GRANT_USER_GROUP,
  2068. userRelatedGrantUserGroupIds: [
  2069. { item: upodUserGroupIdAB, type: GroupType.userGroup },
  2070. {
  2071. item: upodExternalUserGroupIdAB,
  2072. type: GroupType.externalUserGroup,
  2073. },
  2074. ],
  2075. overwriteScopesOfDescendants: true,
  2076. };
  2077. const updatedPagePromise = updatePage(
  2078. upodPagePublic,
  2079. 'newRevisionBody',
  2080. 'oldRevisionBody',
  2081. upodUserA,
  2082. options,
  2083. );
  2084. await expect(updatedPagePromise).rejects.toThrowError();
  2085. });
  2086. it(`(case 5) it should throw when some of descendants is not granted
  2087. , update grant is GRANT_USER_GROUP
  2088. , and some of users of descendants does NOT belong to the update user group`, async () => {
  2089. const upodPagePublic = await Page.findOne({ path: '/public_upod_5' });
  2090. const upodPagegA = await Page.findOne({
  2091. path: '/public_upod_5/gA_upod_5',
  2092. });
  2093. const upodPageonlyC = await Page.findOne({
  2094. path: '/public_upod_5/onlyC_upod_5',
  2095. });
  2096. expect(upodPagePublic).not.toBeNull();
  2097. expect(upodPagegA).not.toBeNull();
  2098. expect(upodPageonlyC).not.toBeNull();
  2099. expect(upodPagePublic?.grant).toBe(PageGrant.GRANT_PUBLIC);
  2100. expect(upodPagegA?.grant).toBe(PageGrant.GRANT_USER_GROUP);
  2101. expect(upodPageonlyC?.grant).toBe(PageGrant.GRANT_OWNER);
  2102. // Update
  2103. const options = {
  2104. grant: PageGrant.GRANT_USER_GROUP,
  2105. userRelatedGrantUserGroupIds: [
  2106. { item: upodUserGroupIdAB, type: GroupType.userGroup },
  2107. {
  2108. item: upodExternalUserGroupIdAB,
  2109. type: GroupType.externalUserGroup,
  2110. },
  2111. ],
  2112. overwriteScopesOfDescendants: true,
  2113. };
  2114. const updatedPagePromise = updatePage(
  2115. upodPagePublic,
  2116. 'newRevisionBody',
  2117. 'oldRevisionBody',
  2118. upodUserA,
  2119. options,
  2120. );
  2121. await expect(updatedPagePromise).rejects.toThrowError();
  2122. });
  2123. it('(case 6) it should throw when some of descendants is not granted and update grant is GRANT_OWNER', async () => {
  2124. const upodPagePublic = await Page.findOne({ path: '/public_upod_6' });
  2125. const upodPageonlyC = await Page.findOne({
  2126. path: '/public_upod_6/onlyC_upod_6',
  2127. });
  2128. expect(upodPagePublic).not.toBeNull();
  2129. expect(upodPageonlyC).not.toBeNull();
  2130. expect(upodPagePublic?.grant).toBe(PageGrant.GRANT_PUBLIC);
  2131. expect(upodPageonlyC?.grant).toBe(PageGrant.GRANT_OWNER);
  2132. // Update
  2133. const options = {
  2134. grant: PageGrant.GRANT_USER_GROUP,
  2135. userRelatedGrantUserGroupIds: [
  2136. { item: upodUserGroupIdAB, type: GroupType.userGroup },
  2137. {
  2138. item: upodExternalUserGroupIdAB,
  2139. type: GroupType.externalUserGroup,
  2140. },
  2141. ],
  2142. overwriteScopesOfDescendants: true,
  2143. };
  2144. const updatedPagePromise = updatePage(
  2145. upodPagePublic,
  2146. 'newRevisionBody',
  2147. 'oldRevisionBody',
  2148. upodUserA,
  2149. options,
  2150. );
  2151. await expect(updatedPagePromise).rejects.toThrowError();
  2152. });
  2153. });
  2154. });