page.test.js 50 KB

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