v5.page.test.ts 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  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. let pageOpId1;
  31. let pageOpId2;
  32. let pageOpId3;
  33. let pageOpId4;
  34. let pageOpId5;
  35. let pageOpId6;
  36. beforeAll(async() => {
  37. crowi = await getInstance();
  38. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', { 'app:isV5Compatible': true });
  39. User = mongoose.model('User');
  40. UserGroup = mongoose.model('UserGroup');
  41. UserGroupRelation = mongoose.model('UserGroupRelation');
  42. Page = mongoose.model('Page');
  43. Revision = mongoose.model('Revision');
  44. Tag = mongoose.model('Tag');
  45. PageTagRelation = mongoose.model('PageTagRelation');
  46. Bookmark = mongoose.model('Bookmark');
  47. Comment = mongoose.model('Comment');
  48. ShareLink = mongoose.model('ShareLink');
  49. PageRedirect = mongoose.model('PageRedirect');
  50. UserGroup = mongoose.model('UserGroup');
  51. UserGroupRelation = mongoose.model('UserGroupRelation');
  52. PageOperation = mongoose.model('PageOperation');
  53. /*
  54. * Common
  55. */
  56. xssSpy = jest.spyOn(crowi.xss, 'process').mockImplementation(path => path);
  57. // ***********************************************************************************************************
  58. // * Do NOT change properties of globally used documents. Otherwise, it might cause some errors in other tests
  59. // ***********************************************************************************************************
  60. // users
  61. dummyUser1 = await User.findOne({ username: 'v5DummyUser1' });
  62. dummyUser2 = await User.findOne({ username: 'v5DummyUser2' });
  63. globalGroupUser1 = await User.findOne({ username: 'gGroupUser1' });
  64. globalGroupUser2 = await User.findOne({ username: 'gGroupUser2' });
  65. globalGroupUser3 = await User.findOne({ username: 'gGroupUser3' });
  66. // groups
  67. globalGroupIsolate = await UserGroup.findOne({ name: 'globalGroupIsolate' });
  68. globalGroupA = await UserGroup.findOne({ name: 'globalGroupA' });
  69. globalGroupB = await UserGroup.findOne({ name: 'globalGroupB' });
  70. globalGroupC = await UserGroup.findOne({ name: 'globalGroupC' });
  71. // page
  72. rootPage = await Page.findOne({ path: '/' });
  73. /**
  74. * pages
  75. */
  76. const pageId0 = new mongoose.Types.ObjectId();
  77. const pageId1 = new mongoose.Types.ObjectId();
  78. const pageId2 = new mongoose.Types.ObjectId();
  79. const pageId3 = new mongoose.Types.ObjectId();
  80. const pageId4 = new mongoose.Types.ObjectId();
  81. const pageId5 = new mongoose.Types.ObjectId();
  82. const pageId6 = new mongoose.Types.ObjectId();
  83. const pageId7 = new mongoose.Types.ObjectId();
  84. const pageId8 = new mongoose.Types.ObjectId();
  85. const pageId9 = new mongoose.Types.ObjectId();
  86. const pageId10 = new mongoose.Types.ObjectId();
  87. const pageId11 = new mongoose.Types.ObjectId();
  88. const pageId12 = new mongoose.Types.ObjectId();
  89. const pageId13 = new mongoose.Types.ObjectId();
  90. const pageId14 = new mongoose.Types.ObjectId();
  91. const pageId15 = new mongoose.Types.ObjectId();
  92. const pageId16 = new mongoose.Types.ObjectId();
  93. const pageId17 = new mongoose.Types.ObjectId();
  94. await Page.insertMany([
  95. {
  96. _id: pageId0,
  97. path: '/resume_rename_0',
  98. parent: rootPage._id,
  99. grant: Page.GRANT_PUBLIC,
  100. creator: dummyUser1,
  101. lastUpdateUser: dummyUser1._id,
  102. descendantCount: 1,
  103. isEmpty: false,
  104. },
  105. {
  106. _id: pageId1,
  107. path: '/resume_rename_0/resume_rename_1',
  108. parent: pageId0,
  109. grant: Page.GRANT_PUBLIC,
  110. creator: dummyUser1,
  111. lastUpdateUser: dummyUser1._id,
  112. descendantCount: 2,
  113. isEmpty: false,
  114. },
  115. {
  116. _id: pageId2,
  117. path: '/resume_rename_1/resume_rename_2',
  118. parent: pageId1,
  119. grant: Page.GRANT_PUBLIC,
  120. creator: dummyUser1,
  121. lastUpdateUser: dummyUser1._id,
  122. descendantCount: 1,
  123. isEmpty: false,
  124. },
  125. {
  126. _id: pageId3,
  127. path: '/resume_rename_1/resume_rename_2/resume_rename_3',
  128. parent: pageId2,
  129. grant: Page.GRANT_PUBLIC,
  130. creator: dummyUser1,
  131. lastUpdateUser: dummyUser1._id,
  132. descendantCount: 0,
  133. isEmpty: false,
  134. },
  135. {
  136. _id: pageId4,
  137. path: '/resume_rename_4',
  138. parent: rootPage._id,
  139. grant: Page.GRANT_PUBLIC,
  140. creator: dummyUser1,
  141. lastUpdateUser: dummyUser1._id,
  142. descendantCount: 0,
  143. isEmpty: false,
  144. },
  145. {
  146. _id: pageId5,
  147. path: '/resume_rename_4/resume_rename_5',
  148. parent: pageId0,
  149. grant: Page.GRANT_PUBLIC,
  150. creator: dummyUser1,
  151. lastUpdateUser: dummyUser1._id,
  152. descendantCount: 1,
  153. isEmpty: false,
  154. },
  155. {
  156. _id: pageId6,
  157. path: '/resume_rename_5/resume_rename_6',
  158. parent: pageId5,
  159. grant: Page.GRANT_PUBLIC,
  160. creator: dummyUser1,
  161. lastUpdateUser: dummyUser1._id,
  162. descendantCount: 0,
  163. isEmpty: false,
  164. },
  165. {
  166. _id: pageId7,
  167. path: '/resume_rename_7',
  168. parent: rootPage._id,
  169. grant: Page.GRANT_PUBLIC,
  170. creator: dummyUser1,
  171. lastUpdateUser: dummyUser1._id,
  172. descendantCount: 0,
  173. isEmpty: false,
  174. },
  175. {
  176. _id: pageId8,
  177. path: '/resume_rename_8',
  178. parent: rootPage._id,
  179. grant: Page.GRANT_PUBLIC,
  180. creator: dummyUser1,
  181. lastUpdateUser: dummyUser1._id,
  182. descendantCount: 1,
  183. isEmpty: false,
  184. },
  185. {
  186. _id: pageId9,
  187. path: '/resume_rename_8/resume_rename_9',
  188. parent: pageId8,
  189. grant: Page.GRANT_PUBLIC,
  190. creator: dummyUser1,
  191. lastUpdateUser: dummyUser1._id,
  192. descendantCount: 1,
  193. isEmpty: false,
  194. },
  195. {
  196. path: '/resume_rename_9/resume_rename_10',
  197. parent: pageId9,
  198. grant: Page.GRANT_PUBLIC,
  199. creator: dummyUser1,
  200. lastUpdateUser: dummyUser1._id,
  201. descendantCount: 0,
  202. isEmpty: false,
  203. },
  204. {
  205. _id: pageId10,
  206. path: '/resume_rename_11',
  207. parent: rootPage._id,
  208. grant: Page.GRANT_PUBLIC,
  209. creator: dummyUser1,
  210. lastUpdateUser: dummyUser1._id,
  211. descendantCount: 3,
  212. isEmpty: false,
  213. },
  214. {
  215. _id: pageId11,
  216. path: '/resume_rename_11/resume_rename_12',
  217. parent: pageId10,
  218. grant: Page.GRANT_PUBLIC,
  219. creator: dummyUser1,
  220. lastUpdateUser: dummyUser1._id,
  221. descendantCount: 2,
  222. isEmpty: false,
  223. },
  224. {
  225. _id: pageId12,
  226. path: '/resume_rename_11/resume_rename_12/resume_rename_13',
  227. parent: pageId11,
  228. grant: Page.GRANT_PUBLIC,
  229. creator: dummyUser1,
  230. lastUpdateUser: dummyUser1._id,
  231. descendantCount: 1,
  232. isEmpty: false,
  233. },
  234. {
  235. path: '/resume_rename_11/resume_rename_12/resume_rename_13/resume_rename_14',
  236. parent: pageId12,
  237. grant: Page.GRANT_PUBLIC,
  238. creator: dummyUser1,
  239. lastUpdateUser: dummyUser1._id,
  240. descendantCount: 0,
  241. isEmpty: false,
  242. },
  243. {
  244. _id: pageId13,
  245. path: '/resume_rename_15',
  246. parent: rootPage._id,
  247. grant: Page.GRANT_PUBLIC,
  248. creator: dummyUser1,
  249. lastUpdateUser: dummyUser1._id,
  250. descendantCount: 2,
  251. isEmpty: false,
  252. },
  253. {
  254. _id: pageId14,
  255. path: '/resume_rename_15/resume_rename_16',
  256. parent: pageId13,
  257. grant: Page.GRANT_PUBLIC,
  258. creator: dummyUser1,
  259. lastUpdateUser: dummyUser1._id,
  260. descendantCount: 0,
  261. isEmpty: false,
  262. },
  263. {
  264. _id: pageId15,
  265. path: '/resume_rename_15/resume_rename_17',
  266. parent: pageId13,
  267. grant: Page.GRANT_PUBLIC,
  268. creator: dummyUser1,
  269. lastUpdateUser: dummyUser1._id,
  270. descendantCount: 1,
  271. isEmpty: false,
  272. },
  273. {
  274. _id: pageId16,
  275. path: '/resume_rename_15/resume_rename_17/resume_rename_18',
  276. parent: pageId15,
  277. grant: Page.GRANT_PUBLIC,
  278. creator: dummyUser1,
  279. lastUpdateUser: dummyUser1._id,
  280. descendantCount: 1,
  281. isEmpty: false,
  282. },
  283. {
  284. _id: pageId17,
  285. path: '/resume_rename_15/resume_rename_17/resume_rename_18/resume_rename_19',
  286. parent: pageId16,
  287. grant: Page.GRANT_PUBLIC,
  288. creator: dummyUser1,
  289. lastUpdateUser: dummyUser1._id,
  290. descendantCount: 0,
  291. isEmpty: false,
  292. },
  293. ]);
  294. /**
  295. * PageOperation
  296. */
  297. pageOpId1 = new mongoose.Types.ObjectId();
  298. pageOpId2 = new mongoose.Types.ObjectId();
  299. pageOpId3 = new mongoose.Types.ObjectId();
  300. pageOpId4 = new mongoose.Types.ObjectId();
  301. pageOpId5 = new mongoose.Types.ObjectId();
  302. pageOpId6 = new mongoose.Types.ObjectId();
  303. const pageOpRevisionId1 = new mongoose.Types.ObjectId();
  304. const pageOpRevisionId2 = new mongoose.Types.ObjectId();
  305. const pageOpRevisionId3 = new mongoose.Types.ObjectId();
  306. const pageOpRevisionId4 = new mongoose.Types.ObjectId();
  307. const pageOpRevisionId5 = new mongoose.Types.ObjectId();
  308. const pageOpRevisionId6 = new mongoose.Types.ObjectId();
  309. await PageOperation.insertMany([
  310. {
  311. _id: pageOpId1,
  312. actionType: 'Rename',
  313. actionStage: 'Sub',
  314. fromPath: '/resume_rename_1',
  315. toPath: '/resume_rename_0/resume_rename_1',
  316. page: {
  317. _id: pageId1,
  318. parent: rootPage._id,
  319. descendantCount: 2,
  320. isEmpty: false,
  321. path: '/resume_rename_1',
  322. revision: pageOpRevisionId1,
  323. status: 'published',
  324. grant: 1,
  325. grantedUsers: [],
  326. grantedGroup: null,
  327. creator: dummyUser1._id,
  328. lastUpdateUser: dummyUser1._id,
  329. },
  330. user: {
  331. _id: dummyUser1._id,
  332. },
  333. options: {
  334. createRedirectPage: false,
  335. updateMetadata: true,
  336. },
  337. unprocessableExpiryDate: null,
  338. },
  339. {
  340. _id: pageOpId2,
  341. actionType: 'Rename',
  342. actionStage: 'Sub',
  343. fromPath: '/resume_rename_5',
  344. toPath: '/resume_rename_4/resume_rename_5',
  345. page: {
  346. _id: pageId5,
  347. parent: rootPage._id,
  348. descendantCount: 2,
  349. isEmpty: false,
  350. path: '/resume_rename_5',
  351. revision: pageOpRevisionId2,
  352. status: 'published',
  353. grant: 1,
  354. grantedUsers: [],
  355. grantedGroup: null,
  356. creator: dummyUser1._id,
  357. lastUpdateUser: dummyUser1._id,
  358. },
  359. user: {
  360. _id: dummyUser1._id,
  361. },
  362. options: {
  363. createRedirectPage: false,
  364. updateMetadata: true,
  365. },
  366. unprocessableExpiryDate: new Date(),
  367. },
  368. {
  369. _id: pageOpId3,
  370. actionType: 'Rename',
  371. actionStage: 'Sub',
  372. fromPath: '/resume_rename_7',
  373. // toPath NOT exist
  374. page: {
  375. _id: pageId7,
  376. parent: rootPage._id,
  377. descendantCount: 2,
  378. isEmpty: false,
  379. path: '/resume_rename_7',
  380. revision: pageOpRevisionId3,
  381. status: 'published',
  382. grant: 1,
  383. grantedUsers: [],
  384. grantedGroup: null,
  385. creator: dummyUser1._id,
  386. lastUpdateUser: dummyUser1._id,
  387. },
  388. user: {
  389. _id: dummyUser1._id,
  390. },
  391. options: {
  392. createRedirectPage: false,
  393. updateMetadata: true,
  394. },
  395. unprocessableExpiryDate: new Date(),
  396. },
  397. {
  398. _id: pageOpId4,
  399. actionType: 'Rename',
  400. actionStage: 'Sub',
  401. fromPath: '/resume_rename_9',
  402. toPath: '/resume_rename_8/resume_rename_9',
  403. page: {
  404. _id: pageId9,
  405. parent: rootPage._id,
  406. descendantCount: 1,
  407. isEmpty: false,
  408. path: '/resume_rename_9',
  409. revision: pageOpRevisionId4,
  410. status: 'published',
  411. grant: Page.GRANT_PUBLIC,
  412. grantedUsers: [],
  413. grantedGroup: null,
  414. creator: dummyUser1._id,
  415. lastUpdateUser: dummyUser1._id,
  416. },
  417. user: {
  418. _id: dummyUser1._id,
  419. },
  420. options: {
  421. createRedirectPage: false,
  422. updateMetadata: true,
  423. },
  424. unprocessableExpiryDate: null,
  425. },
  426. {
  427. _id: pageOpId5,
  428. actionType: 'Rename',
  429. actionStage: 'Sub',
  430. fromPath: '/resume_rename_11/resume_rename_13',
  431. toPath: '/resume_rename_11/resume_rename_12/resume_rename_13',
  432. page: {
  433. _id: pageId12,
  434. parent: pageId10,
  435. descendantCount: 1,
  436. isEmpty: false,
  437. path: '/resume_rename_11/resume_rename_13',
  438. revision: pageOpRevisionId5,
  439. status: 'published',
  440. grant: Page.GRANT_PUBLIC,
  441. grantedUsers: [],
  442. grantedGroup: null,
  443. creator: dummyUser1._id,
  444. lastUpdateUser: dummyUser1._id,
  445. },
  446. user: {
  447. _id: dummyUser1._id,
  448. },
  449. options: {
  450. createRedirectPage: false,
  451. updateMetadata: true,
  452. },
  453. unprocessableExpiryDate: new Date(),
  454. },
  455. {
  456. _id: pageOpId6,
  457. actionType: 'Rename',
  458. actionStage: 'Sub',
  459. fromPath: '/resume_rename_15/resume_rename_16/resume_rename_18',
  460. toPath: '/resume_rename_15/resume_rename_17/resume_rename_18',
  461. page: {
  462. _id: pageId16,
  463. parent: pageId14,
  464. descendantCount: 1,
  465. isEmpty: false,
  466. path: '/resume_rename_15/resume_rename_16/resume_rename_18',
  467. revision: pageOpRevisionId6,
  468. status: 'published',
  469. grant: Page.GRANT_PUBLIC,
  470. grantedUsers: [],
  471. grantedGroup: null,
  472. creator: dummyUser1._id,
  473. lastUpdateUser: dummyUser1._id,
  474. },
  475. user: {
  476. _id: dummyUser1._id,
  477. },
  478. options: {
  479. createRedirectPage: false,
  480. updateMetadata: true,
  481. },
  482. unprocessableExpiryDate: new Date(),
  483. },
  484. ]);
  485. });
  486. describe('restart renameOperation', () => {
  487. const resumeRenameSubOperation = async(page) => {
  488. const mockedRenameSubOperation = jest.spyOn(crowi.pageService, 'renameSubOperation').mockReturnValue(null);
  489. await crowi.pageService.resumeRenameSubOperation(page);
  490. const argsForRenameSubOperation = mockedRenameSubOperation.mock.calls[0];
  491. mockedRenameSubOperation.mockRestore();
  492. await crowi.pageService.renameSubOperation(...argsForRenameSubOperation);
  493. };
  494. test('it should successfully restart rename operation', async() => {
  495. // paths before renaming
  496. const _path0 = '/resume_rename_0'; // out of renaming scope
  497. const _path1 = '/resume_rename_0/resume_rename_1'; // renamed already
  498. const _path2 = '/resume_rename_1/resume_rename_2'; // not renamed yet
  499. const _path3 = '/resume_rename_1/resume_rename_2/resume_rename_3'; // not renamed yet
  500. // paths after renaming
  501. const path0 = '/resume_rename_0';
  502. const path1 = '/resume_rename_0/resume_rename_1';
  503. const path2 = '/resume_rename_0/resume_rename_1/resume_rename_2';
  504. const path3 = '/resume_rename_0/resume_rename_1/resume_rename_2/resume_rename_3';
  505. // page
  506. const _page0 = await Page.findOne({ path: _path0 });
  507. const _page1 = await Page.findOne({ path: _path1 });
  508. const _page2 = await Page.findOne({ path: _path2 });
  509. const _page3 = await Page.findOne({ path: _path3 });
  510. expect(_page0).toBeTruthy();
  511. expect(_page1).toBeTruthy();
  512. expect(_page2).toBeTruthy();
  513. expect(_page3).toBeTruthy();
  514. expect(_page0.descendantCount).toBe(1);
  515. expect(_page1.descendantCount).toBe(2);
  516. expect(_page2.descendantCount).toBe(1);
  517. expect(_page3.descendantCount).toBe(0);
  518. // page operation
  519. const fromPath = '/resume_rename_1';
  520. const toPath = '/resume_rename_0/resume_rename_1';
  521. const _pageOperation = await PageOperation.findOne({
  522. _id: pageOpId1, fromPath, toPath, 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub,
  523. });
  524. expect(_pageOperation).toBeTruthy();
  525. // rename
  526. await resumeRenameSubOperation(_page1);
  527. // page
  528. const page0 = await Page.findById(_page0._id);
  529. const page1 = await Page.findById(_page1._id);
  530. const page2 = await Page.findById(_page2._id);
  531. const page3 = await Page.findById(_page3._id);
  532. expect(page0).toBeTruthy();
  533. expect(page1).toBeTruthy();
  534. expect(page2).toBeTruthy();
  535. expect(page3).toBeTruthy();
  536. // check paths after renaming
  537. expect(page0.path).toBe(path0);
  538. expect(page1.path).toBe(path1);
  539. expect(page2.path).toBe(path2);
  540. expect(page3.path).toBe(path3);
  541. // page operation
  542. const pageOperation = await PageOperation.findById(_pageOperation._id);
  543. expect(pageOperation).toBeNull(); // should not exist
  544. expect(page0.descendantCount).toBe(3);
  545. expect(page1.descendantCount).toBe(2);
  546. expect(page2.descendantCount).toBe(1);
  547. expect(page3.descendantCount).toBe(0);
  548. });
  549. test('it should successfully restart rename operation when unprocessableExpiryDate is null', async() => {
  550. // paths before renaming
  551. const _path0 = '/resume_rename_8'; // out of renaming scope
  552. const _path1 = '/resume_rename_8/resume_rename_9'; // renamed already
  553. const _path2 = '/resume_rename_9/resume_rename_10'; // not renamed yet
  554. // paths after renaming
  555. const path0 = '/resume_rename_8';
  556. const path1 = '/resume_rename_8/resume_rename_9';
  557. const path2 = '/resume_rename_8/resume_rename_9/resume_rename_10';
  558. // page
  559. const _page0 = await Page.findOne({ path: _path0 });
  560. const _page1 = await Page.findOne({ path: _path1 });
  561. const _page2 = await Page.findOne({ path: _path2 });
  562. expect(_page0).toBeTruthy();
  563. expect(_page1).toBeTruthy();
  564. expect(_page2).toBeTruthy();
  565. expect(_page0.descendantCount).toBe(1);
  566. expect(_page1.descendantCount).toBe(1);
  567. expect(_page2.descendantCount).toBe(0);
  568. // page operation
  569. const fromPath = '/resume_rename_9';
  570. const toPath = '/resume_rename_8/resume_rename_9';
  571. const _pageOperation = await PageOperation.findOne({
  572. _id: pageOpId4, fromPath, toPath, 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub,
  573. });
  574. expect(_pageOperation).toBeTruthy();
  575. // rename
  576. await resumeRenameSubOperation(_page1);
  577. // page
  578. const page0 = await Page.findById(_page0._id);
  579. const page1 = await Page.findById(_page1._id);
  580. const page2 = await Page.findById(_page2._id);
  581. expect(page0).toBeTruthy();
  582. expect(page1).toBeTruthy();
  583. expect(page2).toBeTruthy();
  584. // check paths after renaming
  585. expect(page0.path).toBe(path0);
  586. expect(page1.path).toBe(path1);
  587. expect(page2.path).toBe(path2);
  588. // page operation
  589. const pageOperation = await PageOperation.findById(_pageOperation._id);
  590. expect(pageOperation).toBeNull(); // should not exist
  591. // others
  592. expect(page1.parent).toStrictEqual(page0._id);
  593. expect(page2.parent).toStrictEqual(page1._id);
  594. expect(page0.descendantCount).toBe(2);
  595. expect(page1.descendantCount).toBe(1);
  596. expect(page2.descendantCount).toBe(0);
  597. });
  598. test('it should fail and throw error if PageOperation is not found', async() => {
  599. // create dummy page operation data not stored in DB
  600. const notExistPageOp = {
  601. _id: new mongoose.Types.ObjectId(),
  602. actionType: 'Rename',
  603. actionStage: 'Sub',
  604. fromPath: '/FROM_NOT_EXIST',
  605. toPath: 'TO_NOT_EXIST',
  606. page: {
  607. _id: new mongoose.Types.ObjectId(),
  608. parent: rootPage._id,
  609. descendantCount: 2,
  610. isEmpty: false,
  611. path: '/NOT_EXIST_PAGE',
  612. revision: new mongoose.Types.ObjectId(),
  613. status: 'published',
  614. grant: 1,
  615. grantedUsers: [],
  616. grantedGroup: null,
  617. creator: dummyUser1._id,
  618. lastUpdateUser: dummyUser1._id,
  619. },
  620. user: {
  621. _id: dummyUser1._id,
  622. },
  623. options: {
  624. createRedirectPage: false,
  625. updateMetadata: false,
  626. },
  627. unprocessableExpiryDate: new Date(),
  628. };
  629. await expect(resumeRenameSubOperation(notExistPageOp))
  630. .rejects.toThrow(new Error('There is nothing to be processed right now'));
  631. });
  632. test('it should fail and throw error if the current time is behind unprocessableExpiryDate', async() => {
  633. // path before renaming
  634. const _path0 = '/resume_rename_4'; // out of renaming scope
  635. const _path1 = '/resume_rename_4/resume_rename_5'; // renamed already
  636. const _path2 = '/resume_rename_5/resume_rename_6'; // not renamed yet
  637. // page
  638. const _page0 = await Page.findOne({ path: _path0 });
  639. const _page1 = await Page.findOne({ path: _path1 });
  640. const _page2 = await Page.findOne({ path: _path2 });
  641. expect(_page0).toBeTruthy();
  642. expect(_page1).toBeTruthy();
  643. expect(_page2).toBeTruthy();
  644. // page operation
  645. const fromPath = '/resume_rename_5';
  646. const toPath = '/resume_rename_4/resume_rename_5';
  647. const pageOperation = await PageOperation.findOne({
  648. _id: pageOpId2, fromPath, toPath, 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub,
  649. });
  650. expect(pageOperation).toBeTruthy();
  651. // Make `unprocessableExpiryDate` 15 seconds ahead of current time.
  652. // The number 15 seconds has no meaning other than placing time in the furue.
  653. await PageOperation.findByIdAndUpdate(pageOperation._id, { unprocessableExpiryDate: addSeconds(new Date(), 15) });
  654. await expect(resumeRenameSubOperation(_page1)).rejects.toThrow(new Error('This page operation is currently being processed'));
  655. // cleanup
  656. await PageOperation.findByIdAndDelete(pageOperation._id);
  657. });
  658. test('Missing property(toPath) for PageOperation should throw error', async() => {
  659. // page
  660. const _path1 = '/resume_rename_7';
  661. const _page1 = await Page.findOne({ path: _path1 });
  662. expect(_page1).toBeTruthy();
  663. // page operation
  664. const pageOperation = await PageOperation.findOne({
  665. _id: pageOpId3, 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub,
  666. });
  667. expect(pageOperation).toBeTruthy();
  668. const promise = resumeRenameSubOperation(_page1);
  669. await expect(promise).rejects.toThrow(new Error(`Property toPath is missing which is needed to resume page operation(${pageOperation._id})`));
  670. // cleanup
  671. await PageOperation.findByIdAndDelete(pageOperation._id);
  672. });
  673. test(`it should succeed but 2 extra descendantCount should be added
  674. if the page operation was interrupted right after increasing ancestor's descendantCount in renameSubOperation`, async() => {
  675. // paths before renaming
  676. const _path0 = '/resume_rename_11'; // out of renaming scope
  677. const _path1 = '/resume_rename_11/resume_rename_12'; // out of renaming scope
  678. const _path2 = '/resume_rename_11/resume_rename_12/resume_rename_13'; // renamed already
  679. const _path3 = '/resume_rename_11/resume_rename_12/resume_rename_13/resume_rename_14'; // renamed already
  680. // paths after renaming
  681. const path0 = '/resume_rename_11';
  682. const path1 = '/resume_rename_11/resume_rename_12';
  683. const path2 = '/resume_rename_11/resume_rename_12/resume_rename_13';
  684. const path3 = '/resume_rename_11/resume_rename_12/resume_rename_13/resume_rename_14';
  685. // page
  686. const _page0 = await Page.findOne({ path: _path0 });
  687. const _page1 = await Page.findOne({ path: _path1 });
  688. const _page2 = await Page.findOne({ path: _path2 });
  689. const _page3 = await Page.findOne({ path: _path3 });
  690. expect(_page0).toBeTruthy();
  691. expect(_page1).toBeTruthy();
  692. expect(_page2).toBeTruthy();
  693. expect(_page3).toBeTruthy();
  694. // descendantCount
  695. expect(_page0.descendantCount).toBe(3);
  696. expect(_page1.descendantCount).toBe(2);
  697. expect(_page2.descendantCount).toBe(1);
  698. expect(_page3.descendantCount).toBe(0);
  699. // page operation
  700. const fromPath = '/resume_rename_11/resume_rename_13';
  701. const toPath = '/resume_rename_11/resume_rename_12/resume_rename_13';
  702. const _pageOperation = await PageOperation.findOne({
  703. _id: pageOpId5, fromPath, toPath, 'page._id': _page2._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub,
  704. });
  705. expect(_pageOperation).toBeTruthy();
  706. // rename
  707. await resumeRenameSubOperation(_page2);
  708. // page
  709. const page0 = await Page.findById(_page0._id);
  710. const page1 = await Page.findById(_page1._id);
  711. const page2 = await Page.findById(_page2._id);
  712. const page3 = await Page.findById(_page3._id);
  713. expect(page0).toBeTruthy();
  714. expect(page1).toBeTruthy();
  715. expect(page2).toBeTruthy();
  716. expect(page3).toBeTruthy();
  717. expect(page0.path).toBe(path0);
  718. expect(page1.path).toBe(path1);
  719. expect(page2.path).toBe(path2);
  720. expect(page3.path).toBe(path3);
  721. // page operation
  722. const pageOperation = await PageOperation.findById(_pageOperation._id);
  723. expect(pageOperation).toBeNull(); // should not exist
  724. // 2 extra descendants should be added to page1
  725. expect(page0.descendantCount).toBe(3);
  726. expect(page1.descendantCount).toBe(3); // originally 2, +1 in Main, -1 in Sub, +2 for new descendants
  727. expect(page2.descendantCount).toBe(1);
  728. expect(page3.descendantCount).toBe(0);
  729. });
  730. test(`it should succeed but 2 extra descendantCount should be subtracted from ex parent page
  731. if the page operation was interrupted right after reducing ancestor's descendantCount in renameSubOperation`, async() => {
  732. // paths before renaming
  733. const _path0 = '/resume_rename_15'; // out of renaming scope
  734. const _path1 = '/resume_rename_15/resume_rename_16'; // out of renaming scope
  735. const _path2 = '/resume_rename_15/resume_rename_17'; // out of renaming scope
  736. const _path3 = '/resume_rename_15/resume_rename_17/resume_rename_18'; // renamed already
  737. const _path4 = '/resume_rename_15/resume_rename_17/resume_rename_18/resume_rename_19'; // renamed already
  738. // paths after renaming
  739. const path0 = '/resume_rename_15';
  740. const path1 = '/resume_rename_15/resume_rename_16';
  741. const path2 = '/resume_rename_15/resume_rename_17';
  742. const path3 = '/resume_rename_15/resume_rename_17/resume_rename_18';
  743. const path4 = '/resume_rename_15/resume_rename_17/resume_rename_18/resume_rename_19';
  744. // page
  745. const _page0 = await Page.findOne({ path: _path0 });
  746. const _page1 = await Page.findOne({ path: _path1 });
  747. const _page2 = await Page.findOne({ path: _path2 });
  748. const _page3 = await Page.findOne({ path: _path3 });
  749. const _page4 = await Page.findOne({ path: _path4 });
  750. expect(_page0).toBeTruthy();
  751. expect(_page1).toBeTruthy();
  752. expect(_page2).toBeTruthy();
  753. expect(_page3).toBeTruthy();
  754. expect(_page4).toBeTruthy();
  755. // descendantCount
  756. expect(_page0.descendantCount).toBe(2);
  757. expect(_page1.descendantCount).toBe(0);
  758. expect(_page2.descendantCount).toBe(1);
  759. expect(_page3.descendantCount).toBe(1);
  760. expect(_page4.descendantCount).toBe(0);
  761. // page operation
  762. const fromPath = '/resume_rename_15/resume_rename_16/resume_rename_18';
  763. const toPath = '/resume_rename_15/resume_rename_17/resume_rename_18';
  764. const _pageOperation = await PageOperation.findOne({
  765. _id: pageOpId6, fromPath, toPath, 'page._id': _page3._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub,
  766. });
  767. expect(_pageOperation).toBeTruthy();
  768. // rename
  769. await resumeRenameSubOperation(_page3);
  770. // page
  771. const page0 = await Page.findById(_page0._id);
  772. const page1 = await Page.findById(_page1._id);
  773. const page2 = await Page.findById(_page2._id);
  774. const page3 = await Page.findById(_page3._id);
  775. const page4 = await Page.findById(_page4._id);
  776. expect(page0).toBeTruthy();
  777. expect(page1).toBeTruthy();
  778. expect(page2).toBeTruthy();
  779. expect(page3).toBeTruthy();
  780. expect(page3).toBeTruthy();
  781. expect(page0.path).toBe(path0);
  782. expect(page1.path).toBe(path1);
  783. expect(page2.path).toBe(path2);
  784. expect(page3.path).toBe(path3);
  785. expect(page4.path).toBe(path4);
  786. // page operation
  787. const pageOperation = await PageOperation.findById(_pageOperation._id);
  788. expect(pageOperation).toBeNull(); // should not exist
  789. // 2 extra descendants should be subtracted from page1
  790. expect(page0.descendantCount).toBe(2);
  791. expect(page1.descendantCount).toBe(-2); // originally 0, -2 for old descendants
  792. expect(page2.descendantCount).toBe(2); // originally 1, -1 in Sub, +2 for new descendants
  793. expect(page3.descendantCount).toBe(1);
  794. expect(page4.descendantCount).toBe(0);
  795. });
  796. });
  797. });