v5.page.test.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. import { addSeconds } from 'date-fns';
  2. import mongoose from 'mongoose';
  3. import { PageActionStage, PageActionType } from '../../../src/server/models/page-operation';
  4. import { getInstance } from '../setup-crowi';
  5. describe('Test page service methods', () => {
  6. let crowi;
  7. let Page;
  8. let Revision;
  9. let User;
  10. let UserGroup;
  11. let UserGroupRelation;
  12. let Tag;
  13. let PageTagRelation;
  14. let Bookmark;
  15. let Comment;
  16. let ShareLink;
  17. let PageRedirect;
  18. let PageOperation;
  19. let xssSpy;
  20. let rootPage;
  21. let dummyUser1;
  22. let dummyUser2;
  23. let globalGroupUser1;
  24. let globalGroupUser2;
  25. let globalGroupUser3;
  26. let globalGroupIsolate;
  27. let globalGroupA;
  28. let globalGroupB;
  29. let globalGroupC;
  30. beforeAll(async() => {
  31. crowi = await getInstance();
  32. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', { 'app:isV5Compatible': true });
  33. User = mongoose.model('User');
  34. UserGroup = mongoose.model('UserGroup');
  35. UserGroupRelation = mongoose.model('UserGroupRelation');
  36. Page = mongoose.model('Page');
  37. Revision = mongoose.model('Revision');
  38. Tag = mongoose.model('Tag');
  39. PageTagRelation = mongoose.model('PageTagRelation');
  40. Bookmark = mongoose.model('Bookmark');
  41. Comment = mongoose.model('Comment');
  42. ShareLink = mongoose.model('ShareLink');
  43. PageRedirect = mongoose.model('PageRedirect');
  44. UserGroup = mongoose.model('UserGroup');
  45. UserGroupRelation = mongoose.model('UserGroupRelation');
  46. PageOperation = mongoose.model('PageOperation');
  47. /*
  48. * Common
  49. */
  50. xssSpy = jest.spyOn(crowi.xss, 'process').mockImplementation(path => path);
  51. // ***********************************************************************************************************
  52. // * Do NOT change properties of globally used documents. Otherwise, it might cause some errors in other tests
  53. // ***********************************************************************************************************
  54. // users
  55. dummyUser1 = await User.findOne({ username: 'v5DummyUser1' });
  56. dummyUser2 = await User.findOne({ username: 'v5DummyUser2' });
  57. globalGroupUser1 = await User.findOne({ username: 'gGroupUser1' });
  58. globalGroupUser2 = await User.findOne({ username: 'gGroupUser2' });
  59. globalGroupUser3 = await User.findOne({ username: 'gGroupUser3' });
  60. // groups
  61. globalGroupIsolate = await UserGroup.findOne({ name: 'globalGroupIsolate' });
  62. globalGroupA = await UserGroup.findOne({ name: 'globalGroupA' });
  63. globalGroupB = await UserGroup.findOne({ name: 'globalGroupB' });
  64. globalGroupC = await UserGroup.findOne({ name: 'globalGroupC' });
  65. // page
  66. rootPage = await Page.findOne({ path: '/' });
  67. /**
  68. * pages
  69. */
  70. const pageId0 = new mongoose.Types.ObjectId();
  71. const pageId1 = new mongoose.Types.ObjectId();
  72. const pageId2 = new mongoose.Types.ObjectId();
  73. const pageId3 = new mongoose.Types.ObjectId();
  74. const pageId4 = new mongoose.Types.ObjectId();
  75. const pageId5 = new mongoose.Types.ObjectId();
  76. const pageId6 = new mongoose.Types.ObjectId();
  77. const pageId7 = new mongoose.Types.ObjectId();
  78. const pageId8 = new mongoose.Types.ObjectId();
  79. const pageId9 = new mongoose.Types.ObjectId();
  80. const pageId10 = new mongoose.Types.ObjectId();
  81. const pageId11 = new mongoose.Types.ObjectId();
  82. const pageId12 = new mongoose.Types.ObjectId();
  83. await Page.insertMany([
  84. {
  85. _id: pageId0,
  86. path: '/resume_rename_0',
  87. parent: rootPage._id,
  88. grant: Page.GRANT_PUBLIC,
  89. creator: dummyUser1,
  90. lastUpdateUser: dummyUser1._id,
  91. descendantCount: 0,
  92. isEmpty: false,
  93. },
  94. {
  95. _id: pageId1,
  96. path: '/resume_rename_0/resume_rename_1',
  97. parent: pageId0._id,
  98. grant: Page.GRANT_PUBLIC,
  99. creator: dummyUser1,
  100. lastUpdateUser: dummyUser1._id,
  101. descendantCount: 2,
  102. isEmpty: false,
  103. },
  104. {
  105. _id: pageId2,
  106. path: '/resume_rename_1/resume_rename_2',
  107. parent: pageId1,
  108. grant: Page.GRANT_PUBLIC,
  109. creator: dummyUser1,
  110. lastUpdateUser: dummyUser1._id,
  111. descendantCount: 1,
  112. isEmpty: false,
  113. },
  114. {
  115. _id: pageId3,
  116. path: '/resume_rename_1/resume_rename_2/resume_rename_3',
  117. parent: pageId2,
  118. grant: Page.GRANT_PUBLIC,
  119. creator: dummyUser1,
  120. lastUpdateUser: dummyUser1._id,
  121. descendantCount: 0,
  122. isEmpty: false,
  123. },
  124. {
  125. _id: pageId4,
  126. path: '/resume_rename_4',
  127. parent: rootPage._id,
  128. grant: Page.GRANT_PUBLIC,
  129. creator: dummyUser1,
  130. lastUpdateUser: dummyUser1._id,
  131. descendantCount: 0,
  132. isEmpty: false,
  133. },
  134. {
  135. _id: pageId5,
  136. path: '/resume_rename_4/resume_rename_5',
  137. parent: pageId0._id,
  138. grant: Page.GRANT_PUBLIC,
  139. creator: dummyUser1,
  140. lastUpdateUser: dummyUser1._id,
  141. descendantCount: 1,
  142. isEmpty: false,
  143. },
  144. {
  145. _id: pageId6,
  146. path: '/resume_rename_5/resume_rename_6',
  147. parent: pageId5,
  148. grant: Page.GRANT_PUBLIC,
  149. creator: dummyUser1,
  150. lastUpdateUser: dummyUser1._id,
  151. descendantCount: 0,
  152. isEmpty: false,
  153. },
  154. {
  155. _id: pageId7,
  156. path: '/resume_rename_7',
  157. parent: rootPage._id,
  158. grant: Page.GRANT_PUBLIC,
  159. creator: dummyUser1,
  160. lastUpdateUser: dummyUser1._id,
  161. descendantCount: 0,
  162. isEmpty: false,
  163. },
  164. {
  165. _id: pageId8,
  166. path: '/resume_rename_8',
  167. parent: rootPage._id,
  168. grant: Page.GRANT_PUBLIC,
  169. creator: dummyUser1,
  170. lastUpdateUser: dummyUser1._id,
  171. descendantCount: 0,
  172. isEmpty: false,
  173. },
  174. {
  175. _id: pageId9,
  176. path: '/resume_rename_8/resume_rename_9',
  177. parent: pageId8,
  178. grant: Page.GRANT_PUBLIC,
  179. creator: dummyUser1,
  180. lastUpdateUser: dummyUser1._id,
  181. descendantCount: 1,
  182. isEmpty: false,
  183. },
  184. {
  185. path: '/resume_rename_9/resume_rename_10',
  186. parent: pageId9,
  187. grant: Page.GRANT_PUBLIC,
  188. creator: dummyUser1,
  189. lastUpdateUser: dummyUser1._id,
  190. descendantCount: 0,
  191. isEmpty: false,
  192. },
  193. {
  194. _id: pageId10,
  195. path: '/resume_rename_11',
  196. parent: rootPage._id,
  197. grant: Page.GRANT_PUBLIC,
  198. creator: dummyUser1,
  199. lastUpdateUser: dummyUser1._id,
  200. descendantCount: 3,
  201. isEmpty: false,
  202. },
  203. {
  204. _id: pageId11,
  205. path: '/resume_rename_11/resume_rename_12',
  206. parent: pageId10,
  207. grant: Page.GRANT_PUBLIC,
  208. creator: dummyUser1,
  209. lastUpdateUser: dummyUser1._id,
  210. descendantCount: 2,
  211. isEmpty: false,
  212. },
  213. {
  214. _id: pageId12,
  215. path: '/resume_rename_11/resume_rename_12/resume_rename_13',
  216. parent: pageId11,
  217. grant: Page.GRANT_PUBLIC,
  218. creator: dummyUser1,
  219. lastUpdateUser: dummyUser1._id,
  220. descendantCount: 1,
  221. isEmpty: false,
  222. },
  223. {
  224. path: '/resume_rename_11/resume_rename_12/resume_rename_13/resume_rename_14',
  225. parent: pageId12,
  226. grant: Page.GRANT_PUBLIC,
  227. creator: dummyUser1,
  228. lastUpdateUser: dummyUser1._id,
  229. descendantCount: 0,
  230. isEmpty: false,
  231. },
  232. ]);
  233. /**
  234. * PageOperation
  235. */
  236. const pageOpId1 = new mongoose.Types.ObjectId();
  237. const pageOpId2 = new mongoose.Types.ObjectId();
  238. const pageOpId3 = new mongoose.Types.ObjectId();
  239. const pageOpId4 = new mongoose.Types.ObjectId();
  240. const pageOpId5 = new mongoose.Types.ObjectId();
  241. const pageOpRevisionId1 = new mongoose.Types.ObjectId();
  242. const pageOpRevisionId2 = new mongoose.Types.ObjectId();
  243. const pageOpRevisionId3 = new mongoose.Types.ObjectId();
  244. const pageOpRevisionId4 = new mongoose.Types.ObjectId();
  245. const pageOpRevisionId5 = new mongoose.Types.ObjectId();
  246. await PageOperation.insertMany([
  247. {
  248. _id: pageOpId1,
  249. actionType: 'Rename',
  250. actionStage: 'Sub',
  251. fromPath: '/resume_rename_1',
  252. toPath: '/resume_rename_0/resume_rename_1',
  253. page: {
  254. _id: pageId1,
  255. parent: rootPage._id,
  256. descendantCount: 2,
  257. isEmpty: false,
  258. path: '/resume_rename_1',
  259. revision: pageOpRevisionId1,
  260. status: 'published',
  261. grant: 1,
  262. grantedUsers: [],
  263. grantedGroup: null,
  264. creator: dummyUser1._id,
  265. lastUpdateUser: dummyUser1._id,
  266. },
  267. user: {
  268. _id: dummyUser1._id,
  269. },
  270. options: {
  271. createRedirectPage: false,
  272. updateMetadata: true,
  273. },
  274. unprocessableExpiryDate: null,
  275. },
  276. {
  277. _id: pageOpId2,
  278. actionType: 'Rename',
  279. actionStage: 'Sub',
  280. fromPath: '/resume_rename_5',
  281. toPath: '/resume_rename_4/resume_rename_5',
  282. page: {
  283. _id: pageId5,
  284. parent: rootPage._id,
  285. descendantCount: 2,
  286. isEmpty: false,
  287. path: '/resume_rename_5',
  288. revision: pageOpRevisionId2,
  289. status: 'published',
  290. grant: 1,
  291. grantedUsers: [],
  292. grantedGroup: null,
  293. creator: dummyUser1._id,
  294. lastUpdateUser: dummyUser1._id,
  295. },
  296. user: {
  297. _id: dummyUser1._id,
  298. },
  299. options: {
  300. createRedirectPage: false,
  301. updateMetadata: true,
  302. },
  303. unprocessableExpiryDate: new Date(),
  304. },
  305. {
  306. _id: pageOpId3,
  307. actionType: 'Rename',
  308. actionStage: 'Sub',
  309. fromPath: '/resume_rename_7',
  310. // toPath NOT exist
  311. page: {
  312. _id: pageId7,
  313. parent: rootPage._id,
  314. descendantCount: 2,
  315. isEmpty: false,
  316. path: '/resume_rename_7',
  317. revision: pageOpRevisionId3,
  318. status: 'published',
  319. grant: 1,
  320. grantedUsers: [],
  321. grantedGroup: null,
  322. creator: dummyUser1._id,
  323. lastUpdateUser: dummyUser1._id,
  324. },
  325. user: {
  326. _id: dummyUser1._id,
  327. },
  328. options: {
  329. createRedirectPage: false,
  330. updateMetadata: true,
  331. },
  332. unprocessableExpiryDate: new Date(),
  333. },
  334. {
  335. _id: pageOpId4,
  336. actionType: 'Rename',
  337. actionStage: 'Sub',
  338. fromPath: '/resume_rename_9',
  339. toPath: '/resume_rename_8/resume_rename_9',
  340. page: {
  341. _id: pageId9,
  342. parent: rootPage._id,
  343. descendantCount: 1,
  344. isEmpty: false,
  345. path: '/resume_rename_9',
  346. revision: pageOpRevisionId4,
  347. status: 'published',
  348. grant: Page.GRANT_PUBLIC,
  349. grantedUsers: [],
  350. grantedGroup: null,
  351. creator: dummyUser1._id,
  352. lastUpdateUser: dummyUser1._id,
  353. },
  354. user: {
  355. _id: dummyUser1._id,
  356. },
  357. options: {
  358. createRedirectPage: false,
  359. updateMetadata: true,
  360. },
  361. unprocessableExpiryDate: null,
  362. },
  363. {
  364. _id: pageOpId5,
  365. actionType: 'Rename',
  366. actionStage: 'Sub',
  367. fromPath: '/resume_rename_11/resume_rename_13',
  368. toPath: '/resume_rename_11/resume_rename_12/resume_rename_13',
  369. page: {
  370. _id: pageId12,
  371. parent: pageId10,
  372. descendantCount: 1,
  373. isEmpty: false,
  374. path: '/resume_rename_11/resume_rename_13',
  375. revision: pageOpRevisionId5,
  376. status: 'published',
  377. grant: Page.GRANT_PUBLIC,
  378. grantedUsers: [],
  379. grantedGroup: null,
  380. creator: dummyUser1._id,
  381. lastUpdateUser: dummyUser1._id,
  382. },
  383. user: {
  384. _id: dummyUser1._id,
  385. },
  386. options: {
  387. createRedirectPage: false,
  388. updateMetadata: true,
  389. },
  390. unprocessableExpiryDate: new Date(),
  391. },
  392. ]);
  393. });
  394. describe('restart renameOperation', () => {
  395. const resumeRenameSubOperation = async(page) => {
  396. const mockedRenameSubOperation = jest.spyOn(crowi.pageService, 'renameSubOperation').mockReturnValue(null);
  397. await crowi.pageService.resumeRenameSubOperation(page);
  398. const argsForRenameSubOperation = mockedRenameSubOperation.mock.calls[0];
  399. mockedRenameSubOperation.mockRestore();
  400. await crowi.pageService.renameSubOperation(...argsForRenameSubOperation);
  401. };
  402. test('it should successfully restart rename operation', async() => {
  403. // paths before renaming
  404. const _path0 = '/resume_rename_0'; // out of renaming scope
  405. const _path1 = '/resume_rename_0/resume_rename_1'; // renamed already
  406. const _path2 = '/resume_rename_1/resume_rename_2'; // not renamed yet
  407. const _path3 = '/resume_rename_1/resume_rename_2/resume_rename_3'; // not renamed yet
  408. // paths after renaming
  409. const path0 = '/resume_rename_0';
  410. const path1 = '/resume_rename_0/resume_rename_1';
  411. const path2 = '/resume_rename_0/resume_rename_1/resume_rename_2';
  412. const path3 = '/resume_rename_0/resume_rename_1/resume_rename_2/resume_rename_3';
  413. // page
  414. const _page0 = await Page.findOne({ path: _path0 });
  415. const _page1 = await Page.findOne({ path: _path1 });
  416. const _page2 = await Page.findOne({ path: _path2 });
  417. const _page3 = await Page.findOne({ path: _path3 });
  418. expect(_page0).toBeTruthy();
  419. expect(_page1).toBeTruthy();
  420. expect(_page2).toBeTruthy();
  421. expect(_page3).toBeTruthy();
  422. // page operation
  423. const _pageOperation = await PageOperation.findOne({ 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub });
  424. expect(_pageOperation).toBeTruthy();
  425. // rename
  426. await resumeRenameSubOperation(_page1);
  427. // page
  428. const page0 = await Page.findById(_page0._id);
  429. const page1 = await Page.findById(_page1._id);
  430. const page2 = await Page.findById(_page2._id);
  431. const page3 = await Page.findById(_page3._id);
  432. expect(page0).toBeTruthy();
  433. expect(page1).toBeTruthy();
  434. expect(page2).toBeTruthy();
  435. expect(page3).toBeTruthy();
  436. // check paths after renaming
  437. expect(page0.path).toBe(path0);
  438. expect(page1.path).toBe(path1);
  439. expect(page2.path).toBe(path2);
  440. expect(page3.path).toBe(path3);
  441. // page operation
  442. const pageOperation = await PageOperation.findById(_pageOperation._id);
  443. expect(pageOperation).toBeNull(); // should not exist
  444. // others
  445. expect(page1.parent).toStrictEqual(page0._id);
  446. expect(page2.parent).toStrictEqual(page1._id);
  447. expect(page3.parent).toStrictEqual(page2._id);
  448. expect(page0.descendantCount).toBe(3);
  449. expect(page1.descendantCount).toBe(2);
  450. expect(page2.descendantCount).toBe(1);
  451. expect(page3.descendantCount).toBe(0);
  452. });
  453. test('it should successfully restart rename operation when unprocessableExpiryDate is null', async() => {
  454. // paths before renaming
  455. const _path0 = '/resume_rename_8'; // out of renaming scope
  456. const _path1 = '/resume_rename_8/resume_rename_9'; // renamed already
  457. const _path2 = '/resume_rename_9/resume_rename_10'; // not renamed yet
  458. // paths after renaming
  459. const path0 = '/resume_rename_8';
  460. const path1 = '/resume_rename_8/resume_rename_9';
  461. const path2 = '/resume_rename_8/resume_rename_9/resume_rename_10';
  462. // page
  463. const _page0 = await Page.findOne({ path: _path0 });
  464. const _page1 = await Page.findOne({ path: _path1 });
  465. const _page2 = await Page.findOne({ path: _path2 });
  466. expect(_page0).toBeTruthy();
  467. expect(_page1).toBeTruthy();
  468. expect(_page2).toBeTruthy();
  469. // page operation
  470. const _pageOperation = await PageOperation.findOne({ 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub });
  471. expect(_pageOperation).toBeTruthy();
  472. // rename
  473. await resumeRenameSubOperation(_page1);
  474. // page
  475. const page0 = await Page.findById(_page0._id);
  476. const page1 = await Page.findById(_page1._id);
  477. const page2 = await Page.findById(_page2._id);
  478. expect(page0).toBeTruthy();
  479. expect(page1).toBeTruthy();
  480. expect(page2).toBeTruthy();
  481. // check paths after renaming
  482. expect(page0.path).toBe(path0);
  483. expect(page1.path).toBe(path1);
  484. expect(page2.path).toBe(path2);
  485. // page operation
  486. const pageOperation = await PageOperation.findById(_pageOperation._id);
  487. expect(pageOperation).toBeNull(); // should not exist
  488. // others
  489. expect(page1.parent).toStrictEqual(page0._id);
  490. expect(page2.parent).toStrictEqual(page1._id);
  491. expect(page0.descendantCount).toBe(2);
  492. expect(page1.descendantCount).toBe(1);
  493. expect(page2.descendantCount).toBe(0);
  494. });
  495. test('it should fail and throw error if PageOperation is not found', async() => {
  496. // create dummy page operation data not stored in DB
  497. const notExistPageOp = {
  498. _id: new mongoose.Types.ObjectId(),
  499. actionType: 'Rename',
  500. actionStage: 'Sub',
  501. fromPath: '/FROM_NOT_EXIST',
  502. toPath: 'TO_NOT_EXIST',
  503. page: {
  504. _id: new mongoose.Types.ObjectId(),
  505. parent: rootPage._id,
  506. descendantCount: 2,
  507. isEmpty: false,
  508. path: '/NOT_EXIST_PAGE',
  509. revision: new mongoose.Types.ObjectId(),
  510. status: 'published',
  511. grant: 1,
  512. grantedUsers: [],
  513. grantedGroup: null,
  514. creator: dummyUser1._id,
  515. lastUpdateUser: dummyUser1._id,
  516. },
  517. user: {
  518. _id: dummyUser1._id,
  519. },
  520. options: {
  521. createRedirectPage: false,
  522. updateMetadata: false,
  523. },
  524. unprocessableExpiryDate: new Date(),
  525. };
  526. await expect(resumeRenameSubOperation(notExistPageOp))
  527. .rejects.toThrow(new Error('There is nothing to be processed right now'));
  528. });
  529. test('it should fail and throw error if the current time is behind unprocessableExpiryDate', async() => {
  530. // path before renaming
  531. const _path0 = '/resume_rename_4'; // out of renaming scope
  532. const _path1 = '/resume_rename_4/resume_rename_5'; // renamed already
  533. const _path2 = '/resume_rename_5/resume_rename_6'; // not renamed yet
  534. // page
  535. const _page0 = await Page.findOne({ path: _path0 });
  536. const _page1 = await Page.findOne({ path: _path1 });
  537. const _page2 = await Page.findOne({ path: _path2 });
  538. expect(_page0).toBeTruthy();
  539. expect(_page1).toBeTruthy();
  540. expect(_page2).toBeTruthy();
  541. // page operation
  542. const pageOperation = await PageOperation.findOne({ 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub });
  543. expect(pageOperation).toBeTruthy();
  544. // Make `unprocessableExpiryDate` 15 seconds ahead of current time.
  545. // The number 15 seconds has no meaning other than placing time in the furue.
  546. await PageOperation.findByIdAndUpdate(pageOperation._id, { unprocessableExpiryDate: addSeconds(new Date(), 15) });
  547. await expect(resumeRenameSubOperation(_page1)).rejects.toThrow(new Error('This page operation is currently being processed'));
  548. // cleanup
  549. await PageOperation.findByIdAndDelete(pageOperation._id);
  550. });
  551. test('Missing property(toPath) for PageOperation should throw error', async() => {
  552. // page
  553. const _path1 = '/resume_rename_7';
  554. const _page1 = await Page.findOne({ path: _path1 });
  555. expect(_page1).toBeTruthy();
  556. // page operation
  557. const pageOperation = await PageOperation.findOne({ 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub });
  558. expect(pageOperation).toBeTruthy();
  559. const promise = resumeRenameSubOperation(_page1);
  560. await expect(promise).rejects.toThrow(new Error(`Property toPath is missing which is needed to resume page operation(${pageOperation._id})`));
  561. // cleanup
  562. await PageOperation.findByIdAndDelete(pageOperation._id);
  563. });
  564. test(`it should succeed but 2 extra descendantCount should be added
  565. if resumeRenameOperation gets executed after updating descendantCount of ancestor in renameSubOperation is finished`, async() => {
  566. // paths before renaming
  567. const _path0 = '/resume_rename_11'; // out of renaming scope
  568. const _path1 = '/resume_rename_11/resume_rename_12'; // out of renaming scope
  569. const _path2 = '/resume_rename_11/resume_rename_12/resume_rename_13'; // renamed already
  570. const _path3 = '/resume_rename_11/resume_rename_12/resume_rename_13/resume_rename_14'; // renamed already
  571. // paths after renaming
  572. const path0 = '/resume_rename_11';
  573. const path1 = '/resume_rename_11/resume_rename_12';
  574. const path2 = '/resume_rename_11/resume_rename_12/resume_rename_13';
  575. const path3 = '/resume_rename_11/resume_rename_12/resume_rename_13/resume_rename_14';
  576. // page
  577. const _page0 = await Page.findOne({ path: _path0 });
  578. const _page1 = await Page.findOne({ path: _path1 });
  579. const _page2 = await Page.findOne({ path: _path2 });
  580. const _page3 = await Page.findOne({ path: _path3 });
  581. expect(_page0).toBeTruthy();
  582. expect(_page1).toBeTruthy();
  583. expect(_page2).toBeTruthy();
  584. expect(_page3).toBeTruthy();
  585. // page operation
  586. const _pageOperation = await PageOperation.findOne({ 'page._id': _page2._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub });
  587. expect(_pageOperation).toBeTruthy();
  588. // rename
  589. await resumeRenameSubOperation(_page2);
  590. // page
  591. const page0 = await Page.findById(_page0._id);
  592. const page1 = await Page.findById(_page1._id);
  593. const page2 = await Page.findById(_page2._id);
  594. const page3 = await Page.findById(_page3._id);
  595. expect(page0).toBeTruthy();
  596. expect(page1).toBeTruthy();
  597. expect(page2).toBeTruthy();
  598. expect(page3).toBeTruthy();
  599. expect(page0.path).toBe(path0);
  600. expect(page1.path).toBe(path1);
  601. expect(page2.path).toBe(path2);
  602. expect(page3.path).toBe(path3);
  603. // page operation
  604. const pageOperation = await PageOperation.findById(_pageOperation._id);
  605. expect(pageOperation).toBeNull(); // should not exist
  606. // others
  607. expect(page0.parent).toStrictEqual(rootPage._id);
  608. expect(page1.parent).toStrictEqual(page0._id);
  609. expect(page2.parent).toStrictEqual(page1._id);
  610. expect(page3.parent).toStrictEqual(page2._id);
  611. // 2 extra descendants should be added
  612. expect(page0.descendantCount).toBe(3); // originally 3
  613. expect(page1.descendantCount).toBe(4); // originally 2
  614. expect(page2.descendantCount).toBe(1);
  615. expect(page3.descendantCount).toBe(0);
  616. });
  617. });
  618. });