v5.page.test.ts 25 KB

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