page.test.js 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542
  1. /* eslint-disable no-unused-vars */
  2. import { GroupType } from '@growi/core';
  3. import { advanceTo } from 'jest-date-mock';
  4. import {
  5. PageRecursiveDeleteCompConfigValue,
  6. PageSingleDeleteCompConfigValue,
  7. } from '~/interfaces/page-delete-config';
  8. import PageTagRelation from '~/server/models/page-tag-relation';
  9. import Tag from '~/server/models/tag';
  10. import UserGroup from '~/server/models/user-group';
  11. import UserGroupRelation from '~/server/models/user-group-relation';
  12. import { generalXssFilter } from '../../../src/services/general-xss-filter';
  13. const mongoose = require('mongoose');
  14. const { getInstance } = require('../setup-crowi');
  15. let rootPage;
  16. let dummyUser1;
  17. let testUser1;
  18. let testUser2;
  19. let testUser3;
  20. let parentTag;
  21. let childTag;
  22. let parentForRename1;
  23. let parentForRename2;
  24. let parentForRename3;
  25. let parentForRename4;
  26. let parentForRename5;
  27. let parentForRename6;
  28. let parentForRename7;
  29. let parentForRename8;
  30. let parentForRename9;
  31. let irrelevantPage1;
  32. let irrelevantPage2;
  33. let childForRename1;
  34. let childForRename2;
  35. let childForRename3;
  36. let parentForDuplicate;
  37. let parentForDelete1;
  38. let parentForDelete2;
  39. let childForDelete;
  40. let canDeleteCompletelyTestPage;
  41. let parentForDeleteCompletely;
  42. let parentForRevert1;
  43. let parentForRevert2;
  44. let childForDuplicate;
  45. let childForDeleteCompletely;
  46. let childForRevert;
  47. describe('PageService', () => {
  48. let crowi;
  49. let Page;
  50. let Revision;
  51. let User;
  52. let Bookmark;
  53. let Comment;
  54. let ShareLink;
  55. let generalXssFilterProcessSpy;
  56. beforeAll(async () => {
  57. crowi = await getInstance();
  58. await crowi.configManager.updateConfig('app:isV5Compatible', null);
  59. User = mongoose.model('User');
  60. Page = mongoose.model('Page');
  61. Revision = mongoose.model('Revision');
  62. Bookmark = mongoose.model('Bookmark');
  63. Comment = mongoose.model('Comment');
  64. ShareLink = mongoose.model('ShareLink');
  65. await User.insertMany([
  66. { name: 'someone1', username: 'someone1', email: 'someone1@example.com' },
  67. { name: 'someone2', username: 'someone2', email: 'someone2@example.com' },
  68. { name: 'someone3', username: 'someone3', email: 'someone3@example.com' },
  69. ]);
  70. testUser1 = await User.findOne({ username: 'someone1' });
  71. testUser2 = await User.findOne({ username: 'someone2' });
  72. testUser3 = await User.findOne({ username: 'someone3' });
  73. dummyUser1 = await User.findOne({ username: 'v5DummyUser1' });
  74. await UserGroup.insertMany([
  75. {
  76. name: 'userGroupForCanDeleteCompletelyTest1',
  77. parent: null,
  78. },
  79. {
  80. name: 'userGroupForCanDeleteCompletelyTest2',
  81. parent: null,
  82. },
  83. ]);
  84. const userGroupForCanDeleteCompletelyTest1 = await UserGroup.findOne({
  85. name: 'userGroupForCanDeleteCompletelyTest1',
  86. });
  87. const userGroupForCanDeleteCompletelyTest2 = await UserGroup.findOne({
  88. name: 'userGroupForCanDeleteCompletelyTest2',
  89. });
  90. await UserGroupRelation.insertMany([
  91. {
  92. relatedGroup: userGroupForCanDeleteCompletelyTest1._id,
  93. relatedUser: testUser1._id,
  94. },
  95. {
  96. relatedGroup: userGroupForCanDeleteCompletelyTest2._id,
  97. relatedUser: testUser2._id,
  98. },
  99. {
  100. relatedGroup: userGroupForCanDeleteCompletelyTest1._id,
  101. relatedUser: testUser3._id,
  102. },
  103. {
  104. relatedGroup: userGroupForCanDeleteCompletelyTest2._id,
  105. relatedUser: testUser3._id,
  106. },
  107. ]);
  108. rootPage = await Page.findOne({ path: '/' });
  109. await Page.insertMany([
  110. {
  111. path: '/parentForRename1',
  112. grant: Page.GRANT_PUBLIC,
  113. creator: testUser1,
  114. lastUpdateUser: testUser1,
  115. },
  116. {
  117. path: '/parentForRename2',
  118. grant: Page.GRANT_PUBLIC,
  119. creator: testUser1,
  120. lastUpdateUser: testUser1,
  121. },
  122. {
  123. path: '/parentForRename3',
  124. grant: Page.GRANT_PUBLIC,
  125. creator: testUser1,
  126. lastUpdateUser: testUser1,
  127. },
  128. {
  129. path: '/parentForRename4',
  130. grant: Page.GRANT_PUBLIC,
  131. creator: testUser1,
  132. lastUpdateUser: testUser1,
  133. },
  134. {
  135. path: '/parentForRename5',
  136. grant: Page.GRANT_PUBLIC,
  137. creator: testUser1,
  138. lastUpdateUser: testUser1,
  139. },
  140. {
  141. path: '/parentForRename6',
  142. grant: Page.GRANT_PUBLIC,
  143. creator: testUser1,
  144. lastUpdateUser: testUser1,
  145. },
  146. {
  147. path: '/level1/level2',
  148. grant: Page.GRANT_PUBLIC,
  149. creator: testUser1,
  150. lastUpdateUser: testUser1,
  151. },
  152. {
  153. path: '/level1/level2/child',
  154. grant: Page.GRANT_PUBLIC,
  155. creator: testUser1,
  156. lastUpdateUser: testUser1,
  157. },
  158. {
  159. path: '/level1/level2/level2',
  160. grant: Page.GRANT_PUBLIC,
  161. creator: testUser1,
  162. lastUpdateUser: testUser1,
  163. },
  164. {
  165. path: '/parentForRename6-2021H1',
  166. grant: Page.GRANT_PUBLIC,
  167. creator: testUser1,
  168. lastUpdateUser: testUser1,
  169. },
  170. {
  171. path: '/level1-2021H1',
  172. grant: Page.GRANT_PUBLIC,
  173. creator: testUser1,
  174. lastUpdateUser: testUser1,
  175. },
  176. {
  177. path: '/parentForRename1/child',
  178. grant: Page.GRANT_PUBLIC,
  179. creator: testUser1,
  180. lastUpdateUser: testUser1,
  181. },
  182. {
  183. path: '/parentForRename2/child',
  184. grant: Page.GRANT_PUBLIC,
  185. creator: testUser1,
  186. lastUpdateUser: testUser1,
  187. },
  188. {
  189. path: '/parentForRename3/child',
  190. grant: Page.GRANT_PUBLIC,
  191. creator: testUser1,
  192. lastUpdateUser: testUser1,
  193. },
  194. {
  195. path: '/canDeleteCompletelyTestPage',
  196. grant: Page.GRANT_USER_GROUP,
  197. creator: testUser2,
  198. grantedGroups: [
  199. {
  200. item: userGroupForCanDeleteCompletelyTest1._id,
  201. type: GroupType.userGroup,
  202. },
  203. {
  204. item: userGroupForCanDeleteCompletelyTest2._id,
  205. type: GroupType.userGroup,
  206. },
  207. ],
  208. lastUpdateUser: testUser1,
  209. },
  210. {
  211. path: '/parentForDuplicate',
  212. grant: Page.GRANT_PUBLIC,
  213. creator: testUser1,
  214. lastUpdateUser: testUser1,
  215. revision: '600d395667536503354cbe91',
  216. },
  217. {
  218. path: '/parentForDuplicate/child',
  219. grant: Page.GRANT_PUBLIC,
  220. creator: testUser1,
  221. lastUpdateUser: testUser1,
  222. revision: '600d395667536503354cbe92',
  223. },
  224. {
  225. path: '/parentForDelete1',
  226. grant: Page.GRANT_PUBLIC,
  227. creator: testUser1,
  228. lastUpdateUser: testUser1,
  229. },
  230. {
  231. path: '/parentForDelete2',
  232. grant: Page.GRANT_PUBLIC,
  233. creator: testUser1,
  234. lastUpdateUser: testUser1,
  235. },
  236. {
  237. path: '/parentForDelete/child',
  238. grant: Page.GRANT_PUBLIC,
  239. creator: testUser1,
  240. lastUpdateUser: testUser1,
  241. },
  242. {
  243. path: '/parentForDeleteCompletely',
  244. grant: Page.GRANT_PUBLIC,
  245. creator: testUser1,
  246. lastUpdateUser: testUser1,
  247. },
  248. {
  249. path: '/parentForDeleteCompletely/child',
  250. grant: Page.GRANT_PUBLIC,
  251. creator: testUser1,
  252. lastUpdateUser: testUser1,
  253. },
  254. {
  255. path: '/trash/parentForRevert1',
  256. status: Page.STATUS_DELETED,
  257. grant: Page.GRANT_PUBLIC,
  258. creator: testUser1,
  259. lastUpdateUser: testUser1,
  260. },
  261. {
  262. path: '/trash/parentForRevert2',
  263. status: Page.STATUS_DELETED,
  264. grant: Page.GRANT_PUBLIC,
  265. creator: testUser1,
  266. lastUpdateUser: testUser1,
  267. },
  268. {
  269. path: '/trash/parentForRevert/child',
  270. status: Page.STATUS_DELETED,
  271. grant: Page.GRANT_PUBLIC,
  272. creator: testUser1,
  273. lastUpdateUser: testUser1,
  274. },
  275. ]);
  276. parentForRename1 = await Page.findOne({ path: '/parentForRename1' });
  277. parentForRename2 = await Page.findOne({ path: '/parentForRename2' });
  278. parentForRename3 = await Page.findOne({ path: '/parentForRename3' });
  279. parentForRename4 = await Page.findOne({ path: '/parentForRename4' });
  280. parentForRename5 = await Page.findOne({ path: '/parentForRename5' });
  281. parentForRename6 = await Page.findOne({ path: '/parentForRename6' });
  282. parentForRename7 = await Page.findOne({ path: '/level1/level2' });
  283. parentForRename8 = await Page.findOne({ path: '/level1/level2/child' });
  284. parentForRename9 = await Page.findOne({ path: '/level1/level2/level2' });
  285. irrelevantPage1 = await Page.findOne({ path: '/parentForRename6-2021H1' });
  286. irrelevantPage2 = await Page.findOne({ path: '/level1-2021H1' });
  287. parentForDuplicate = await Page.findOne({ path: '/parentForDuplicate' });
  288. parentForDelete1 = await Page.findOne({ path: '/parentForDelete1' });
  289. parentForDelete2 = await Page.findOne({ path: '/parentForDelete2' });
  290. canDeleteCompletelyTestPage = await Page.findOne({
  291. path: '/canDeleteCompletelyTestPage',
  292. });
  293. parentForDeleteCompletely = await Page.findOne({
  294. path: '/parentForDeleteCompletely',
  295. });
  296. parentForRevert1 = await Page.findOne({ path: '/trash/parentForRevert1' });
  297. parentForRevert2 = await Page.findOne({ path: '/trash/parentForRevert2' });
  298. childForRename1 = await Page.findOne({ path: '/parentForRename1/child' });
  299. childForRename2 = await Page.findOne({ path: '/parentForRename2/child' });
  300. childForRename3 = await Page.findOne({ path: '/parentForRename3/child' });
  301. childForDuplicate = await Page.findOne({
  302. path: '/parentForDuplicate/child',
  303. });
  304. childForDelete = await Page.findOne({ path: '/parentForDelete/child' });
  305. childForDeleteCompletely = await Page.findOne({
  306. path: '/parentForDeleteCompletely/child',
  307. });
  308. childForRevert = await Page.findOne({
  309. path: '/trash/parentForRevert/child',
  310. });
  311. await Tag.insertMany([{ name: 'Parent' }, { name: 'Child' }]);
  312. parentTag = await Tag.findOne({ name: 'Parent' });
  313. childTag = await Tag.findOne({ name: 'Child' });
  314. await PageTagRelation.insertMany([
  315. { relatedPage: parentForDuplicate, relatedTag: parentTag },
  316. { relatedPage: childForDuplicate, relatedTag: childTag },
  317. ]);
  318. await Revision.insertMany([
  319. {
  320. _id: '600d395667536503354cbe91',
  321. pageId: parentForDuplicate._id,
  322. body: 'duplicateBody',
  323. },
  324. {
  325. _id: '600d395667536503354cbe92',
  326. pageId: childForDuplicate._id,
  327. body: 'duplicateChildBody',
  328. },
  329. ]);
  330. generalXssFilterProcessSpy = jest.spyOn(generalXssFilter, 'process');
  331. /**
  332. * getParentAndFillAncestors
  333. */
  334. const pageIdPAF1 = new mongoose.Types.ObjectId();
  335. const pageIdPAF2 = new mongoose.Types.ObjectId();
  336. const pageIdPAF3 = new mongoose.Types.ObjectId();
  337. await Page.insertMany([
  338. {
  339. _id: pageIdPAF1,
  340. path: '/PAF1',
  341. grant: Page.GRANT_PUBLIC,
  342. creator: dummyUser1,
  343. lastUpdateUser: dummyUser1._id,
  344. isEmpty: false,
  345. parent: rootPage._id,
  346. descendantCount: 0,
  347. },
  348. {
  349. _id: pageIdPAF2,
  350. path: '/emp_anc3',
  351. grant: Page.GRANT_PUBLIC,
  352. isEmpty: true,
  353. descendantCount: 1,
  354. parent: rootPage._id,
  355. },
  356. {
  357. path: '/emp_anc3/PAF3',
  358. grant: Page.GRANT_PUBLIC,
  359. creator: dummyUser1,
  360. lastUpdateUser: dummyUser1._id,
  361. isEmpty: false,
  362. descendantCount: 0,
  363. parent: pageIdPAF2,
  364. },
  365. {
  366. _id: pageIdPAF3,
  367. path: '/emp_anc4',
  368. grant: Page.GRANT_PUBLIC,
  369. isEmpty: true,
  370. descendantCount: 1,
  371. parent: rootPage._id,
  372. },
  373. {
  374. path: '/emp_anc4/PAF4',
  375. grant: Page.GRANT_PUBLIC,
  376. creator: dummyUser1,
  377. lastUpdateUser: dummyUser1._id,
  378. isEmpty: false,
  379. descendantCount: 0,
  380. parent: pageIdPAF3,
  381. },
  382. {
  383. path: '/emp_anc4',
  384. grant: Page.GRANT_OWNER,
  385. grantedUsers: [dummyUser1._id],
  386. creator: dummyUser1,
  387. lastUpdateUser: dummyUser1._id,
  388. isEmpty: false,
  389. },
  390. {
  391. path: '/get_parent_A',
  392. creator: dummyUser1,
  393. lastUpdateUser: dummyUser1,
  394. parent: null,
  395. },
  396. {
  397. path: '/get_parent_A/get_parent_B',
  398. creator: dummyUser1,
  399. lastUpdateUser: dummyUser1,
  400. parent: null,
  401. },
  402. {
  403. path: '/get_parent_C',
  404. creator: dummyUser1,
  405. lastUpdateUser: dummyUser1,
  406. parent: rootPage._id,
  407. },
  408. {
  409. path: '/get_parent_C/get_parent_D',
  410. creator: dummyUser1,
  411. lastUpdateUser: dummyUser1,
  412. parent: null,
  413. },
  414. ]);
  415. });
  416. describe('rename page without using renameDescendantsWithStreamSpy', () => {
  417. test('rename page with different tree with isRecursively [deeper]', async () => {
  418. const resultPage = await crowi.pageService.renamePage(
  419. parentForRename6,
  420. '/parentForRename6/renamedChild',
  421. testUser1,
  422. { isRecursively: true },
  423. { ip: '::ffff:127.0.0.1', endpoint: '/_api/v3/pages/rename' },
  424. );
  425. const wrongPage = await Page.findOne({
  426. path: '/parentForRename6/renamedChild/renamedChild',
  427. });
  428. const expectPage1 = await Page.findOne({
  429. path: '/parentForRename6/renamedChild',
  430. });
  431. const expectPage2 = await Page.findOne({
  432. path: '/parentForRename6-2021H1',
  433. });
  434. expect(resultPage.path).toEqual(expectPage1.path);
  435. expect(expectPage2.path).not.toBeNull();
  436. // Check that pages that are not to be renamed have not been renamed
  437. expect(wrongPage).toBeNull();
  438. });
  439. test('rename page with different tree with isRecursively [shallower]', async () => {
  440. // setup
  441. expect(await Page.findOne({ path: '/level1' })).toBeNull();
  442. expect(await Page.findOne({ path: '/level1/level2' })).not.toBeNull();
  443. expect(
  444. await Page.findOne({ path: '/level1/level2/child' }),
  445. ).not.toBeNull();
  446. expect(
  447. await Page.findOne({ path: '/level1/level2/level2' }),
  448. ).not.toBeNull();
  449. expect(await Page.findOne({ path: '/level1-2021H1' })).not.toBeNull();
  450. // when
  451. // rename /level1/level2 --> /level1
  452. await crowi.pageService.renamePage(
  453. parentForRename7,
  454. '/level1',
  455. testUser1,
  456. { isRecursively: true },
  457. { ip: '::ffff:127.0.0.1', endpoint: '/_api/v3/pages/rename' },
  458. );
  459. // then
  460. expect(await Page.findOne({ path: '/level1' })).not.toBeNull();
  461. expect(await Page.findOne({ path: '/level1/child' })).not.toBeNull();
  462. expect(await Page.findOne({ path: '/level1/level2' })).not.toBeNull();
  463. expect(await Page.findOne({ path: '/level1/level2/child' })).toBeNull();
  464. expect(await Page.findOne({ path: '/level1/level2/level2' })).toBeNull();
  465. // Check that pages that are not to be renamed have not been renamed
  466. expect(await Page.findOne({ path: '/level1-2021H1' })).not.toBeNull();
  467. });
  468. });
  469. describe('rename page', () => {
  470. let pageEventSpy;
  471. let renameDescendantsWithStreamSpy;
  472. // mock new Date() and Date.now()
  473. advanceTo(new Date(2000, 1, 1, 0, 0, 0));
  474. const dateToUse = new Date();
  475. beforeEach(async () => {
  476. pageEventSpy = jest
  477. .spyOn(crowi.pageService.pageEvent, 'emit')
  478. .mockImplementation();
  479. renameDescendantsWithStreamSpy = jest
  480. .spyOn(crowi.pageService, 'renameDescendantsWithStream')
  481. .mockImplementation();
  482. });
  483. describe('renamePage()', () => {
  484. test('rename page without options', async () => {
  485. const resultPage = await crowi.pageService.renamePage(
  486. parentForRename1,
  487. '/renamed1',
  488. testUser2,
  489. {},
  490. { ip: '::ffff:127.0.0.1', endpoint: '/_api/v3/pages/rename' },
  491. );
  492. expect(generalXssFilterProcessSpy).toHaveBeenCalled();
  493. expect(pageEventSpy).toHaveBeenCalledWith('rename');
  494. expect(resultPage.path).toBe('/renamed1');
  495. expect(resultPage.updatedAt).toEqual(parentForRename1.updatedAt);
  496. expect(resultPage.lastUpdateUser).toEqual(testUser1._id);
  497. });
  498. test('rename page with updateMetadata option', async () => {
  499. const resultPage = await crowi.pageService.renamePage(
  500. parentForRename2,
  501. '/renamed2',
  502. testUser2,
  503. { updateMetadata: true },
  504. { ip: '::ffff:127.0.0.1', endpoint: '/_api/v3/pages/rename' },
  505. );
  506. expect(generalXssFilterProcessSpy).toHaveBeenCalled();
  507. expect(pageEventSpy).toHaveBeenCalledWith('rename');
  508. expect(resultPage.path).toBe('/renamed2');
  509. expect(resultPage.updatedAt).toEqual(dateToUse);
  510. expect(resultPage.lastUpdateUser).toEqual(testUser2._id);
  511. });
  512. test('rename page with createRedirectPage option', async () => {
  513. const resultPage = await crowi.pageService.renamePage(
  514. parentForRename3,
  515. '/renamed3',
  516. testUser2,
  517. { createRedirectPage: true },
  518. { ip: '::ffff:127.0.0.1', endpoint: '/_api/v3/pages/rename' },
  519. );
  520. expect(generalXssFilterProcessSpy).toHaveBeenCalled();
  521. expect(pageEventSpy).toHaveBeenCalledWith('rename');
  522. expect(resultPage.path).toBe('/renamed3');
  523. expect(resultPage.updatedAt).toEqual(parentForRename3.updatedAt);
  524. expect(resultPage.lastUpdateUser).toEqual(testUser1._id);
  525. });
  526. test('rename page with isRecursively', async () => {
  527. const resultPage = await crowi.pageService.renamePage(
  528. parentForRename4,
  529. '/renamed4',
  530. testUser2,
  531. { isRecursively: true },
  532. { ip: '::ffff:127.0.0.1', endpoint: '/_api/v3/pages/rename' },
  533. );
  534. expect(generalXssFilterProcessSpy).toHaveBeenCalled();
  535. expect(renameDescendantsWithStreamSpy).toHaveBeenCalled();
  536. expect(pageEventSpy).toHaveBeenCalledWith('rename');
  537. expect(resultPage.path).toBe('/renamed4');
  538. expect(resultPage.updatedAt).toEqual(parentForRename4.updatedAt);
  539. expect(resultPage.lastUpdateUser).toEqual(testUser1._id);
  540. });
  541. test('rename page with different tree with isRecursively', async () => {
  542. const resultPage = await crowi.pageService.renamePage(
  543. parentForRename5,
  544. '/parentForRename5/renamedChild',
  545. testUser1,
  546. { isRecursively: true },
  547. { ip: '::ffff:127.0.0.1', endpoint: '/_api/v3/pages/rename' },
  548. );
  549. const wrongPage = await Page.findOne({
  550. path: '/parentForRename5/renamedChild/renamedChild',
  551. });
  552. const expectPage = await Page.findOne({
  553. path: '/parentForRename5/renamedChild',
  554. });
  555. expect(resultPage.path).toEqual(expectPage.path);
  556. expect(wrongPage).toBeNull();
  557. });
  558. });
  559. test('renameDescendants without options', async () => {
  560. const oldPagePathPrefix = /^\/parentForRename1/i;
  561. const newPagePathPrefix = '/renamed1';
  562. await crowi.pageService.renameDescendants(
  563. [childForRename1],
  564. testUser2,
  565. {},
  566. oldPagePathPrefix,
  567. newPagePathPrefix,
  568. );
  569. const resultPage = await Page.findOne({ path: '/renamed1/child' });
  570. expect(resultPage).not.toBeNull();
  571. expect(pageEventSpy).toHaveBeenCalledWith(
  572. 'updateMany',
  573. [childForRename1],
  574. testUser2,
  575. );
  576. expect(resultPage.path).toBe('/renamed1/child');
  577. expect(resultPage.updatedAt).toEqual(childForRename1.updatedAt);
  578. expect(resultPage.lastUpdateUser).toEqual(testUser1._id);
  579. });
  580. test('renameDescendants with updateMetadata option', async () => {
  581. const oldPagePathPrefix = /^\/parentForRename2/i;
  582. const newPagePathPrefix = '/renamed2';
  583. await crowi.pageService.renameDescendants(
  584. [childForRename2],
  585. testUser2,
  586. { updateMetadata: true },
  587. oldPagePathPrefix,
  588. newPagePathPrefix,
  589. );
  590. const resultPage = await Page.findOne({ path: '/renamed2/child' });
  591. expect(resultPage).not.toBeNull();
  592. expect(pageEventSpy).toHaveBeenCalledWith(
  593. 'updateMany',
  594. [childForRename2],
  595. testUser2,
  596. );
  597. expect(resultPage.path).toBe('/renamed2/child');
  598. expect(resultPage.updatedAt).toEqual(dateToUse);
  599. expect(resultPage.lastUpdateUser).toEqual(testUser2._id);
  600. });
  601. test('renameDescendants with createRedirectPage option', async () => {
  602. const oldPagePathPrefix = /^\/parentForRename3/i;
  603. const newPagePathPrefix = '/renamed3';
  604. await crowi.pageService.renameDescendants(
  605. [childForRename3],
  606. testUser2,
  607. { createRedirectPage: true },
  608. oldPagePathPrefix,
  609. newPagePathPrefix,
  610. );
  611. const resultPage = await Page.findOne({ path: '/renamed3/child' });
  612. expect(resultPage).not.toBeNull();
  613. expect(pageEventSpy).toHaveBeenCalledWith(
  614. 'updateMany',
  615. [childForRename3],
  616. testUser2,
  617. );
  618. expect(resultPage.path).toBe('/renamed3/child');
  619. expect(resultPage.updatedAt).toEqual(childForRename3.updatedAt);
  620. expect(resultPage.lastUpdateUser).toEqual(testUser1._id);
  621. });
  622. });
  623. describe('duplicate page', () => {
  624. let duplicateDescendantsWithStreamSpy;
  625. // TODO https://redmine.weseek.co.jp/issues/87537 : activate outer module mockImplementation
  626. // jest.mock('~/server/models/serializers/page-serializer');
  627. // const { serializePageSecurely } = require('~/server/models/serializers/page-serializer');
  628. // serializePageSecurely.mockImplementation(page => page);
  629. beforeEach(async () => {
  630. duplicateDescendantsWithStreamSpy = jest
  631. .spyOn(crowi.pageService, 'duplicateDescendantsWithStream')
  632. .mockImplementation();
  633. });
  634. test('duplicate page (isRecursively: false)', async () => {
  635. const dummyId = '600d395667536503354c9999';
  636. crowi.models.Page.findRelatedTagsById = jest
  637. .fn()
  638. .mockImplementation(() => {
  639. return parentTag;
  640. });
  641. const originTagsMock = jest
  642. .spyOn(Page, 'findRelatedTagsById')
  643. .mockImplementation(() => {
  644. return parentTag;
  645. });
  646. jest.spyOn(PageTagRelation, 'updatePageTags').mockImplementation(() => {
  647. return [dummyId, parentTag.name];
  648. });
  649. jest
  650. .spyOn(PageTagRelation, 'listTagNamesByPage')
  651. .mockImplementation(() => {
  652. return [parentTag.name];
  653. });
  654. const resultPage = await crowi.pageService.duplicate(
  655. parentForDuplicate,
  656. '/newParentDuplicate',
  657. testUser2,
  658. false,
  659. );
  660. const duplicatedToPageRevision = await Revision.findOne({
  661. pageId: resultPage._id,
  662. });
  663. expect(generalXssFilterProcessSpy).toHaveBeenCalled();
  664. expect(duplicateDescendantsWithStreamSpy).not.toHaveBeenCalled();
  665. // TODO https://redmine.weseek.co.jp/issues/87537 : activate outer module mockImplementation
  666. // expect(serializePageSecurely).toHaveBeenCalled();
  667. expect(resultPage.path).toBe('/newParentDuplicate');
  668. expect(resultPage.lastUpdateUser._id).toEqual(testUser2._id);
  669. expect(duplicatedToPageRevision._id).not.toEqual(
  670. parentForDuplicate.revision._id,
  671. );
  672. expect(resultPage.grant).toEqual(parentForDuplicate.grant);
  673. expect(resultPage.tags).toEqual([originTagsMock().name]);
  674. });
  675. test('duplicate page (isRecursively: true)', async () => {
  676. const dummyId = '600d395667536503354c9999';
  677. crowi.models.Page.findRelatedTagsById = jest
  678. .fn()
  679. .mockImplementation(() => {
  680. return parentTag;
  681. });
  682. const originTagsMock = jest
  683. .spyOn(Page, 'findRelatedTagsById')
  684. .mockImplementation(() => {
  685. return parentTag;
  686. });
  687. jest.spyOn(PageTagRelation, 'updatePageTags').mockImplementation(() => {
  688. return [dummyId, parentTag.name];
  689. });
  690. jest
  691. .spyOn(PageTagRelation, 'listTagNamesByPage')
  692. .mockImplementation(() => {
  693. return [parentTag.name];
  694. });
  695. const resultPageRecursivly = await crowi.pageService.duplicate(
  696. parentForDuplicate,
  697. '/newParentDuplicateRecursively',
  698. testUser2,
  699. true,
  700. );
  701. const duplicatedRecursivelyToPageRevision = await Revision.findOne({
  702. pageId: resultPageRecursivly._id,
  703. });
  704. expect(generalXssFilterProcessSpy).toHaveBeenCalled();
  705. expect(duplicateDescendantsWithStreamSpy).toHaveBeenCalled();
  706. // TODO https://redmine.weseek.co.jp/issues/87537 : activate outer module mockImplementation
  707. // expect(serializePageSecurely).toHaveBeenCalled();
  708. expect(resultPageRecursivly.path).toBe('/newParentDuplicateRecursively');
  709. expect(resultPageRecursivly.lastUpdateUser._id).toEqual(testUser2._id);
  710. expect(duplicatedRecursivelyToPageRevision._id).not.toEqual(
  711. parentForDuplicate.revision._id,
  712. );
  713. expect(resultPageRecursivly.grant).toEqual(parentForDuplicate.grant);
  714. expect(resultPageRecursivly.tags).toEqual([originTagsMock().name]);
  715. });
  716. test('duplicateDescendants()', async () => {
  717. const duplicateTagsMock = await jest
  718. .spyOn(crowi.pageService, 'duplicateTags')
  719. .mockImplementationOnce();
  720. await crowi.pageService.duplicateDescendants(
  721. [childForDuplicate],
  722. testUser2,
  723. parentForDuplicate.path,
  724. '/newPathPrefix',
  725. );
  726. const childForDuplicateRevision = await Revision.findOne({
  727. pageId: childForDuplicate._id,
  728. });
  729. const insertedPage = await Page.findOne({ path: '/newPathPrefix/child' });
  730. const insertedRevision = await Revision.findOne({
  731. pageId: insertedPage._id,
  732. });
  733. expect(insertedPage).not.toBeNull();
  734. expect(insertedPage.path).toEqual('/newPathPrefix/child');
  735. expect(insertedPage.lastUpdateUser).toEqual(testUser2._id);
  736. expect([insertedRevision]).not.toBeNull();
  737. expect(insertedRevision.pageId).toEqual(insertedPage._id);
  738. expect(insertedRevision._id).not.toEqual(childForDuplicateRevision._id);
  739. expect(insertedRevision.body).toEqual(childForDuplicateRevision.body);
  740. expect(duplicateTagsMock).toHaveBeenCalled();
  741. });
  742. test('duplicateTags()', async () => {
  743. const pageIdMapping = {
  744. [parentForDuplicate._id]: '60110bdd85339d7dc732dddd',
  745. };
  746. const duplicateTagsReturn =
  747. await crowi.pageService.duplicateTags(pageIdMapping);
  748. const parentoForDuplicateTag = await PageTagRelation.findOne({
  749. relatedPage: parentForDuplicate._id,
  750. });
  751. expect(duplicateTagsReturn).toHaveLength(1);
  752. expect(duplicateTagsReturn[0].relatedTag).toEqual(
  753. parentoForDuplicateTag.relatedTag,
  754. );
  755. });
  756. });
  757. describe('delete page', () => {
  758. let getDeletedPageNameSpy;
  759. let pageEventSpy;
  760. let deleteDescendantsWithStreamSpy;
  761. const dateToUse = new Date('2000-01-01');
  762. beforeEach(async () => {
  763. jest.spyOn(global.Date, 'now').mockImplementation(() => dateToUse);
  764. getDeletedPageNameSpy = jest.spyOn(Page, 'getDeletedPageName');
  765. pageEventSpy = jest.spyOn(crowi.pageService.pageEvent, 'emit');
  766. deleteDescendantsWithStreamSpy = jest
  767. .spyOn(crowi.pageService, 'deleteDescendantsWithStream')
  768. .mockImplementation();
  769. });
  770. test('delete page without options', async () => {
  771. const resultPage = await crowi.pageService.deletePage(
  772. parentForDelete1,
  773. testUser2,
  774. {},
  775. false,
  776. {
  777. ip: '::ffff:127.0.0.1',
  778. endpoint: '/_api/v3/pages/delete',
  779. },
  780. );
  781. expect(getDeletedPageNameSpy).toHaveBeenCalled();
  782. expect(deleteDescendantsWithStreamSpy).not.toHaveBeenCalled();
  783. expect(resultPage.status).toBe(Page.STATUS_DELETED);
  784. expect(resultPage.path).toBe('/trash/parentForDelete1');
  785. expect(resultPage.deleteUser).toEqual(testUser2._id);
  786. expect(resultPage.deletedAt).toEqual(dateToUse);
  787. expect(resultPage.updatedAt).toEqual(parentForDelete1.updatedAt);
  788. expect(resultPage.lastUpdateUser).toEqual(testUser1._id);
  789. expect(pageEventSpy).toHaveBeenCalledWith(
  790. 'delete',
  791. parentForDelete1,
  792. resultPage,
  793. testUser2,
  794. );
  795. });
  796. test('delete page with isRecursively', async () => {
  797. const resultPage = await crowi.pageService.deletePage(
  798. parentForDelete2,
  799. testUser2,
  800. {},
  801. true,
  802. {
  803. ip: '::ffff:127.0.0.1',
  804. endpoint: '/_api/v3/pages/delete',
  805. },
  806. );
  807. expect(getDeletedPageNameSpy).toHaveBeenCalled();
  808. expect(deleteDescendantsWithStreamSpy).toHaveBeenCalled();
  809. expect(resultPage.status).toBe(Page.STATUS_DELETED);
  810. expect(resultPage.path).toBe('/trash/parentForDelete2');
  811. expect(resultPage.deleteUser).toEqual(testUser2._id);
  812. expect(resultPage.deletedAt).toEqual(dateToUse);
  813. expect(resultPage.updatedAt).toEqual(parentForDelete2.updatedAt);
  814. expect(resultPage.lastUpdateUser).toEqual(testUser1._id);
  815. expect(pageEventSpy).toHaveBeenCalledWith(
  816. 'delete',
  817. parentForDelete2,
  818. resultPage,
  819. testUser2,
  820. );
  821. });
  822. test('deleteDescendants', async () => {
  823. await crowi.pageService.deleteDescendants([childForDelete], testUser2);
  824. const resultPage = await Page.findOne({
  825. path: '/trash/parentForDelete/child',
  826. });
  827. expect(resultPage.status).toBe(Page.STATUS_DELETED);
  828. expect(resultPage.path).toBe('/trash/parentForDelete/child');
  829. expect(resultPage.deleteUser).toEqual(testUser2._id);
  830. expect(resultPage.deletedAt).toEqual(dateToUse);
  831. expect(resultPage.updatedAt).toEqual(childForDelete.updatedAt);
  832. expect(resultPage.lastUpdateUser).toEqual(testUser1._id);
  833. });
  834. });
  835. describe('delete page completely', () => {
  836. describe('canDeleteCompletely', () => {
  837. describe(`when user is not admin or author,
  838. pageCompleteDeletionAuthority is 'anyone',
  839. user is not related to all granted groups,
  840. and isAllGroupMembershipRequiredForPageCompleteDeletion is true`, () => {
  841. beforeEach(async () => {
  842. const config = {
  843. 'security:isAllGroupMembershipRequiredForPageCompleteDeletion': true,
  844. 'security:pageCompleteDeletionAuthority':
  845. PageSingleDeleteCompConfigValue.Anyone,
  846. 'security:pageRecursiveCompleteDeletionAuthority':
  847. PageRecursiveDeleteCompConfigValue.Anyone,
  848. };
  849. await crowi.configManager.updateConfigs(config);
  850. });
  851. test('is not deletable', async () => {
  852. const creatorId = await crowi.pageService.getCreatorIdForCanDelete(
  853. canDeleteCompletelyTestPage,
  854. );
  855. const userRelatedGroups =
  856. await crowi.pageGrantService.getUserRelatedGroups(testUser1);
  857. const isDeleteable = crowi.pageService.canDeleteCompletely(
  858. canDeleteCompletelyTestPage,
  859. creatorId,
  860. testUser1,
  861. false,
  862. userRelatedGroups,
  863. );
  864. expect(isDeleteable).toBe(false);
  865. });
  866. });
  867. describe(`when user is not admin or author,
  868. pageCompleteDeletionAuthority is 'anyone',
  869. user is related to all granted groups,
  870. and isAllGroupMembershipRequiredForPageCompleteDeletion is true`, () => {
  871. beforeEach(async () => {
  872. const config = {
  873. 'security:isAllGroupMembershipRequiredForPageCompleteDeletion': true,
  874. 'security:pageCompleteDeletionAuthority':
  875. PageSingleDeleteCompConfigValue.Anyone,
  876. 'security:pageRecursiveCompleteDeletionAuthority':
  877. PageRecursiveDeleteCompConfigValue.Anyone,
  878. };
  879. await crowi.configManager.updateConfigs(config);
  880. });
  881. test('is not deletable', async () => {
  882. const creatorId = await crowi.pageService.getCreatorIdForCanDelete(
  883. canDeleteCompletelyTestPage,
  884. );
  885. const userRelatedGroups =
  886. await crowi.pageGrantService.getUserRelatedGroups(testUser3);
  887. const isDeleteable = crowi.pageService.canDeleteCompletely(
  888. canDeleteCompletelyTestPage,
  889. creatorId,
  890. testUser3,
  891. false,
  892. userRelatedGroups,
  893. );
  894. expect(isDeleteable).toBe(true);
  895. });
  896. });
  897. describe(`when user is not admin or author,
  898. pageCompleteDeletionAuthority is 'anyone',
  899. user is not related to all granted groups,
  900. and isAllGroupMembershipRequiredForPageCompleteDeletion is false`, () => {
  901. beforeEach(async () => {
  902. const config = {
  903. 'security:isAllGroupMembershipRequiredForPageCompleteDeletion': false,
  904. 'security:pageCompleteDeletionAuthority':
  905. PageSingleDeleteCompConfigValue.Anyone,
  906. 'security:pageRecursiveCompleteDeletionAuthority':
  907. PageRecursiveDeleteCompConfigValue.Anyone,
  908. };
  909. await crowi.configManager.updateConfigs(config);
  910. });
  911. test('is deletable', async () => {
  912. const creatorId = await crowi.pageService.getCreatorIdForCanDelete(
  913. canDeleteCompletelyTestPage,
  914. );
  915. const userRelatedGroups =
  916. await crowi.pageGrantService.getUserRelatedGroups(testUser1);
  917. const isDeleteable = crowi.pageService.canDeleteCompletely(
  918. canDeleteCompletelyTestPage,
  919. creatorId,
  920. testUser1,
  921. false,
  922. userRelatedGroups,
  923. );
  924. expect(isDeleteable).toBe(true);
  925. });
  926. });
  927. describe(`when user is author,
  928. pageCompleteDeletionAuthority is 'anyone',
  929. user is not related to all granted groups,
  930. and isAllGroupMembershipRequiredForPageCompleteDeletion is true`, () => {
  931. beforeEach(async () => {
  932. const config = {
  933. 'security:isAllGroupMembershipRequiredForPageCompleteDeletion': false,
  934. 'security:pageCompleteDeletionAuthority':
  935. PageSingleDeleteCompConfigValue.Anyone,
  936. 'security:pageRecursiveCompleteDeletionAuthority':
  937. PageRecursiveDeleteCompConfigValue.Anyone,
  938. };
  939. await crowi.configManager.updateConfigs(config);
  940. });
  941. test('is deletable', async () => {
  942. const creatorId = await crowi.pageService.getCreatorIdForCanDelete(
  943. canDeleteCompletelyTestPage,
  944. );
  945. const userRelatedGroups =
  946. await crowi.pageGrantService.getUserRelatedGroups(testUser2);
  947. const isDeleteable = crowi.pageService.canDeleteCompletely(
  948. canDeleteCompletelyTestPage,
  949. creatorId,
  950. testUser2,
  951. false,
  952. userRelatedGroups,
  953. );
  954. expect(isDeleteable).toBe(true);
  955. });
  956. });
  957. });
  958. describe('actual delete process', () => {
  959. let pageEventSpy;
  960. let deleteCompletelyOperationSpy;
  961. let deleteCompletelyDescendantsWithStreamSpy;
  962. let deleteManyBookmarkSpy;
  963. let deleteManyCommentSpy;
  964. let deleteManyPageTagRelationSpy;
  965. let deleteManyShareLinkSpy;
  966. let deleteManyRevisionSpy;
  967. let deleteManyPageSpy;
  968. let removeAllAttachmentsSpy;
  969. beforeEach(async () => {
  970. pageEventSpy = jest.spyOn(crowi.pageService.pageEvent, 'emit');
  971. deleteCompletelyOperationSpy = jest.spyOn(
  972. crowi.pageService,
  973. 'deleteCompletelyOperation',
  974. );
  975. deleteCompletelyDescendantsWithStreamSpy = jest
  976. .spyOn(crowi.pageService, 'deleteCompletelyDescendantsWithStream')
  977. .mockImplementation();
  978. deleteManyBookmarkSpy = jest
  979. .spyOn(Bookmark, 'deleteMany')
  980. .mockImplementation();
  981. deleteManyCommentSpy = jest
  982. .spyOn(Comment, 'deleteMany')
  983. .mockImplementation();
  984. deleteManyPageTagRelationSpy = jest
  985. .spyOn(PageTagRelation, 'deleteMany')
  986. .mockImplementation();
  987. deleteManyShareLinkSpy = jest
  988. .spyOn(ShareLink, 'deleteMany')
  989. .mockImplementation();
  990. deleteManyRevisionSpy = jest
  991. .spyOn(Revision, 'deleteMany')
  992. .mockImplementation();
  993. deleteManyPageSpy = jest.spyOn(Page, 'deleteMany').mockImplementation();
  994. removeAllAttachmentsSpy = jest
  995. .spyOn(crowi.attachmentService, 'removeAllAttachments')
  996. .mockImplementation();
  997. });
  998. test('deleteCompletelyOperation', async () => {
  999. await crowi.pageService.deleteCompletelyOperation(
  1000. [parentForDeleteCompletely._id],
  1001. [parentForDeleteCompletely.path],
  1002. {},
  1003. );
  1004. // bookmarks should not be deleted
  1005. expect(deleteManyBookmarkSpy).not.toHaveBeenCalled();
  1006. expect(deleteManyCommentSpy).toHaveBeenCalledWith({
  1007. page: { $in: [parentForDeleteCompletely._id] },
  1008. });
  1009. expect(deleteManyPageTagRelationSpy).toHaveBeenCalledWith({
  1010. relatedPage: { $in: [parentForDeleteCompletely._id] },
  1011. });
  1012. expect(deleteManyShareLinkSpy).toHaveBeenCalledWith({
  1013. relatedPage: { $in: [parentForDeleteCompletely._id] },
  1014. });
  1015. expect(deleteManyRevisionSpy).toHaveBeenCalledWith({
  1016. pageId: { $in: [parentForDeleteCompletely._id] },
  1017. });
  1018. expect(deleteManyPageSpy).toHaveBeenCalledWith({
  1019. _id: { $in: [parentForDeleteCompletely._id] },
  1020. });
  1021. expect(removeAllAttachmentsSpy).toHaveBeenCalled();
  1022. });
  1023. test('delete completely without options', async () => {
  1024. await crowi.pageService.deleteCompletely(
  1025. parentForDeleteCompletely,
  1026. testUser2,
  1027. {},
  1028. false,
  1029. false,
  1030. {
  1031. ip: '::ffff:127.0.0.1',
  1032. endpoint: '/_api/v3/pages/deletecompletely',
  1033. },
  1034. );
  1035. expect(deleteCompletelyOperationSpy).toHaveBeenCalled();
  1036. expect(deleteCompletelyDescendantsWithStreamSpy).not.toHaveBeenCalled();
  1037. expect(pageEventSpy).toHaveBeenCalledWith(
  1038. 'deleteCompletely',
  1039. parentForDeleteCompletely,
  1040. testUser2,
  1041. );
  1042. });
  1043. test('delete completely with isRecursively', async () => {
  1044. await crowi.pageService.deleteCompletely(
  1045. parentForDeleteCompletely,
  1046. testUser2,
  1047. {},
  1048. true,
  1049. false,
  1050. {
  1051. ip: '::ffff:127.0.0.1',
  1052. endpoint: '/_api/v3/pages/deletecompletely',
  1053. },
  1054. );
  1055. expect(deleteCompletelyOperationSpy).toHaveBeenCalled();
  1056. expect(deleteCompletelyDescendantsWithStreamSpy).toHaveBeenCalled();
  1057. expect(pageEventSpy).toHaveBeenCalledWith(
  1058. 'deleteCompletely',
  1059. parentForDeleteCompletely,
  1060. testUser2,
  1061. );
  1062. });
  1063. });
  1064. });
  1065. describe('revert page', () => {
  1066. let getRevertDeletedPageNameSpy;
  1067. let findByPathSpy;
  1068. let findSpy;
  1069. let deleteCompletelySpy;
  1070. let revertDeletedDescendantsWithStreamSpy;
  1071. beforeEach(async () => {
  1072. getRevertDeletedPageNameSpy = jest.spyOn(
  1073. Page,
  1074. 'getRevertDeletedPageName',
  1075. );
  1076. deleteCompletelySpy = jest
  1077. .spyOn(crowi.pageService, 'deleteCompletely')
  1078. .mockImplementation();
  1079. revertDeletedDescendantsWithStreamSpy = jest
  1080. .spyOn(crowi.pageService, 'revertDeletedDescendantsWithStream')
  1081. .mockImplementation();
  1082. });
  1083. test('revert deleted page when the redirect from page exists', async () => {
  1084. const resultPage = await crowi.pageService.revertDeletedPage(
  1085. parentForRevert1,
  1086. testUser2,
  1087. {},
  1088. false,
  1089. {
  1090. ip: '::ffff:127.0.0.1',
  1091. endpoint: '/_api/v3/pages/revert',
  1092. },
  1093. );
  1094. expect(getRevertDeletedPageNameSpy).toHaveBeenCalledWith(
  1095. parentForRevert1.path,
  1096. );
  1097. expect(revertDeletedDescendantsWithStreamSpy).not.toHaveBeenCalled();
  1098. expect(resultPage.path).toBe('/parentForRevert1');
  1099. expect(resultPage.lastUpdateUser._id).toEqual(testUser2._id);
  1100. expect(resultPage.status).toBe(Page.STATUS_PUBLISHED);
  1101. expect(resultPage.deleteUser).toBeNull();
  1102. expect(resultPage.deletedAt).toBeNull();
  1103. });
  1104. test('revert deleted page when the redirect from page does not exist', async () => {
  1105. findByPathSpy = jest.spyOn(Page, 'findByPath').mockImplementation(() => {
  1106. return null;
  1107. });
  1108. const resultPage = await crowi.pageService.revertDeletedPage(
  1109. parentForRevert2,
  1110. testUser2,
  1111. {},
  1112. true,
  1113. {
  1114. ip: '::ffff:127.0.0.1',
  1115. endpoint: '/_api/v3/pages/revert',
  1116. },
  1117. );
  1118. expect(getRevertDeletedPageNameSpy).toHaveBeenCalledWith(
  1119. parentForRevert2.path,
  1120. );
  1121. expect(findByPathSpy).toHaveBeenCalledWith('/parentForRevert2');
  1122. expect(deleteCompletelySpy).not.toHaveBeenCalled();
  1123. expect(revertDeletedDescendantsWithStreamSpy).toHaveBeenCalled();
  1124. expect(resultPage.path).toBe('/parentForRevert2');
  1125. expect(resultPage.lastUpdateUser._id).toEqual(testUser2._id);
  1126. expect(resultPage.status).toBe(Page.STATUS_PUBLISHED);
  1127. expect(resultPage.deleteUser).toBeNull();
  1128. expect(resultPage.deletedAt).toBeNull();
  1129. });
  1130. test('revert deleted descendants', async () => {
  1131. await crowi.pageService.revertDeletedDescendants(
  1132. [childForRevert],
  1133. testUser2,
  1134. );
  1135. const resultPage = await Page.findOne({ path: '/parentForRevert/child' });
  1136. const revrtedFromPage = await Page.findOne({
  1137. path: '/trash/parentForRevert/child',
  1138. });
  1139. const revrtedFromPageRevision = await Revision.findOne({
  1140. pageId: resultPage._id,
  1141. });
  1142. expect(getRevertDeletedPageNameSpy).toHaveBeenCalledWith(
  1143. childForRevert.path,
  1144. );
  1145. expect(resultPage.path).toBe('/parentForRevert/child');
  1146. expect(resultPage.lastUpdateUser._id).toEqual(testUser2._id);
  1147. expect(resultPage.status).toBe(Page.STATUS_PUBLISHED);
  1148. expect(resultPage.deleteUser).toBeNull();
  1149. expect(resultPage.deletedAt).toBeNull();
  1150. expect(revrtedFromPage).toBeNull();
  1151. expect(revrtedFromPageRevision).toBeNull();
  1152. });
  1153. });
  1154. describe('getParentAndFillAncestors', () => {
  1155. test('return parent if exist', async () => {
  1156. const page1 = await Page.findOne({ path: '/PAF1' });
  1157. const parent = await crowi.pageService.getParentAndFillAncestorsByUser(
  1158. dummyUser1,
  1159. page1.path,
  1160. );
  1161. expect(parent).toBeTruthy();
  1162. expect(page1.parent).toStrictEqual(parent._id);
  1163. });
  1164. test('create parent and ancestors when they do not exist, and return the new parent', async () => {
  1165. const path1 = '/emp_anc1';
  1166. const path2 = '/emp_anc1/emp_anc2';
  1167. const path3 = '/emp_anc1/emp_anc2/PAF2';
  1168. const _page1 = await Page.findOne({ path: path1 }); // not exist
  1169. const _page2 = await Page.findOne({ path: path2 }); // not exist
  1170. const _page3 = await Page.findOne({ path: path3 }); // not exist
  1171. expect(_page1).toBeNull();
  1172. expect(_page2).toBeNull();
  1173. expect(_page3).toBeNull();
  1174. const parent = await crowi.pageService.getParentAndFillAncestorsByUser(
  1175. dummyUser1,
  1176. path3,
  1177. );
  1178. const page1 = await Page.findOne({ path: path1 });
  1179. const page2 = await Page.findOne({ path: path2 });
  1180. const page3 = await Page.findOne({ path: path3 });
  1181. expect(parent._id).toStrictEqual(page2._id);
  1182. expect(parent.path).toStrictEqual(page2.path);
  1183. expect(parent.parent).toStrictEqual(page2.parent);
  1184. expect(parent).toBeTruthy();
  1185. expect(page1).toBeTruthy();
  1186. expect(page2).toBeTruthy();
  1187. expect(page3).toBeNull();
  1188. expect(page1.parent).toStrictEqual(rootPage._id);
  1189. expect(page2.parent).toStrictEqual(page1._id);
  1190. });
  1191. test('return parent even if the parent page is empty', async () => {
  1192. const path1 = '/emp_anc3';
  1193. const path2 = '/emp_anc3/PAF3';
  1194. const _page1 = await Page.findOne({ path: path1, isEmpty: true });
  1195. const _page2 = await Page.findOne({ path: path2, isEmpty: false });
  1196. expect(_page1).toBeTruthy();
  1197. expect(_page2).toBeTruthy();
  1198. const parent = await crowi.pageService.getParentAndFillAncestorsByUser(
  1199. dummyUser1,
  1200. _page2.path,
  1201. );
  1202. const page1 = await Page.findOne({ path: path1, isEmpty: true }); // parent
  1203. const page2 = await Page.findOne({ path: path2, isEmpty: false });
  1204. // check for the parent (should be the same as page1)
  1205. expect(parent._id).toStrictEqual(page1._id);
  1206. expect(parent.path).toStrictEqual(page1.path);
  1207. expect(parent.parent).toStrictEqual(page1.parent);
  1208. expect(page1.parent).toStrictEqual(rootPage._id);
  1209. expect(page2.parent).toStrictEqual(page1._id);
  1210. });
  1211. test("should find parent while NOT updating private legacy page's parent", async () => {
  1212. const path1 = '/emp_anc4';
  1213. const path2 = '/emp_anc4/PAF4';
  1214. const _page1 = await Page.findOne({
  1215. path: path1,
  1216. isEmpty: true,
  1217. grant: Page.GRANT_PUBLIC,
  1218. });
  1219. const _page2 = await Page.findOne({
  1220. path: path2,
  1221. isEmpty: false,
  1222. grant: Page.GRANT_PUBLIC,
  1223. });
  1224. const _page3 = await Page.findOne({
  1225. path: path1,
  1226. isEmpty: false,
  1227. grant: Page.GRANT_OWNER,
  1228. });
  1229. expect(_page1).toBeTruthy();
  1230. expect(_page2).toBeTruthy();
  1231. expect(_page3).toBeTruthy();
  1232. expect(_page3.parent).toBeNull();
  1233. const parent = await crowi.pageService.getParentAndFillAncestorsByUser(
  1234. dummyUser1,
  1235. _page2.path,
  1236. );
  1237. const page1 = await Page.findOne({
  1238. path: path1,
  1239. isEmpty: true,
  1240. grant: Page.GRANT_PUBLIC,
  1241. });
  1242. const page2 = await Page.findOne({
  1243. path: path2,
  1244. isEmpty: false,
  1245. grant: Page.GRANT_PUBLIC,
  1246. });
  1247. const page3 = await Page.findOne({
  1248. path: path1,
  1249. isEmpty: false,
  1250. grant: Page.GRANT_OWNER,
  1251. });
  1252. expect(page1).toBeTruthy();
  1253. expect(page2).toBeTruthy();
  1254. expect(page3).toBeTruthy();
  1255. expect(page3.parent).toBeNull(); // parent property of page in private legacy pages should be null
  1256. expect(page1._id).toStrictEqual(parent._id);
  1257. expect(page2.parent).toStrictEqual(parent._id);
  1258. });
  1259. test('should find parent while NOT creating unnecessary empty pages with all v4 public pages', async () => {
  1260. // All pages does not have parent (v4 schema)
  1261. const _pageA = await Page.findOne({
  1262. path: '/get_parent_A',
  1263. grant: Page.GRANT_PUBLIC,
  1264. isEmpty: false,
  1265. parent: null,
  1266. });
  1267. const _pageAB = await Page.findOne({
  1268. path: '/get_parent_A/get_parent_B',
  1269. grant: Page.GRANT_PUBLIC,
  1270. isEmpty: false,
  1271. parent: null,
  1272. });
  1273. const _emptyA = await Page.findOne({
  1274. path: '/get_parent_A',
  1275. grant: Page.GRANT_PUBLIC,
  1276. isEmpty: true,
  1277. });
  1278. const _emptyAB = await Page.findOne({
  1279. path: '/get_parent_A/get_parent_B',
  1280. grant: Page.GRANT_PUBLIC,
  1281. isEmpty: true,
  1282. });
  1283. expect(_pageA).not.toBeNull();
  1284. expect(_pageAB).not.toBeNull();
  1285. expect(_emptyA).toBeNull();
  1286. expect(_emptyAB).toBeNull();
  1287. const parent = await crowi.pageService.getParentAndFillAncestorsByUser(
  1288. dummyUser1,
  1289. '/get_parent_A/get_parent_B/get_parent_C',
  1290. );
  1291. const pageA = await Page.findOne({
  1292. path: '/get_parent_A',
  1293. grant: Page.GRANT_PUBLIC,
  1294. isEmpty: false,
  1295. });
  1296. const pageAB = await Page.findOne({
  1297. path: '/get_parent_A/get_parent_B',
  1298. grant: Page.GRANT_PUBLIC,
  1299. isEmpty: false,
  1300. });
  1301. const emptyA = await Page.findOne({
  1302. path: '/get_parent_A',
  1303. grant: Page.GRANT_PUBLIC,
  1304. isEmpty: true,
  1305. });
  1306. const emptyAB = await Page.findOne({
  1307. path: '/get_parent_A/get_parent_B',
  1308. grant: Page.GRANT_PUBLIC,
  1309. isEmpty: true,
  1310. });
  1311. // -- Check existance
  1312. expect(parent).not.toBeNull();
  1313. expect(pageA).not.toBeNull();
  1314. expect(pageAB).not.toBeNull();
  1315. expect(emptyA).toBeNull();
  1316. expect(emptyAB).toBeNull();
  1317. // -- Check parent
  1318. expect(pageA.parent).not.toBeNull();
  1319. expect(pageAB.parent).not.toBeNull();
  1320. });
  1321. test('should find parent while NOT creating unnecessary empty pages with some v5 public pages', async () => {
  1322. const _pageC = await Page.findOne({
  1323. path: '/get_parent_C',
  1324. grant: Page.GRANT_PUBLIC,
  1325. isEmpty: false,
  1326. parent: { $ne: null },
  1327. });
  1328. const _pageCD = await Page.findOne({
  1329. path: '/get_parent_C/get_parent_D',
  1330. grant: Page.GRANT_PUBLIC,
  1331. isEmpty: false,
  1332. });
  1333. const _emptyC = await Page.findOne({
  1334. path: '/get_parent_C',
  1335. grant: Page.GRANT_PUBLIC,
  1336. isEmpty: true,
  1337. });
  1338. const _emptyCD = await Page.findOne({
  1339. path: '/get_parent_C/get_parent_D',
  1340. grant: Page.GRANT_PUBLIC,
  1341. isEmpty: true,
  1342. });
  1343. expect(_pageC).not.toBeNull();
  1344. expect(_pageCD).not.toBeNull();
  1345. expect(_emptyC).toBeNull();
  1346. expect(_emptyCD).toBeNull();
  1347. const parent = await crowi.pageService.getParentAndFillAncestorsByUser(
  1348. dummyUser1,
  1349. '/get_parent_C/get_parent_D/get_parent_E',
  1350. );
  1351. const pageC = await Page.findOne({
  1352. path: '/get_parent_C',
  1353. grant: Page.GRANT_PUBLIC,
  1354. isEmpty: false,
  1355. });
  1356. const pageCD = await Page.findOne({
  1357. path: '/get_parent_C/get_parent_D',
  1358. grant: Page.GRANT_PUBLIC,
  1359. isEmpty: false,
  1360. });
  1361. const emptyC = await Page.findOne({
  1362. path: '/get_parent_C',
  1363. grant: Page.GRANT_PUBLIC,
  1364. isEmpty: true,
  1365. });
  1366. const emptyCD = await Page.findOne({
  1367. path: '/get_parent_C/get_parent_D',
  1368. grant: Page.GRANT_PUBLIC,
  1369. isEmpty: true,
  1370. });
  1371. // -- Check existance
  1372. expect(parent).not.toBeNull();
  1373. expect(pageC).not.toBeNull();
  1374. expect(pageCD).not.toBeNull();
  1375. expect(emptyC).toBeNull();
  1376. expect(emptyCD).toBeNull();
  1377. // -- Check parent attribute
  1378. expect(pageC.parent).toStrictEqual(rootPage._id);
  1379. expect(pageCD.parent).toStrictEqual(pageC._id);
  1380. // -- Check the found parent
  1381. expect(parent.toObject()).toStrictEqual(pageCD.toObject());
  1382. });
  1383. });
  1384. });