Просмотр исходного кода

biome autofix for service integration tests

Futa Arai 8 месяцев назад
Родитель
Сommit
9281116616

+ 1 - 0
apps/app/.eslintrc.js

@@ -25,6 +25,7 @@ module.exports = {
     'test/integration/middlewares/**',
     'test/integration/migrations/**',
     'test/integration/models/**',
+    'test/integration/service/**',
     'test/integration/setup.js',
   ],
   settings: {

+ 142 - 69
apps/app/test/integration/service/external-user-group-sync.test.ts

@@ -2,7 +2,10 @@ import type { IUserHasId } from '@growi/core';
 import mongoose, { Types } from 'mongoose';
 
 import {
-  ExternalGroupProviderType, ExternalUserGroupTreeNode, IExternalUserGroup, IExternalUserGroupHasId,
+  ExternalGroupProviderType,
+  type ExternalUserGroupTreeNode,
+  type IExternalUserGroup,
+  type IExternalUserGroupHasId,
 } from '../../../src/features/external-user-group/interfaces/external-user-group';
 import ExternalUserGroup from '../../../src/features/external-user-group/server/models/external-user-group';
 import ExternalUserGroupRelation from '../../../src/features/external-user-group/server/models/external-user-group-relation';
@@ -15,7 +18,6 @@ import { getInstance } from '../setup-crowi';
 
 // dummy class to implement generateExternalUserGroupTrees which returns test data
 class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
-
   constructor(s2sMessagingService, socketIoService) {
     super('ldap', s2sMessagingService, socketIoService);
     this.authProviderType = ExternalGroupProviderType.ldap;
@@ -24,12 +26,14 @@ class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
   async generateExternalUserGroupTrees(): Promise<ExternalUserGroupTreeNode[]> {
     const childNode: ExternalUserGroupTreeNode = {
       id: 'cn=childGroup,ou=groups,dc=example,dc=org',
-      userInfos: [{
-        id: 'childGroupUser',
-        username: 'childGroupUser',
-        name: 'Child Group User',
-        email: 'user@childgroup.com',
-      }],
+      userInfos: [
+        {
+          id: 'childGroupUser',
+          username: 'childGroupUser',
+          name: 'Child Group User',
+          email: 'user@childgroup.com',
+        },
+      ],
       childGroupNodes: [],
       name: 'childGroup',
       description: 'this is a child group',
@@ -37,11 +41,13 @@ class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
     const parentNode: ExternalUserGroupTreeNode = {
       id: 'cn=parentGroup,ou=groups,dc=example,dc=org',
       // name is undefined
-      userInfos: [{
-        id: 'parentGroupUser',
-        username: 'parentGroupUser',
-        email: 'user@parentgroup.com',
-      }],
+      userInfos: [
+        {
+          id: 'parentGroupUser',
+          username: 'parentGroupUser',
+          email: 'user@parentgroup.com',
+        },
+      ],
       childGroupNodes: [childNode],
       name: 'parentGroup',
       description: 'this is a parent group',
@@ -49,11 +55,13 @@ class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
     const grandParentNode: ExternalUserGroupTreeNode = {
       id: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
       // email is undefined
-      userInfos: [{
-        id: 'grandParentGroupUser',
-        username: 'grandParentGroupUser',
-        name: 'Grand Parent Group User',
-      }],
+      userInfos: [
+        {
+          id: 'grandParentGroupUser',
+          username: 'grandParentGroupUser',
+          name: 'Grand Parent Group User',
+        },
+      ],
       childGroupNodes: [parentNode],
       name: 'grandParentGroup',
       description: 'this is a grand parent group',
@@ -61,12 +69,14 @@ class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
 
     const previouslySyncedNode: ExternalUserGroupTreeNode = {
       id: 'cn=previouslySyncedGroup,ou=groups,dc=example,dc=org',
-      userInfos: [{
-        id: 'previouslySyncedGroupUser',
-        username: 'previouslySyncedGroupUser',
-        name: 'Root Group User',
-        email: 'user@previouslySyncedgroup.com',
-      }],
+      userInfos: [
+        {
+          id: 'previouslySyncedGroupUser',
+          username: 'previouslySyncedGroupUser',
+          name: 'Root Group User',
+          email: 'user@previouslySyncedgroup.com',
+        },
+      ],
       childGroupNodes: [],
       name: 'previouslySyncedGroup',
       description: 'this is a previouslySynced group',
@@ -74,12 +84,14 @@ class TestExternalUserGroupSyncService extends ExternalUserGroupSyncService {
 
     return [grandParentNode, previouslySyncedNode];
   }
-
 }
 
 const testService = new TestExternalUserGroupSyncService(null, null);
 
-const checkGroup = (group: IExternalUserGroupHasId, expected: Omit<IExternalUserGroup, 'createdAt'>) => {
+const checkGroup = (
+  group: IExternalUserGroupHasId,
+  expected: Omit<IExternalUserGroup, 'createdAt'>,
+) => {
   const actual = {
     name: group.name,
     parent: group.parent,
@@ -90,8 +102,10 @@ const checkGroup = (group: IExternalUserGroupHasId, expected: Omit<IExternalUser
   expect(actual).toStrictEqual(expected);
 };
 
-const checkSync = async(autoGenerateUserOnGroupSync = true) => {
-  const grandParentGroup = await ExternalUserGroup.findOne({ name: 'grandParentGroup' });
+const checkSync = async (autoGenerateUserOnGroupSync = true) => {
+  const grandParentGroup = await ExternalUserGroup.findOne({
+    name: 'grandParentGroup',
+  });
   checkGroup(grandParentGroup, {
     externalId: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
     name: 'grandParentGroup',
@@ -118,7 +132,9 @@ const checkSync = async(autoGenerateUserOnGroupSync = true) => {
     parent: parentGroup._id,
   });
 
-  const previouslySyncedGroup = await ExternalUserGroup.findOne({ name: 'previouslySyncedGroup' });
+  const previouslySyncedGroup = await ExternalUserGroup.findOne({
+    name: 'previouslySyncedGroup',
+  });
   checkGroup(previouslySyncedGroup, {
     externalId: 'cn=previouslySyncedGroup,ou=groups,dc=example,dc=org',
     name: 'previouslySyncedGroup',
@@ -127,49 +143,79 @@ const checkSync = async(autoGenerateUserOnGroupSync = true) => {
     parent: null,
   });
 
-  const grandParentGroupRelations = await ExternalUserGroupRelation
-    .find({ relatedGroup: grandParentGroup._id });
-  const parentGroupRelations = await ExternalUserGroupRelation
-    .find({ relatedGroup: parentGroup._id });
-  const childGroupRelations = await ExternalUserGroupRelation
-    .find({ relatedGroup: childGroup._id });
-  const previouslySyncedGroupRelations = await ExternalUserGroupRelation
-    .find({ relatedGroup: previouslySyncedGroup._id });
+  const grandParentGroupRelations = await ExternalUserGroupRelation.find({
+    relatedGroup: grandParentGroup._id,
+  });
+  const parentGroupRelations = await ExternalUserGroupRelation.find({
+    relatedGroup: parentGroup._id,
+  });
+  const childGroupRelations = await ExternalUserGroupRelation.find({
+    relatedGroup: childGroup._id,
+  });
+  const previouslySyncedGroupRelations = await ExternalUserGroupRelation.find({
+    relatedGroup: previouslySyncedGroup._id,
+  });
 
   if (autoGenerateUserOnGroupSync) {
     expect(grandParentGroupRelations.length).toBe(3);
-    const populatedGrandParentGroupRelations = await Promise.all(grandParentGroupRelations.map((relation) => {
-      return relation.populate<{relatedUser: IUserHasId}>('relatedUser');
-    }));
-    expect(populatedGrandParentGroupRelations[0].relatedUser.username).toBe('grandParentGroupUser');
-    expect(populatedGrandParentGroupRelations[1].relatedUser.username).toBe('parentGroupUser');
-    expect(populatedGrandParentGroupRelations[2].relatedUser.username).toBe('childGroupUser');
+    const populatedGrandParentGroupRelations = await Promise.all(
+      grandParentGroupRelations.map((relation) => {
+        return relation.populate<{ relatedUser: IUserHasId }>('relatedUser');
+      }),
+    );
+    expect(populatedGrandParentGroupRelations[0].relatedUser.username).toBe(
+      'grandParentGroupUser',
+    );
+    expect(populatedGrandParentGroupRelations[1].relatedUser.username).toBe(
+      'parentGroupUser',
+    );
+    expect(populatedGrandParentGroupRelations[2].relatedUser.username).toBe(
+      'childGroupUser',
+    );
 
     expect(parentGroupRelations.length).toBe(2);
-    const populatedParentGroupRelations = await Promise.all(parentGroupRelations.map((relation) => {
-      return relation.populate<{relatedUser: IUserHasId}>('relatedUser');
-    }));
-    expect(populatedParentGroupRelations[0].relatedUser.username).toBe('parentGroupUser');
-    expect(populatedParentGroupRelations[1].relatedUser.username).toBe('childGroupUser');
+    const populatedParentGroupRelations = await Promise.all(
+      parentGroupRelations.map((relation) => {
+        return relation.populate<{ relatedUser: IUserHasId }>('relatedUser');
+      }),
+    );
+    expect(populatedParentGroupRelations[0].relatedUser.username).toBe(
+      'parentGroupUser',
+    );
+    expect(populatedParentGroupRelations[1].relatedUser.username).toBe(
+      'childGroupUser',
+    );
 
     expect(childGroupRelations.length).toBe(1);
-    const childGroupUser = (await childGroupRelations[0].populate<{relatedUser: IUserHasId}>('relatedUser'))?.relatedUser;
+    const childGroupUser = (
+      await childGroupRelations[0].populate<{ relatedUser: IUserHasId }>(
+        'relatedUser',
+      )
+    )?.relatedUser;
     expect(childGroupUser?.username).toBe('childGroupUser');
 
     expect(previouslySyncedGroupRelations.length).toBe(1);
-    const previouslySyncedGroupUser = (await previouslySyncedGroupRelations[0].populate<{relatedUser: IUserHasId}>('relatedUser'))?.relatedUser;
-    expect(previouslySyncedGroupUser?.username).toBe('previouslySyncedGroupUser');
+    const previouslySyncedGroupUser = (
+      await previouslySyncedGroupRelations[0].populate<{
+        relatedUser: IUserHasId;
+      }>('relatedUser')
+    )?.relatedUser;
+    expect(previouslySyncedGroupUser?.username).toBe(
+      'previouslySyncedGroupUser',
+    );
 
     const userPages = await mongoose.model('Page').find({
       path: {
         $in: [
-          '/user/childGroupUser', '/user/parentGroupUser', '/user/grandParentGroupUser', '/user/previouslySyncedGroupUser',
+          '/user/childGroupUser',
+          '/user/parentGroupUser',
+          '/user/grandParentGroupUser',
+          '/user/previouslySyncedGroupUser',
         ],
       },
     });
     expect(userPages.length).toBe(4);
-  }
-  else {
+  } else {
     expect(grandParentGroupRelations.length).toBe(0);
     expect(parentGroupRelations.length).toBe(0);
     expect(childGroupRelations.length).toBe(0);
@@ -180,14 +226,14 @@ const checkSync = async(autoGenerateUserOnGroupSync = true) => {
 describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
   let crowi;
 
-  beforeAll(async() => {
+  beforeAll(async () => {
     crowi = await getInstance();
     await configManager.updateConfig('app:isV5Compatible', true);
     const passportService = new PassportService(crowi);
     instanciateExternalAccountService(passportService);
   });
 
-  beforeEach(async() => {
+  beforeEach(async () => {
     await ExternalUserGroup.create({
       name: 'nameBeforeEdit',
       description: 'this is a description before edit',
@@ -196,16 +242,38 @@ describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
     });
   });
 
-  afterEach(async() => {
+  afterEach(async () => {
     await ExternalUserGroup.deleteMany();
     await ExternalUserGroupRelation.deleteMany();
-    await mongoose.model('User')
-      .deleteMany({ username: { $in: ['childGroupUser', 'parentGroupUser', 'grandParentGroupUser', 'previouslySyncedGroupUser'] } });
-    await ExternalAccount.deleteMany({ accountId: { $in: ['childGroupUser', 'parentGroupUser', 'grandParentGroupUser', 'previouslySyncedGroupUser'] } });
+    await mongoose
+      .model('User')
+      .deleteMany({
+        username: {
+          $in: [
+            'childGroupUser',
+            'parentGroupUser',
+            'grandParentGroupUser',
+            'previouslySyncedGroupUser',
+          ],
+        },
+      });
+    await ExternalAccount.deleteMany({
+      accountId: {
+        $in: [
+          'childGroupUser',
+          'parentGroupUser',
+          'grandParentGroupUser',
+          'previouslySyncedGroupUser',
+        ],
+      },
+    });
     await mongoose.model('Page').deleteMany({
       path: {
         $in: [
-          '/user/childGroupUser', '/user/parentGroupUser', '/user/grandParentGroupUser', '/user/previouslySyncedGroupUser',
+          '/user/childGroupUser',
+          '/user/parentGroupUser',
+          '/user/grandParentGroupUser',
+          '/user/previouslySyncedGroupUser',
         ],
       },
     });
@@ -217,12 +285,12 @@ describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
       'external-user-group:ldap:preserveDeletedGroups': false,
     };
 
-    beforeAll(async() => {
+    beforeAll(async () => {
       await configManager.updateConfigs(configParams);
     });
 
     // eslint-disable-next-line jest/expect-expect
-    it('syncs groups with new users', async() => {
+    it('syncs groups with new users', async () => {
       await testService.syncExternalUserGroups();
       await checkSync();
     });
@@ -234,12 +302,12 @@ describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
       'external-user-group:ldap:preserveDeletedGroups': true,
     };
 
-    beforeAll(async() => {
+    beforeAll(async () => {
       await configManager.updateConfigs(configParams);
     });
 
     // eslint-disable-next-line jest/expect-expect
-    it('syncs groups without new users', async() => {
+    it('syncs groups without new users', async () => {
       await testService.syncExternalUserGroups();
       await checkSync(false);
     });
@@ -251,7 +319,7 @@ describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
       'external-user-group:ldap:preserveDeletedGroups': false,
     };
 
-    beforeAll(async() => {
+    beforeAll(async () => {
       await configManager.updateConfigs(configParams);
 
       const groupId = new Types.ObjectId();
@@ -263,11 +331,16 @@ describe('ExternalUserGroupSyncService.syncExternalUserGroups', () => {
         externalId: 'cn=nonExistentGroup,ou=groups,dc=example,dc=org',
         provider: 'ldap',
       });
-      await mongoose.model('User').create({ _id: userId, username: 'nonExistentGroupUser' });
-      await ExternalUserGroupRelation.create({ relatedUser: userId, relatedGroup: groupId });
+      await mongoose
+        .model('User')
+        .create({ _id: userId, username: 'nonExistentGroupUser' });
+      await ExternalUserGroupRelation.create({
+        relatedUser: userId,
+        relatedGroup: groupId,
+      });
     });
 
-    it('syncs groups and deletes groups that do not exist externally', async() => {
+    it('syncs groups and deletes groups that do not exist externally', async () => {
       await testService.syncExternalUserGroups();
       await checkSync();
       expect(await ExternalUserGroup.countDocuments()).toBe(4);

+ 77 - 43
apps/app/test/integration/service/ldap-user-group-sync.test.ts

@@ -1,4 +1,4 @@
-import ldap, { Client } from 'ldapjs';
+import ldap, { type Client } from 'ldapjs';
 
 import { LdapUserGroupSyncService } from '../../../src/features/external-user-group/server/service/ldap-user-group-sync';
 import { configManager } from '../../../src/server/service/config-manager';
@@ -18,7 +18,8 @@ describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
     'external-user-group:ldap:groupDescriptionAttribute': 'description',
     'external-user-group:ldap:groupMembershipAttributeType': 'DN',
     'external-user-group:ldap:groupSearchBase': 'ou=groups,dc=example,dc=org',
-    'security:passport-ldap:serverUrl': 'ldap://openldap:1389/dc=example,dc=org',
+    'security:passport-ldap:serverUrl':
+      'ldap://openldap:1389/dc=example,dc=org',
   };
 
   jest.mock('../../../src/server/service/ldap');
@@ -26,25 +27,31 @@ describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
   const mockLdapSearch = jest.spyOn(ldapService, 'search');
   const mockLdapCreateClient = jest.spyOn(ldap, 'createClient');
 
-  beforeAll(async() => {
+  beforeAll(async () => {
     crowi = await getInstance();
     await configManager.updateConfigs(configParams, { skipPubsub: true });
 
     mockBind.mockImplementation(() => {
       return Promise.resolve();
     });
-    mockLdapCreateClient.mockImplementation(() => { return {} as Client });
+    mockLdapCreateClient.mockImplementation(() => {
+      return {} as Client;
+    });
 
     const passportService = new PassportService(crowi);
-    ldapUserGroupSyncService = new LdapUserGroupSyncService(passportService, null, null);
+    ldapUserGroupSyncService = new LdapUserGroupSyncService(
+      passportService,
+      null,
+      null,
+    );
   });
 
   describe('When there is no circular reference in group tree', () => {
-    it('creates ExternalUserGroupTrees', async() => {
+    it('creates ExternalUserGroupTrees', async () => {
       // mock search on LDAP server
       mockLdapSearch.mockImplementation((filter, base) => {
         if (base === 'ou=groups,dc=example,dc=org') {
-        // search groups
+          // search groups
           return Promise.resolve([
             {
               objectName: 'cn=childGroup,ou=groups,dc=example,dc=org',
@@ -64,7 +71,10 @@ describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
                 { type: 'description', values: ['this is a parent group'] },
                 {
                   type: 'member',
-                  values: ['cn=childGroup,ou=groups,dc=example,dc=org', 'cn=parentGroupUser,ou=users,dc=example,dc=org'],
+                  values: [
+                    'cn=childGroup,ou=groups,dc=example,dc=org',
+                    'cn=parentGroupUser,ou=users,dc=example,dc=org',
+                  ],
                 },
               ],
             },
@@ -73,10 +83,16 @@ describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
               objectName: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
               attributes: [
                 { type: 'cn', values: ['grandParentGroup'] },
-                { type: 'description', values: ['this is a grand parent group'] },
+                {
+                  type: 'description',
+                  values: ['this is a grand parent group'],
+                },
                 {
                   type: 'member',
-                  values: ['cn=parentGroup,ou=groups,dc=example,dc=org', 'cn=grandParentGroupUser,ou=users,dc=example,dc=org'],
+                  values: [
+                    'cn=parentGroup,ou=groups,dc=example,dc=org',
+                    'cn=grandParentGroupUser,ou=users,dc=example,dc=org',
+                  ],
                 },
               ],
             },
@@ -95,7 +111,7 @@ describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
           ]);
         }
         if (base === 'cn=childGroupUser,ou=users,dc=example,dc=org') {
-        // search childGroupUser
+          // search childGroupUser
           return Promise.resolve([
             {
               objectName: 'cn=childGroupUser,ou=users,dc=example,dc=org',
@@ -149,44 +165,53 @@ describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
         return Promise.reject(new Error('not found'));
       });
 
-      const rootNodes = await ldapUserGroupSyncService?.generateExternalUserGroupTrees();
+      const rootNodes =
+        await ldapUserGroupSyncService?.generateExternalUserGroupTrees();
 
       expect(rootNodes?.length).toBe(2);
 
       // check grandParentGroup
-      const grandParentNode = rootNodes?.find(node => node.id === 'cn=grandParentGroup,ou=groups,dc=example,dc=org');
+      const grandParentNode = rootNodes?.find(
+        (node) => node.id === 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
+      );
       const expectedChildNode = {
         id: 'cn=childGroup,ou=groups,dc=example,dc=org',
-        userInfos: [{
-          id: 'childGroupUser',
-          username: 'childGroupUser',
-          name: 'Child Group User',
-          email: 'user@childGroup.com',
-        }],
+        userInfos: [
+          {
+            id: 'childGroupUser',
+            username: 'childGroupUser',
+            name: 'Child Group User',
+            email: 'user@childGroup.com',
+          },
+        ],
         childGroupNodes: [],
         name: 'childGroup',
         description: 'this is a child group',
       };
       const expectedParentNode = {
         id: 'cn=parentGroup,ou=groups,dc=example,dc=org',
-        userInfos: [{
-          id: 'parentGroupUser',
-          username: 'parentGroupUser',
-          name: 'Parent Group User',
-          email: 'user@parentGroup.com',
-        }],
+        userInfos: [
+          {
+            id: 'parentGroupUser',
+            username: 'parentGroupUser',
+            name: 'Parent Group User',
+            email: 'user@parentGroup.com',
+          },
+        ],
         childGroupNodes: [expectedChildNode],
         name: 'parentGroup',
         description: 'this is a parent group',
       };
       const expectedGrandParentNode = {
         id: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
-        userInfos: [{
-          id: 'grandParentGroupUser',
-          username: 'grandParentGroupUser',
-          name: 'Grand Parent Group User',
-          email: 'user@grandParentGroup.com',
-        }],
+        userInfos: [
+          {
+            id: 'grandParentGroupUser',
+            username: 'grandParentGroupUser',
+            name: 'Grand Parent Group User',
+            email: 'user@grandParentGroup.com',
+          },
+        ],
         childGroupNodes: [expectedParentNode],
         name: 'grandParentGroup',
         description: 'this is a grand parent group',
@@ -194,15 +219,19 @@ describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
       expect(grandParentNode).toStrictEqual(expectedGrandParentNode);
 
       // check rootGroup
-      const rootNode = rootNodes?.find(node => node.id === 'cn=rootGroup,ou=groups,dc=example,dc=org');
+      const rootNode = rootNodes?.find(
+        (node) => node.id === 'cn=rootGroup,ou=groups,dc=example,dc=org',
+      );
       const expectedRootNode = {
         id: 'cn=rootGroup,ou=groups,dc=example,dc=org',
-        userInfos: [{
-          id: 'rootGroupUser',
-          username: 'rootGroupUser',
-          name: 'Root Group User',
-          email: 'user@rootGroup.com',
-        }],
+        userInfos: [
+          {
+            id: 'rootGroupUser',
+            username: 'rootGroupUser',
+            name: 'Root Group User',
+            email: 'user@rootGroup.com',
+          },
+        ],
         childGroupNodes: [],
         name: 'rootGroup',
         description: 'this is a root group',
@@ -212,13 +241,13 @@ describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
   });
 
   describe('When there is a circular reference in group tree', () => {
-    it('rejects creating ExternalUserGroupTrees', async() => {
+    it('rejects creating ExternalUserGroupTrees', async () => {
       // mock search on LDAP server
       mockLdapSearch.mockImplementation((filter, base) => {
         if (base === 'ou=groups,dc=example,dc=org') {
-        // search groups
+          // search groups
           return Promise.resolve([
-          // childGroup and parentGroup have circular reference
+            // childGroup and parentGroup have circular reference
             {
               objectName: 'cn=childGroup,ou=groups,dc=example,dc=org',
               attributes: [
@@ -245,7 +274,10 @@ describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
               objectName: 'cn=grandParentGroup,ou=groups,dc=example,dc=org',
               attributes: [
                 { type: 'cn', values: ['grandParentGroup'] },
-                { type: 'description', values: ['this is a grand parent group'] },
+                {
+                  type: 'description',
+                  values: ['this is a grand parent group'],
+                },
                 {
                   type: 'member',
                   values: ['cn=parentGroup,ou=groups,dc=example,dc=org'],
@@ -257,7 +289,9 @@ describe('LdapUserGroupSyncService.generateExternalUserGroupTrees', () => {
         return Promise.reject(new Error('not found'));
       });
 
-      await expect(ldapUserGroupSyncService?.generateExternalUserGroupTrees()).rejects.toThrow('Circular reference inside LDAP group tree');
+      await expect(
+        ldapUserGroupSyncService?.generateExternalUserGroupTrees(),
+      ).rejects.toThrow('Circular reference inside LDAP group tree');
     });
   });
 });

Разница между файлами не показана из-за своего большого размера
+ 441 - 216
apps/app/test/integration/service/page-grant.test.ts


Разница между файлами не показана из-за своего большого размера
+ 488 - 188
apps/app/test/integration/service/page.test.js


+ 45 - 23
apps/app/test/integration/service/search/search-service.test.js

@@ -3,9 +3,8 @@
  */
 
 import mongoose from 'mongoose';
-
-import SearchService from '~/server/service/search';
 import NamedQuery from '~/server/models/named-query';
+import SearchService from '~/server/service/search';
 
 const { getInstance } = require('../../setup-crowi');
 
@@ -29,7 +28,7 @@ describe('SearchService test', () => {
     },
   };
 
-  beforeAll(async() => {
+  beforeAll(async () => {
     crowi = await getInstance();
     searchService = new SearchService(crowi);
     searchService.nqDelegators = {
@@ -37,7 +36,8 @@ describe('SearchService test', () => {
       [DEFAULT]: dummyFullTextSearchDelegator, // override with dummy full-text search delegator
     };
 
-    dummyAliasOf = 'match -notmatch "phrase" -"notphrase" prefix:/pre1 -prefix:/pre2 tag:Tag1 -tag:Tag2';
+    dummyAliasOf =
+      'match -notmatch "phrase" -"notphrase" prefix:/pre1 -prefix:/pre2 tag:Tag1 -tag:Tag2';
 
     await NamedQuery.insertMany([
       { name: 'named_query1', delegatorName: PRIVATE_LEGACY_PAGES },
@@ -48,13 +48,14 @@ describe('SearchService test', () => {
     namedQuery2 = await NamedQuery.findOne({ name: 'named_query2' });
   });
 
-
   describe('parseQueryString()', () => {
-    test('should parse queryString', async() => {
-      const queryString = 'match -notmatch "phrase" -"notphrase" prefix:/pre1 -prefix:/pre2 tag:Tag1 -tag:Tag2';
+    test('should parse queryString', async () => {
+      const queryString =
+        'match -notmatch "phrase" -"notphrase" prefix:/pre1 -prefix:/pre2 tag:Tag1 -tag:Tag2';
       const terms = await searchService.parseQueryString(queryString);
 
-      const expected = { // QueryTerms
+      const expected = {
+        // QueryTerms
         match: ['match'],
         not_match: ['notmatch'],
         phrase: ['"phrase"'],
@@ -70,11 +71,13 @@ describe('SearchService test', () => {
   });
 
   describe('parseSearchQuery()', () => {
-
-    test('should return result with delegatorName', async() => {
+    test('should return result with delegatorName', async () => {
       const queryString = '/';
       const nqName = 'named_query1';
-      const parsedQuery = await searchService.parseSearchQuery(queryString, nqName);
+      const parsedQuery = await searchService.parseSearchQuery(
+        queryString,
+        nqName,
+      );
 
       const expected = {
         queryString,
@@ -94,10 +97,13 @@ describe('SearchService test', () => {
       expect(parsedQuery).toStrictEqual(expected);
     });
 
-    test('should return result with expanded aliasOf value', async() => {
+    test('should return result with expanded aliasOf value', async () => {
       const queryString = '/';
       const nqName = 'named_query2';
-      const parsedQuery = await searchService.parseSearchQuery(queryString, nqName);
+      const parsedQuery = await searchService.parseSearchQuery(
+        queryString,
+        nqName,
+      );
       const expected = {
         queryString: dummyAliasOf,
         terms: {
@@ -117,7 +123,7 @@ describe('SearchService test', () => {
   });
 
   describe('resolve()', () => {
-    test('should resolve as full-text search delegator', async() => {
+    test('should resolve as full-text search delegator', async () => {
       const parsedQuery = {
         queryString: dummyAliasOf,
         terms: {
@@ -140,7 +146,7 @@ describe('SearchService test', () => {
       expect(typeof delegator.search).toBe('function');
     });
 
-    test('should resolve as custom search delegator', async() => {
+    test('should resolve as custom search delegator', async () => {
       const queryString = '/';
       const parsedQuery = {
         queryString,
@@ -170,12 +176,20 @@ describe('SearchService test', () => {
   });
 
   describe('searchKeyword()', () => {
-    test('should search with custom search delegator', async() => {
+    test('should search with custom search delegator', async () => {
       const Page = mongoose.model('Page');
       const User = mongoose.model('User');
       await User.insertMany([
-        { name: 'dummyuser1', username: 'dummyuser1', email: 'dummyuser1@example.com' },
-        { name: 'dummyuser2', username: 'dummyuser2', email: 'dummyuser2@example.com' },
+        {
+          name: 'dummyuser1',
+          username: 'dummyuser1',
+          email: 'dummyuser1@example.com',
+        },
+        {
+          name: 'dummyuser2',
+          username: 'dummyuser2',
+          email: 'dummyuser2@example.com',
+        },
       ]);
 
       const testUser1 = await User.findOne({ username: 'dummyuser1' });
@@ -218,14 +232,22 @@ describe('SearchService test', () => {
       const queryString = '/';
       const nqName = 'named_query1';
 
-      const [result, delegatorName] = await searchService.searchKeyword(queryString, nqName, testUser1, null, { offset: 0, limit: 100 });
-
-      const resultPaths = result.data.map(page => page.path);
-      const flag = resultPaths.includes('/user1') && resultPaths.includes('/user1_owner') && resultPaths.includes('/user2_public');
+      const [result, delegatorName] = await searchService.searchKeyword(
+        queryString,
+        nqName,
+        testUser1,
+        null,
+        { offset: 0, limit: 100 },
+      );
+
+      const resultPaths = result.data.map((page) => page.path);
+      const flag =
+        resultPaths.includes('/user1') &&
+        resultPaths.includes('/user1_owner') &&
+        resultPaths.includes('/user2_public');
 
       expect(flag).toBe(true);
       expect(delegatorName).toBe(PRIVATE_LEGACY_PAGES);
     });
   });
-
 });

+ 222 - 85
apps/app/test/integration/service/user-groups.test.ts

@@ -1,16 +1,12 @@
-
 import type { IGrantedGroup } from '@growi/core';
-import {
-  PageGrant, type IPage, GroupType, getIdForRef,
-} from '@growi/core';
+import { GroupType, getIdForRef, type IPage, PageGrant } from '@growi/core';
 import mongoose from 'mongoose';
-
+import { PageActionOnGroupDelete } from '../../../src/interfaces/user-group';
 import type { PageDocument, PageModel } from '../../../src/server/models/page';
 import UserGroup from '../../../src/server/models/user-group';
 import UserGroupRelation from '../../../src/server/models/user-group-relation';
 import type { IUserGroupService } from '../../../src/server/service/user-group';
 import { getInstance } from '../setup-crowi';
-import { PageActionOnGroupDelete } from '../../../src/interfaces/user-group';
 
 describe('UserGroupService', () => {
   let crowi;
@@ -44,14 +40,18 @@ describe('UserGroupService', () => {
   let rootPage: PageDocument | null;
 
   // normalize for result comparison
-  const normalizeGrantedGroups = (grantedGroups: IGrantedGroup[] | undefined) => {
-    if (grantedGroups == null) { return null }
+  const normalizeGrantedGroups = (
+    grantedGroups: IGrantedGroup[] | undefined,
+  ) => {
+    if (grantedGroups == null) {
+      return null;
+    }
     return grantedGroups.map((group) => {
       return { item: getIdForRef(group.item), type: group.type };
     });
   };
 
-  beforeAll(async() => {
+  beforeAll(async () => {
     crowi = await getInstance();
     User = mongoose.model('User');
     Page = mongoose.model<IPage, PageModel>('Page');
@@ -62,12 +62,14 @@ describe('UserGroupService', () => {
     await User.insertMany([
       // ug -> User Group
       {
-        _id: userId1, name: 'ug_test_user1', username: 'ug_test_user1', email: 'ug_test_user1@example.com',
+        _id: userId1,
+        name: 'ug_test_user1',
+        username: 'ug_test_user1',
+        email: 'ug_test_user1@example.com',
       },
     ]);
     user1 = await User.findOne({ _id: userId1 });
 
-
     // Create Groups
     await UserGroup.insertMany([
       // No parent
@@ -219,29 +221,32 @@ describe('UserGroupService', () => {
         parent: rootPage?._id,
       },
     ]);
-
   });
 
   /*
-    * Update UserGroup
-    */
+   * Update UserGroup
+   */
   describe('updateGroup', () => {
-    test('Updated values should be reflected. (name, description, parent)', async() => {
+    test('Updated values should be reflected. (name, description, parent)', async () => {
       const userGroup2 = await UserGroup.findOne({ _id: groupId2 });
 
       const newGroupName = 'v5_group1_new';
       const newGroupDescription = 'description1_new';
       const newParentId = userGroup2?._id;
 
-      const updatedUserGroup = await userGroupService.updateGroup(groupId1, newGroupName, newGroupDescription, newParentId);
+      const updatedUserGroup = await userGroupService.updateGroup(
+        groupId1,
+        newGroupName,
+        newGroupDescription,
+        newParentId,
+      );
 
       expect(updatedUserGroup.name).toBe(newGroupName);
       expect(updatedUserGroup.description).toBe(newGroupDescription);
       expect(updatedUserGroup.parent).toStrictEqual(newParentId);
     });
 
-    test('Should throw an error when trying to set existing group name', async() => {
-
+    test('Should throw an error when trying to set existing group name', async () => {
       const userGroup2 = await UserGroup.findOne({ _id: groupId2 });
 
       const result = userGroupService.updateGroup(groupId1, userGroup2?.name);
@@ -249,58 +254,102 @@ describe('UserGroupService', () => {
       await expect(result).rejects.toThrow('The group name is already taken');
     });
 
-    test('Parent should be null when parent group is released', async() => {
+    test('Parent should be null when parent group is released', async () => {
       const userGroup = await UserGroup.findOne({ _id: groupId3 });
-      const updatedUserGroup = await userGroupService.updateGroup(userGroup?._id, userGroup?.name, userGroup?.description, null);
+      const updatedUserGroup = await userGroupService.updateGroup(
+        userGroup?._id,
+        userGroup?.name,
+        userGroup?.description,
+        null,
+      );
 
       expect(updatedUserGroup.parent).toBeNull();
     });
 
     /*
-    * forceUpdateParents: false
-    */
-    test('Should throw an error when users in child group do not exist in parent group', async() => {
-      const userGroup4 = await UserGroup.findOne({ _id: groupId4, parent: null });
-      const result = userGroupService.updateGroup(userGroup4?._id, userGroup4?.name, userGroup4?.description, groupId5);
+     * forceUpdateParents: false
+     */
+    test('Should throw an error when users in child group do not exist in parent group', async () => {
+      const userGroup4 = await UserGroup.findOne({
+        _id: groupId4,
+        parent: null,
+      });
+      const result = userGroupService.updateGroup(
+        userGroup4?._id,
+        userGroup4?.name,
+        userGroup4?.description,
+        groupId5,
+      );
 
-      await expect(result).rejects.toThrow('The parent group does not contain the users in this group.');
+      await expect(result).rejects.toThrow(
+        'The parent group does not contain the users in this group.',
+      );
     });
 
     /*
-    * forceUpdateParents: true
-    */
-    test('User should be included to parent group (2 groups ver)', async() => {
-      const userGroup4 = await UserGroup.findOne({ _id: groupId4, parent: null });
-      const userGroup5 = await UserGroup.findOne({ _id: groupId5, parent: null });
+     * forceUpdateParents: true
+     */
+    test('User should be included to parent group (2 groups ver)', async () => {
+      const userGroup4 = await UserGroup.findOne({
+        _id: groupId4,
+        parent: null,
+      });
+      const userGroup5 = await UserGroup.findOne({
+        _id: groupId5,
+        parent: null,
+      });
       // userGroup4 has userId1
-      const userGroupRelation4BeforeUpdate = await UserGroupRelation.findOne({ relatedGroup:  userGroup4, relatedUser: userId1 });
+      const userGroupRelation4BeforeUpdate = await UserGroupRelation.findOne({
+        relatedGroup: userGroup4,
+        relatedUser: userId1,
+      });
       expect(userGroupRelation4BeforeUpdate).not.toBeNull();
 
       // userGroup5 has not userId1
-      const userGroupRelation5BeforeUpdate = await UserGroupRelation.findOne({ relatedGroup:  userGroup5, relatedUser: userId1 });
+      const userGroupRelation5BeforeUpdate = await UserGroupRelation.findOne({
+        relatedGroup: userGroup5,
+        relatedUser: userId1,
+      });
       expect(userGroupRelation5BeforeUpdate).toBeNull();
 
       // update userGroup4's parent with userGroup5 (forceUpdate: true)
       const forceUpdateParents = true;
       const updatedUserGroup = await userGroupService.updateGroup(
-        userGroup4?._id, userGroup4?.name, userGroup4?.description, groupId5, forceUpdateParents,
+        userGroup4?._id,
+        userGroup4?.name,
+        userGroup4?.description,
+        groupId5,
+        forceUpdateParents,
       );
 
       expect(updatedUserGroup.parent).toStrictEqual(groupId5);
       // userGroup5 should have userId1
       const userGroupRelation5AfterUpdate = await UserGroupRelation.findOne({
-        relatedGroup: groupId5, relatedUser: userGroupRelation4BeforeUpdate?.relatedUser,
+        relatedGroup: groupId5,
+        relatedUser: userGroupRelation4BeforeUpdate?.relatedUser,
       });
       expect(userGroupRelation5AfterUpdate).not.toBeNull();
     });
 
-    test('User should be included to parent group (3 groups ver)', async() => {
-      const userGroup8 = await UserGroup.findOne({ _id: groupId8, parent: null });
+    test('User should be included to parent group (3 groups ver)', async () => {
+      const userGroup8 = await UserGroup.findOne({
+        _id: groupId8,
+        parent: null,
+      });
 
       // userGroup7 has not userId1
-      const userGroupRelation6BeforeUpdate = await UserGroupRelation.findOne({ relatedGroup:  groupId6, relatedUser: userId1 });
-      const userGroupRelation7BeforeUpdate = await UserGroupRelation.findOne({ relatedGroup:  groupId7, relatedUser: userId1 });
-      const userGroupRelation8BeforeUpdate = await UserGroupRelation.findOne({ relatedGroup:  groupId8, relatedUser: userId1 });
+      const userGroupRelation6BeforeUpdate = await UserGroupRelation.findOne({
+        relatedGroup: groupId6,
+        relatedUser: userId1,
+      });
+      const userGroupRelation7BeforeUpdate = await UserGroupRelation.findOne({
+        relatedGroup: groupId7,
+        relatedUser: userId1,
+      });
+      const userGroupRelation8BeforeUpdate = await UserGroupRelation.findOne({
+        relatedGroup: groupId8,
+        relatedUser: userId1,
+      });
       expect(userGroupRelation6BeforeUpdate).not.toBeNull();
       // userGroup7 does not have userId1
       expect(userGroupRelation7BeforeUpdate).toBeNull();
@@ -309,53 +358,102 @@ describe('UserGroupService', () => {
       // update userGroup8's parent with userGroup7 (forceUpdate: true)
       const forceUpdateParents = true;
       await userGroupService.updateGroup(
-        userGroup8?._id, userGroup8?.name, userGroup8?.description, groupId7, forceUpdateParents,
+        userGroup8?._id,
+        userGroup8?.name,
+        userGroup8?.description,
+        groupId7,
+        forceUpdateParents,
       );
 
-      const userGroupRelation6AfterUpdate = await UserGroupRelation.findOne({ relatedGroup: groupId6, relatedUser: userId1 });
-      const userGroupRelation7AfterUpdate = await UserGroupRelation.findOne({ relatedGroup: groupId7, relatedUser: userId1 });
-      const userGroupRelation8AfterUpdate = await UserGroupRelation.findOne({ relatedGroup: groupId8, relatedUser: userId1 });
+      const userGroupRelation6AfterUpdate = await UserGroupRelation.findOne({
+        relatedGroup: groupId6,
+        relatedUser: userId1,
+      });
+      const userGroupRelation7AfterUpdate = await UserGroupRelation.findOne({
+        relatedGroup: groupId7,
+        relatedUser: userId1,
+      });
+      const userGroupRelation8AfterUpdate = await UserGroupRelation.findOne({
+        relatedGroup: groupId8,
+        relatedUser: userId1,
+      });
       expect(userGroupRelation6AfterUpdate).not.toBeNull();
       // userGroup7 should have userId1
       expect(userGroupRelation7AfterUpdate).not.toBeNull();
       expect(userGroupRelation8AfterUpdate).not.toBeNull();
     });
 
-    test('Should throw an error when trying to choose parent from descendant groups.', async() => {
-      const userGroup9 = await UserGroup.findOne({ _id: groupId9, parent: null });
-      const userGroup10 = await UserGroup.findOne({ _id: groupId10, parent: groupId9 });
+    test('Should throw an error when trying to choose parent from descendant groups.', async () => {
+      const userGroup9 = await UserGroup.findOne({
+        _id: groupId9,
+        parent: null,
+      });
+      const userGroup10 = await UserGroup.findOne({
+        _id: groupId10,
+        parent: groupId9,
+      });
 
-      const userGroupRelation9BeforeUpdate = await UserGroupRelation.findOne({ relatedGroup:  userGroup9?._id, relatedUser: userId1 });
-      const userGroupRelation10BeforeUpdate = await UserGroupRelation.findOne({ relatedGroup:  userGroup10?._id, relatedUser: userId1 });
+      const userGroupRelation9BeforeUpdate = await UserGroupRelation.findOne({
+        relatedGroup: userGroup9?._id,
+        relatedUser: userId1,
+      });
+      const userGroupRelation10BeforeUpdate = await UserGroupRelation.findOne({
+        relatedGroup: userGroup10?._id,
+        relatedUser: userId1,
+      });
       expect(userGroupRelation9BeforeUpdate).not.toBeNull();
       expect(userGroupRelation10BeforeUpdate).not.toBeNull();
 
       const result = userGroupService.updateGroup(
-        userGroup9?._id, userGroup9?.name, userGroup9?.description, userGroup10?._id,
+        userGroup9?._id,
+        userGroup9?.name,
+        userGroup9?.description,
+        userGroup10?._id,
+      );
+      await expect(result).rejects.toThrow(
+        'It is not allowed to choose parent from descendant groups.',
       );
-      await expect(result).rejects.toThrow('It is not allowed to choose parent from descendant groups.');
     });
   });
 
   describe('removeUserByUsername', () => {
-    test('User should be deleted from child groups when the user excluded from the parent group', async() => {
-      const userGroup11 = await UserGroup.findOne({ _id: groupId11, parent: null });
-      const userGroup12 = await UserGroup.findOne({ _id: groupId12, parent: groupId11 });
+    test('User should be deleted from child groups when the user excluded from the parent group', async () => {
+      const userGroup11 = await UserGroup.findOne({
+        _id: groupId11,
+        parent: null,
+      });
+      const userGroup12 = await UserGroup.findOne({
+        _id: groupId12,
+        parent: groupId11,
+      });
 
       // Both groups have user1
-      const userGroupRelation11BeforeRemove = await UserGroupRelation.findOne({ relatedGroup:  userGroup11?._id, relatedUser: userId1 });
-      const userGroupRelation12BeforeRemove = await UserGroupRelation.findOne({ relatedGroup:  userGroup12?._id, relatedUser: userId1 });
+      const userGroupRelation11BeforeRemove = await UserGroupRelation.findOne({
+        relatedGroup: userGroup11?._id,
+        relatedUser: userId1,
+      });
+      const userGroupRelation12BeforeRemove = await UserGroupRelation.findOne({
+        relatedGroup: userGroup12?._id,
+        relatedUser: userId1,
+      });
       expect(userGroupRelation11BeforeRemove).not.toBeNull();
       expect(userGroupRelation12BeforeRemove).not.toBeNull();
 
       // remove user1 from the parent group
       await userGroupService.removeUserByUsername(
-        userGroup11?._id, 'ug_test_user1',
+        userGroup11?._id,
+        'ug_test_user1',
       );
 
       // Both groups have not user1
-      const userGroupRelation11AfterRemove = await UserGroupRelation.findOne({ relatedGroup:  userGroup11?._id, relatedUser: userId1 });
-      const userGroupRelation12AfterRemove = await UserGroupRelation.findOne({ relatedGroup:  userGroup12?._id, relatedUser: userId1 });
+      const userGroupRelation11AfterRemove = await UserGroupRelation.findOne({
+        relatedGroup: userGroup11?._id,
+        relatedUser: userId1,
+      });
+      const userGroupRelation12AfterRemove = await UserGroupRelation.findOne({
+        relatedGroup: userGroup12?._id,
+        relatedUser: userId1,
+      });
       await expect(userGroupRelation11AfterRemove).toBeNull();
       await expect(userGroupRelation12AfterRemove).toBeNull();
     });
@@ -363,7 +461,7 @@ describe('UserGroupService', () => {
 
   describe('removeCompletelyByRootGroupId', () => {
     describe('when action is public', () => {
-      test('Should remove the group and its descendants and publicize pages that are only visible to the groups to be removed', async() => {
+      test('Should remove the group and its descendants and publicize pages that are only visible to the groups to be removed', async () => {
         const userGroup13 = await UserGroup.findOne({ _id: groupId13 });
         const userGroup14 = await UserGroup.findOne({ _id: groupId14 });
         expect(userGroup13).not.toBeNull();
@@ -372,36 +470,75 @@ describe('UserGroupService', () => {
         const canBePublicized = await Page.findOne({ _id: pageId1 });
         const cannotBePublicized = await Page.findOne({ _id: pageId2 });
         expect(canBePublicized?.grant).toBe(PageGrant.GRANT_USER_GROUP);
-        expect(normalizeGrantedGroups(canBePublicized?.grantedGroups)).toEqual(expect.arrayContaining([
-          { item: groupId13, type: GroupType.userGroup },
-          { item: groupId14, type: GroupType.userGroup },
-        ]));
-        expect(normalizeGrantedGroups(canBePublicized?.grantedGroups)?.length).toBe(2);
+        expect(normalizeGrantedGroups(canBePublicized?.grantedGroups)).toEqual(
+          expect.arrayContaining([
+            { item: groupId13, type: GroupType.userGroup },
+            { item: groupId14, type: GroupType.userGroup },
+          ]),
+        );
+        expect(
+          normalizeGrantedGroups(canBePublicized?.grantedGroups)?.length,
+        ).toBe(2);
         expect(cannotBePublicized?.grant).toBe(PageGrant.GRANT_USER_GROUP);
-        expect(normalizeGrantedGroups(cannotBePublicized?.grantedGroups)).toEqual(expect.arrayContaining([
-          { item: groupId13, type: GroupType.userGroup },
-          { item: groupId15, type: GroupType.userGroup },
-        ]));
-        expect(normalizeGrantedGroups(cannotBePublicized?.grantedGroups)?.length).toBe(2);
-
-        await userGroupService.removeCompletelyByRootGroupId(groupId13, PageActionOnGroupDelete.publicize, user1);
-
-        const userGroup13AfterDeleteProcess = await UserGroup.findOne({ _id: groupId13 });
-        const userGroup14AfterDeleteProcess = await UserGroup.findOne({ _id: groupId14 });
+        expect(
+          normalizeGrantedGroups(cannotBePublicized?.grantedGroups),
+        ).toEqual(
+          expect.arrayContaining([
+            { item: groupId13, type: GroupType.userGroup },
+            { item: groupId15, type: GroupType.userGroup },
+          ]),
+        );
+        expect(
+          normalizeGrantedGroups(cannotBePublicized?.grantedGroups)?.length,
+        ).toBe(2);
+
+        await userGroupService.removeCompletelyByRootGroupId(
+          groupId13,
+          PageActionOnGroupDelete.publicize,
+          user1,
+        );
+
+        const userGroup13AfterDeleteProcess = await UserGroup.findOne({
+          _id: groupId13,
+        });
+        const userGroup14AfterDeleteProcess = await UserGroup.findOne({
+          _id: groupId14,
+        });
         expect(userGroup13AfterDeleteProcess).toBeNull();
         expect(userGroup14AfterDeleteProcess).toBeNull();
 
-        const canBePublicizedAfterDeleteProcess = await Page.findOne({ _id: pageId1 });
-        const cannotBePublicizedAfterDeleteProcess = await Page.findOne({ _id: pageId2 });
-        expect(canBePublicizedAfterDeleteProcess?.grant).toBe(PageGrant.GRANT_PUBLIC);
-        expect(normalizeGrantedGroups(canBePublicizedAfterDeleteProcess?.grantedGroups)).toEqual([]);
-        expect(cannotBePublicizedAfterDeleteProcess?.grant).toBe(PageGrant.GRANT_USER_GROUP);
-        expect(normalizeGrantedGroups(cannotBePublicizedAfterDeleteProcess?.grantedGroups)).toEqual(expect.arrayContaining([
-          { item: groupId15, type: GroupType.userGroup },
-        ]));
-        expect(normalizeGrantedGroups(cannotBePublicizedAfterDeleteProcess?.grantedGroups)?.length).toBe(1);
+        const canBePublicizedAfterDeleteProcess = await Page.findOne({
+          _id: pageId1,
+        });
+        const cannotBePublicizedAfterDeleteProcess = await Page.findOne({
+          _id: pageId2,
+        });
+        expect(canBePublicizedAfterDeleteProcess?.grant).toBe(
+          PageGrant.GRANT_PUBLIC,
+        );
+        expect(
+          normalizeGrantedGroups(
+            canBePublicizedAfterDeleteProcess?.grantedGroups,
+          ),
+        ).toEqual([]);
+        expect(cannotBePublicizedAfterDeleteProcess?.grant).toBe(
+          PageGrant.GRANT_USER_GROUP,
+        );
+        expect(
+          normalizeGrantedGroups(
+            cannotBePublicizedAfterDeleteProcess?.grantedGroups,
+          ),
+        ).toEqual(
+          expect.arrayContaining([
+            { item: groupId15, type: GroupType.userGroup },
+          ]),
+        );
+        expect(
+          normalizeGrantedGroups(
+            cannotBePublicizedAfterDeleteProcess?.grantedGroups,
+          )?.length,
+        ).toBe(1);
       });
     });
   });
-
 });

+ 500 - 152
apps/app/test/integration/service/v5.migration.test.js

@@ -34,10 +34,14 @@ describe('V5 page migration', () => {
   const pageId10 = new mongoose.Types.ObjectId();
   const pageId11 = new mongoose.Types.ObjectId();
 
-  const onlyPublic = filter => ({ grant: Page.GRANT_PUBLIC, ...filter });
-  const ownedByTestUser1 = filter => ({ grant: Page.GRANT_OWNER, grantedUsers: [testUser1._id], ...filter });
-  const root = filter => ({ grantedUsers: [rootUser._id], ...filter });
-  const rootUserGroup = filter => ({
+  const onlyPublic = (filter) => ({ grant: Page.GRANT_PUBLIC, ...filter });
+  const ownedByTestUser1 = (filter) => ({
+    grant: Page.GRANT_OWNER,
+    grantedUsers: [testUser1._id],
+    ...filter,
+  });
+  const root = (filter) => ({ grantedUsers: [rootUser._id], ...filter });
+  const rootUserGroup = (filter) => ({
     grantedGroups: {
       $elemMatch: {
         item: rootUserGroupId,
@@ -45,7 +49,7 @@ describe('V5 page migration', () => {
     },
     ...filter,
   });
-  const testUser1Group = filter => ({
+  const testUser1Group = (filter) => ({
     grantedGroups: {
       $elemMatch: {
         item: testUser1GroupId,
@@ -58,7 +62,7 @@ describe('V5 page migration', () => {
   const notNormalized = { parent: null };
   const empty = { isEmpty: true };
 
-  beforeAll(async() => {
+  beforeAll(async () => {
     jest.restoreAllMocks();
 
     crowi = await getInstance();
@@ -71,7 +75,11 @@ describe('V5 page migration', () => {
 
     await User.insertMany([
       { name: 'rootUser', username: 'rootUser', email: 'rootUser@example.com' },
-      { name: 'testUser1', username: 'testUser1', email: 'testUser1@example.com' },
+      {
+        name: 'testUser1',
+        username: 'testUser1',
+        email: 'testUser1@example.com',
+      },
     ]);
     rootUser = await User.findOne({ username: 'rootUser' });
     testUser1 = await User.findOne({ username: 'testUser1' });
@@ -264,56 +272,82 @@ describe('V5 page migration', () => {
         parent: pageId7,
         descendantCount: 0,
       },
-
     ]);
-
   });
 
-  const normalizeParentRecursivelyByPages = async(pages, user) => {
+  const normalizeParentRecursivelyByPages = async (pages, user) => {
     return crowi.pageService.normalizeParentRecursivelyByPages(pages, user);
   };
 
-  const normalizeParentByPage = async(page, user) => {
+  const normalizeParentByPage = async (page, user) => {
     return crowi.pageService.normalizeParentByPage(page, user);
   };
 
   describe('normalizeParentRecursivelyByPages()', () => {
-
-    test('should migrate all pages specified by pageIds', async() => {
+    test('should migrate all pages specified by pageIds', async () => {
       jest.restoreAllMocks();
 
-      const pagesToRun = await Page.find({ path: { $in: ['/private1', '/dummyParent/private1'] } });
+      const pagesToRun = await Page.find({
+        path: { $in: ['/private1', '/dummyParent/private1'] },
+      });
 
       // migrate
       await normalizeParentRecursivelyByPages(pagesToRun, testUser1);
       const migratedPages = await Page.find({
         path: {
-          $in: ['/private1', '/dummyParent', '/dummyParent/private1', '/dummyParent/private1/private2', '/dummyParent/private1/private3'],
+          $in: [
+            '/private1',
+            '/dummyParent',
+            '/dummyParent/private1',
+            '/dummyParent/private1/private2',
+            '/dummyParent/private1/private3',
+          ],
         },
       });
-      const migratedPagePaths = migratedPages.filter(doc => doc.parent != null).map(doc => doc.path);
-
-      const expected = ['/private1', '/dummyParent', '/dummyParent/private1', '/dummyParent/private1/private2', '/dummyParent/private1/private3'];
+      const migratedPagePaths = migratedPages
+        .filter((doc) => doc.parent != null)
+        .map((doc) => doc.path);
+
+      const expected = [
+        '/private1',
+        '/dummyParent',
+        '/dummyParent/private1',
+        '/dummyParent/private1/private2',
+        '/dummyParent/private1/private3',
+      ];
 
       expect(migratedPagePaths.sort()).toStrictEqual(expected.sort());
     });
 
-    test('should change all v4 pages with usergroup to v5 compatible and create new parent page', async() => {
+    test('should change all v4 pages with usergroup to v5 compatible and create new parent page', async () => {
       const page8 = await Page.findOne({ path: '/normalize_7/normalize_8_gA' });
-      const page9 = await Page.findOne({ path: '/normalize_7/normalize_8_gA/normalize_9_gB' });
-      const page10 = await Page.findOne({ path: '/normalize_7/normalize_8_gC' });
+      const page9 = await Page.findOne({
+        path: '/normalize_7/normalize_8_gA/normalize_9_gB',
+      });
+      const page10 = await Page.findOne({
+        path: '/normalize_7/normalize_8_gC',
+      });
       const page11 = await Page.findOne({ path: '/normalize_7' });
       expect(page8).toBeTruthy();
       expect(page9).toBeTruthy();
       expect(page10).toBeTruthy();
       expect(page11).toBeNull();
-      await normalizeParentRecursivelyByPages([page8, page9, page10], testUser1);
+      await normalizeParentRecursivelyByPages(
+        [page8, page9, page10],
+        testUser1,
+      );
 
       // AM => After Migration
       const page7 = await Page.findOne({ path: '/normalize_7' });
-      const page8AM = await Page.findOne({ path: '/normalize_7/normalize_8_gA' });
-      const page9AM = await Page.findOne({ path: '/normalize_7/normalize_8_gA/normalize_9_gB' });
-      const page10AM = await Page.findOne({ path: '/normalize_7/normalize_8_gC' });
+      const page8AM = await Page.findOne({
+        path: '/normalize_7/normalize_8_gA',
+      });
+      const page9AM = await Page.findOne({
+        path: '/normalize_7/normalize_8_gA/normalize_9_gB',
+      });
+      const page10AM = await Page.findOne({
+        path: '/normalize_7/normalize_8_gC',
+      });
       expect(page7).toBeTruthy();
       expect(page8AM).toBeTruthy();
       expect(page9AM).toBeTruthy();
@@ -327,14 +361,31 @@ describe('V5 page migration', () => {
       expect(page10AM.parent).toStrictEqual(page7._id);
     });
 
-    test('should replace empty page with same path with new non-empty page and update all related children\'s parent', async() => {
-      const page1 = await Page.findOne({ path: '/normalize_10', isEmpty: true, parent: { $ne: null } });
+    test("should replace empty page with same path with new non-empty page and update all related children's parent", async () => {
+      const page1 = await Page.findOne({
+        path: '/normalize_10',
+        isEmpty: true,
+        parent: { $ne: null },
+      });
       const page2 = await Page.findOne({
-        path: '/normalize_10/normalize_11_gA', _id: pageId8, isEmpty: true, parent: { $ne: null },
+        path: '/normalize_10/normalize_11_gA',
+        _id: pageId8,
+        isEmpty: true,
+        parent: { $ne: null },
+      });
+      const page3 = await Page.findOne({
+        path: '/normalize_10/normalize_11_gA',
+        _id: pageId9,
+        parent: null,
+      }); // not v5
+      const page4 = await Page.findOne({
+        path: '/normalize_10/normalize_11_gA/normalize_11_gB',
+        parent: { $ne: null },
+      });
+      const page5 = await Page.findOne({
+        path: '/normalize_10/normalize_12_gC',
+        parent: { $ne: null },
       });
-      const page3 = await Page.findOne({ path: '/normalize_10/normalize_11_gA', _id: pageId9, parent: null }); // not v5
-      const page4 = await Page.findOne({ path: '/normalize_10/normalize_11_gA/normalize_11_gB', parent: { $ne: null } });
-      const page5 = await Page.findOne({ path: '/normalize_10/normalize_12_gC', parent: { $ne: null } });
       expect(page1).toBeTruthy();
       expect(page2).toBeTruthy();
       expect(page3).toBeTruthy();
@@ -344,10 +395,20 @@ describe('V5 page migration', () => {
 
       // AM => After Migration
       const page1AM = await Page.findOne({ path: '/normalize_10' });
-      const page2AM = await Page.findOne({ path: '/normalize_10/normalize_11_gA', _id: pageId8 });
-      const page3AM = await Page.findOne({ path: '/normalize_10/normalize_11_gA', _id: pageId9 });
-      const page4AM = await Page.findOne({ path: '/normalize_10/normalize_11_gA/normalize_11_gB' });
-      const page5AM = await Page.findOne({ path: '/normalize_10/normalize_12_gC' });
+      const page2AM = await Page.findOne({
+        path: '/normalize_10/normalize_11_gA',
+        _id: pageId8,
+      });
+      const page3AM = await Page.findOne({
+        path: '/normalize_10/normalize_11_gA',
+        _id: pageId9,
+      });
+      const page4AM = await Page.findOne({
+        path: '/normalize_10/normalize_11_gA/normalize_11_gB',
+      });
+      const page5AM = await Page.findOne({
+        path: '/normalize_10/normalize_12_gC',
+      });
       expect(page1AM).toBeTruthy();
       expect(page3AM).toBeTruthy();
       expect(page4AM).toBeTruthy();
@@ -361,7 +422,6 @@ describe('V5 page migration', () => {
 
       expect(page3AM.isEmpty).toBe(false);
     });
-
   });
 
   describe('should normalize only selected pages recursively (while observing the page permission rule)', () => {
@@ -416,7 +476,7 @@ describe('V5 page migration', () => {
      *     - /normalize_g/normalize_i/normalize_k (only me) is normalized
      */
 
-    beforeAll(async() => {
+    beforeAll(async () => {
       // Prepare data
       const id1 = new mongoose.Types.ObjectId();
       const id2 = new mongoose.Types.ObjectId();
@@ -514,12 +574,25 @@ describe('V5 page migration', () => {
       ]);
     });
 
-    test('should not run normalization when the target page is GRANT_USER_GROUP surrounded by public pages', async() => {
-      const mockMainOperation = jest.spyOn(crowi.pageService, 'normalizeParentRecursivelyMainOperation').mockImplementation(v => v);
-      const _page1 = await Page.findOne(onlyPublic({ path: '/deep_path/normalize_a', ...empty }));
-      const _page2 = await Page.findOne(onlyPublic({ path: '/deep_path/normalize_a/normalize_b', ...normalized }));
-      const _page3 = await Page.findOne(testUser1Group({ path: '/deep_path/normalize_a', ...notNormalized }));
-      const _page4 = await Page.findOne(testUser1Group({ path: '/deep_path/normalize_c', ...notNormalized }));
+    test('should not run normalization when the target page is GRANT_USER_GROUP surrounded by public pages', async () => {
+      const mockMainOperation = jest
+        .spyOn(crowi.pageService, 'normalizeParentRecursivelyMainOperation')
+        .mockImplementation((v) => v);
+      const _page1 = await Page.findOne(
+        onlyPublic({ path: '/deep_path/normalize_a', ...empty }),
+      );
+      const _page2 = await Page.findOne(
+        onlyPublic({
+          path: '/deep_path/normalize_a/normalize_b',
+          ...normalized,
+        }),
+      );
+      const _page3 = await Page.findOne(
+        testUser1Group({ path: '/deep_path/normalize_a', ...notNormalized }),
+      );
+      const _page4 = await Page.findOne(
+        testUser1Group({ path: '/deep_path/normalize_c', ...notNormalized }),
+      );
 
       expect(_page1).not.toBeNull();
       expect(_page2).not.toBeNull();
@@ -534,11 +607,19 @@ describe('V5 page migration', () => {
       mockMainOperation.mockRestore();
     });
 
-    test('should not include siblings', async() => {
-      const _page1 = await Page.findOne(onlyPublic({ path: '/normalize_d', ...empty }));
-      const _page2 = await Page.findOne(testUser1Group({ path: '/normalize_d/normalize_e', ...normalized }));
-      const _page3 = await Page.findOne(testUser1Group({ path: '/normalize_d', ...notNormalized }));
-      const _page4 = await Page.findOne(testUser1Group({ path: '/normalize_f', ...notNormalized }));
+    test('should not include siblings', async () => {
+      const _page1 = await Page.findOne(
+        onlyPublic({ path: '/normalize_d', ...empty }),
+      );
+      const _page2 = await Page.findOne(
+        testUser1Group({ path: '/normalize_d/normalize_e', ...normalized }),
+      );
+      const _page3 = await Page.findOne(
+        testUser1Group({ path: '/normalize_d', ...notNormalized }),
+      );
+      const _page4 = await Page.findOne(
+        testUser1Group({ path: '/normalize_f', ...notNormalized }),
+      );
 
       expect(_page1).not.toBeNull();
       expect(_page2).not.toBeNull();
@@ -548,10 +629,18 @@ describe('V5 page migration', () => {
       // Normalize
       await normalizeParentRecursivelyByPages([_page3], testUser1);
 
-      const page1 = await Page.findOne(testUser1Group({ path: '/normalize_d/normalize_e' }));
-      const page2 = await Page.findOne(testUser1Group({ path: '/normalize_d' }));
-      const page3 = await Page.findOne(testUser1Group({ path: '/normalize_f' }));
-      const empty4 = await Page.findOne(onlyPublic({ path: '/normalize_d', ...empty }));
+      const page1 = await Page.findOne(
+        testUser1Group({ path: '/normalize_d/normalize_e' }),
+      );
+      const page2 = await Page.findOne(
+        testUser1Group({ path: '/normalize_d' }),
+      );
+      const page3 = await Page.findOne(
+        testUser1Group({ path: '/normalize_f' }),
+      );
+      const empty4 = await Page.findOne(
+        onlyPublic({ path: '/normalize_d', ...empty }),
+      );
 
       expect(page1).not.toBeNull();
       expect(page2).not.toBeNull();
@@ -569,12 +658,34 @@ describe('V5 page migration', () => {
       expect(page3.descendantCount).toBe(0); // should not be normalized
     });
 
-    test('should replace all unnecessary empty pages and normalization succeeds', async() => {
-      const _pageG = await Page.findOne(onlyPublic({ path: '/normalize_g', ...normalized }));
-      const _pageGH = await Page.findOne(ownedByTestUser1({ path: '/normalize_g/normalize_h', ...notNormalized }));
-      const _pageGI = await Page.findOne(ownedByTestUser1({ path: '/normalize_g/normalize_i', ...notNormalized }));
-      const _pageGHJ = await Page.findOne(ownedByTestUser1({ path: '/normalize_g/normalize_h/normalize_j', ...notNormalized }));
-      const _pageGIK = await Page.findOne(ownedByTestUser1({ path: '/normalize_g/normalize_i/normalize_k', ...notNormalized }));
+    test('should replace all unnecessary empty pages and normalization succeeds', async () => {
+      const _pageG = await Page.findOne(
+        onlyPublic({ path: '/normalize_g', ...normalized }),
+      );
+      const _pageGH = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_g/normalize_h',
+          ...notNormalized,
+        }),
+      );
+      const _pageGI = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_g/normalize_i',
+          ...notNormalized,
+        }),
+      );
+      const _pageGHJ = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_g/normalize_h/normalize_j',
+          ...notNormalized,
+        }),
+      );
+      const _pageGIK = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_g/normalize_i/normalize_k',
+          ...notNormalized,
+        }),
+      );
 
       expect(_pageG).not.toBeNull();
       expect(_pageGH).not.toBeNull();
@@ -588,8 +699,12 @@ describe('V5 page migration', () => {
       const countG = await Page.count({ path: '/normalize_g' });
       const countGH = await Page.count({ path: '/normalize_g/normalize_h' });
       const countGI = await Page.count({ path: '/normalize_g/normalize_i' });
-      const countGHJ = await Page.count({ path: '/normalize_g/normalize_h/normalize_j' });
-      const countGIK = await Page.count({ path: '/normalize_g/normalize_i/normalize_k' });
+      const countGHJ = await Page.count({
+        path: '/normalize_g/normalize_h/normalize_j',
+      });
+      const countGIK = await Page.count({
+        path: '/normalize_g/normalize_i/normalize_k',
+      });
 
       expect(countG).toBe(1);
       expect(countGH).toBe(2);
@@ -599,10 +714,20 @@ describe('V5 page migration', () => {
 
       // -- normalized pages
       const pageG = await Page.findOne(onlyPublic({ path: '/normalize_g' }));
-      const emptyGH = await Page.findOne({ path: '/normalize_g/normalize_h', ...empty });
-      const emptyGI = await Page.findOne({ path: '/normalize_g/normalize_i', ...empty });
-      const pageGHJ = await Page.findOne({ path: '/normalize_g/normalize_h/normalize_j' });
-      const pageGIK = await Page.findOne({ path: '/normalize_g/normalize_i/normalize_k' });
+      const emptyGH = await Page.findOne({
+        path: '/normalize_g/normalize_h',
+        ...empty,
+      });
+      const emptyGI = await Page.findOne({
+        path: '/normalize_g/normalize_i',
+        ...empty,
+      });
+      const pageGHJ = await Page.findOne({
+        path: '/normalize_g/normalize_h/normalize_j',
+      });
+      const pageGIK = await Page.findOne({
+        path: '/normalize_g/normalize_i/normalize_k',
+      });
 
       // Check existence
       expect(pageG).not.toBeNull();
@@ -624,8 +749,12 @@ describe('V5 page migration', () => {
       expect(pageGIK.descendantCount).toStrictEqual(0);
 
       // -- not normalized pages
-      const pageGH = await Page.findOne(ownedByTestUser1({ path: '/normalize_g/normalize_h' }));
-      const pageGI = await Page.findOne(ownedByTestUser1({ path: '/normalize_g/normalize_i' }));
+      const pageGH = await Page.findOne(
+        ownedByTestUser1({ path: '/normalize_g/normalize_h' }),
+      );
+      const pageGI = await Page.findOne(
+        ownedByTestUser1({ path: '/normalize_g/normalize_i' }),
+      );
       // Check existence
       expect(pageGH).not.toBeNull();
       expect(pageGI).not.toBeNull();
@@ -671,7 +800,7 @@ describe('V5 page migration', () => {
      *     - E and F are NOT normalized
      */
 
-    beforeAll(async() => {
+    beforeAll(async () => {
       // Prepare data
       const id17 = new mongoose.Types.ObjectId();
       const id21 = new mongoose.Types.ObjectId();
@@ -792,14 +921,40 @@ describe('V5 page migration', () => {
       ]);
     });
 
-
-    test('Should normalize a single page without including other pages', async() => {
-      const _owned13 = await Page.findOne(ownedByTestUser1({ path: '/normalize_13_owned', ...notNormalized }));
-      const _owned14 = await Page.findOne(ownedByTestUser1({ path: '/normalize_13_owned/normalize_14_owned', ...notNormalized }));
-      const _owned15 = await Page.findOne(ownedByTestUser1({ path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned', ...notNormalized }));
-      const _owned16 = await Page.findOne(ownedByTestUser1({ path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_owned', ...notNormalized }));
-      const _root16 = await Page.findOne(root({ path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_root', ...notNormalized }));
-      const _group16 = await Page.findOne(testUser1Group({ path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_group', ...notNormalized }));
+    test('Should normalize a single page without including other pages', async () => {
+      const _owned13 = await Page.findOne(
+        ownedByTestUser1({ path: '/normalize_13_owned', ...notNormalized }),
+      );
+      const _owned14 = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_13_owned/normalize_14_owned',
+          ...notNormalized,
+        }),
+      );
+      const _owned15 = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned',
+          ...notNormalized,
+        }),
+      );
+      const _owned16 = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_owned',
+          ...notNormalized,
+        }),
+      );
+      const _root16 = await Page.findOne(
+        root({
+          path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_root',
+          ...notNormalized,
+        }),
+      );
+      const _group16 = await Page.findOne(
+        testUser1Group({
+          path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_group',
+          ...notNormalized,
+        }),
+      );
 
       expect(_owned13).not.toBeNull();
       expect(_owned14).not.toBeNull();
@@ -812,12 +967,29 @@ describe('V5 page migration', () => {
       await normalizeParentByPage(_owned14, testUser1);
 
       const owned13 = await Page.findOne({ path: '/normalize_13_owned' });
-      const empty13 = await Page.findOne({ path: '/normalize_13_owned', ...empty });
-      const owned14 = await Page.findOne({ path: '/normalize_13_owned/normalize_14_owned' });
-      const owned15 = await Page.findOne({ path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned' });
-      const owned16 = await Page.findOne({ path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_owned' });
-      const root16 = await Page.findOne(root({ path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_root' }));
-      const group16 = await Page.findOne(testUser1Group({ path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_group' }));
+      const empty13 = await Page.findOne({
+        path: '/normalize_13_owned',
+        ...empty,
+      });
+      const owned14 = await Page.findOne({
+        path: '/normalize_13_owned/normalize_14_owned',
+      });
+      const owned15 = await Page.findOne({
+        path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned',
+      });
+      const owned16 = await Page.findOne({
+        path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_owned',
+      });
+      const root16 = await Page.findOne(
+        root({
+          path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_root',
+        }),
+      );
+      const group16 = await Page.findOne(
+        testUser1Group({
+          path: '/normalize_13_owned/normalize_14_owned/normalize_15_owned/normalize_16_group',
+        }),
+      );
 
       expect(owned13).not.toBeNull();
       expect(empty13).not.toBeNull();
@@ -842,13 +1014,40 @@ describe('V5 page migration', () => {
       expect(owned14.descendantCount).toBe(0);
     });
 
-    test('Should normalize pages recursively excluding the pages not selected', async() => {
-      const _owned17 = await Page.findOne(ownedByTestUser1({ path: '/normalize_17_owned', ...normalized }));
-      const _owned18 = await Page.findOne(ownedByTestUser1({ path: '/normalize_17_owned/normalize_18_owned', ...normalized }));
-      const _owned19 = await Page.findOne(ownedByTestUser1({ path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned', ...notNormalized }));
-      const _owned20 = await Page.findOne(ownedByTestUser1({ path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_owned', ...notNormalized }));
-      const _root20 = await Page.findOne(root({ path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_root', ...notNormalized }));
-      const _group20 = await Page.findOne(rootUserGroup({ path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_group', ...notNormalized }));
+    test('Should normalize pages recursively excluding the pages not selected', async () => {
+      const _owned17 = await Page.findOne(
+        ownedByTestUser1({ path: '/normalize_17_owned', ...normalized }),
+      );
+      const _owned18 = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_17_owned/normalize_18_owned',
+          ...normalized,
+        }),
+      );
+      const _owned19 = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned',
+          ...notNormalized,
+        }),
+      );
+      const _owned20 = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_owned',
+          ...notNormalized,
+        }),
+      );
+      const _root20 = await Page.findOne(
+        root({
+          path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_root',
+          ...notNormalized,
+        }),
+      );
+      const _group20 = await Page.findOne(
+        rootUserGroup({
+          path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_group',
+          ...notNormalized,
+        }),
+      );
 
       expect(_owned17).not.toBeNull();
       expect(_owned18).not.toBeNull();
@@ -861,12 +1060,29 @@ describe('V5 page migration', () => {
       await normalizeParentRecursivelyByPages([_owned20], testUser1);
 
       const owned17 = await Page.findOne({ path: '/normalize_17_owned' });
-      const owned18 = await Page.findOne({ path: '/normalize_17_owned/normalize_18_owned' });
-      const owned19 = await Page.findOne({ path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned' });
-      const empty19 = await Page.findOne({ path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned', ...empty });
-      const owned20 = await Page.findOne({ path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_owned' });
-      const root20 = await Page.findOne(root({ path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_root' }));
-      const group20 = await Page.findOne(rootUserGroup({ path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_group' }));
+      const owned18 = await Page.findOne({
+        path: '/normalize_17_owned/normalize_18_owned',
+      });
+      const owned19 = await Page.findOne({
+        path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned',
+      });
+      const empty19 = await Page.findOne({
+        path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned',
+        ...empty,
+      });
+      const owned20 = await Page.findOne({
+        path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_owned',
+      });
+      const root20 = await Page.findOne(
+        root({
+          path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_root',
+        }),
+      );
+      const group20 = await Page.findOne(
+        rootUserGroup({
+          path: '/normalize_17_owned/normalize_18_owned/normalize_19_owned/normalize_20_group',
+        }),
+      );
 
       expect(owned17).not.toBeNull();
       expect(owned18).not.toBeNull();
@@ -890,14 +1106,45 @@ describe('V5 page migration', () => {
       expect(owned18.isEmpty).toBe(false);
     });
 
-    test('Should normalize pages recursively excluding the pages of not user\'s & Should delete unnecessary empty pages', async() => {
-      const _owned21 = await Page.findOne(ownedByTestUser1({ path: '/normalize_21_owned', ...normalized }));
-      const _owned22 = await Page.findOne(ownedByTestUser1({ path: '/normalize_21_owned/normalize_22_owned', ...normalized }));
-      const _owned23 = await Page.findOne(ownedByTestUser1({ path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned', ...notNormalized }));
-      const _empty23 = await Page.findOne({ path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned', ...normalized, ...empty });
-      const _owned24 = await Page.findOne(ownedByTestUser1({ path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_owned', ...normalized }));
-      const _root24 = await Page.findOne(root({ path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_root', ...notNormalized }));
-      const _rootGroup24 = await Page.findOne(rootUserGroup({ path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_rootGroup', ...notNormalized }));
+    test("Should normalize pages recursively excluding the pages of not user's & Should delete unnecessary empty pages", async () => {
+      const _owned21 = await Page.findOne(
+        ownedByTestUser1({ path: '/normalize_21_owned', ...normalized }),
+      );
+      const _owned22 = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_21_owned/normalize_22_owned',
+          ...normalized,
+        }),
+      );
+      const _owned23 = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned',
+          ...notNormalized,
+        }),
+      );
+      const _empty23 = await Page.findOne({
+        path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned',
+        ...normalized,
+        ...empty,
+      });
+      const _owned24 = await Page.findOne(
+        ownedByTestUser1({
+          path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_owned',
+          ...normalized,
+        }),
+      );
+      const _root24 = await Page.findOne(
+        root({
+          path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_root',
+          ...notNormalized,
+        }),
+      );
+      const _rootGroup24 = await Page.findOne(
+        rootUserGroup({
+          path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_rootGroup',
+          ...notNormalized,
+        }),
+      );
 
       expect(_owned21).not.toBeNull();
       expect(_owned22).not.toBeNull();
@@ -911,12 +1158,29 @@ describe('V5 page migration', () => {
       await normalizeParentRecursivelyByPages([_owned23], testUser1);
 
       const owned21 = await Page.findOne({ path: '/normalize_21_owned' });
-      const owned22 = await Page.findOne({ path: '/normalize_21_owned/normalize_22_owned' });
-      const owned23 = await Page.findOne({ path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned' });
-      const empty23 = await Page.findOne({ path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned', ...empty });
-      const owned24 = await Page.findOne({ path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_owned' });
-      const root24 = await Page.findOne(root({ path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_root' }));
-      const rootGroup24 = await Page.findOne(rootUserGroup({ path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_rootGroup' }));
+      const owned22 = await Page.findOne({
+        path: '/normalize_21_owned/normalize_22_owned',
+      });
+      const owned23 = await Page.findOne({
+        path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned',
+      });
+      const empty23 = await Page.findOne({
+        path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned',
+        ...empty,
+      });
+      const owned24 = await Page.findOne({
+        path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_owned',
+      });
+      const root24 = await Page.findOne(
+        root({
+          path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_root',
+        }),
+      );
+      const rootGroup24 = await Page.findOne(
+        rootUserGroup({
+          path: '/normalize_21_owned/normalize_22_owned/normalize_23_owned/normalize_24_rootGroup',
+        }),
+      );
 
       expect(owned21).not.toBeNull();
       expect(owned22).not.toBeNull();
@@ -939,16 +1203,20 @@ describe('V5 page migration', () => {
       expect(owned22.isEmpty).toBe(false);
       expect(owned23.isEmpty).toBe(false);
     });
-
   });
 
   describe('normalizeAllPublicPages()', () => {
     jest.setTimeout(60000);
     let createPagePaths;
     let allPossiblePagePaths;
-    beforeAll(async() => {
+    beforeAll(async () => {
       createPagePaths = [
-        '/publicA', '/publicA/privateB', '/publicA/privateB/publicC', '/parenthesis/(a)[b]{c}d', '/parenthesis/(a)[b]{c}d/public', '/migratedD',
+        '/publicA',
+        '/publicA/privateB',
+        '/publicA/privateB/publicC',
+        '/parenthesis/(a)[b]{c}d',
+        '/parenthesis/(a)[b]{c}d/public',
+        '/migratedD',
       ];
       allPossiblePagePaths = [...createPagePaths, '/parenthesis', '/'];
 
@@ -1003,7 +1271,7 @@ describe('V5 page migration', () => {
       jest.setTimeout(30000);
     });
 
-    test('should migrate all public pages', async() => {
+    test('should migrate all public pages', async () => {
       const migratedPages = await Page.find({
         path: {
           $in: allPossiblePagePaths,
@@ -1024,12 +1292,19 @@ describe('V5 page migration', () => {
         parent: null,
       });
 
-      const migratedPaths = migratedPages.map(page => page.path).sort();
-      const migratedEmptyPaths = migratedEmptyPages.map(page => page.path).sort();
-      const nonMigratedPaths = nonMigratedPages.map(page => page.path).sort();
-
-      const expectedMigratedPaths = allPossiblePagePaths.filter(path => path !== '/').sort();
-      const expectedMigratedEmptyPaths = ['/publicA/privateB', '/parenthesis'].sort();
+      const migratedPaths = migratedPages.map((page) => page.path).sort();
+      const migratedEmptyPaths = migratedEmptyPages
+        .map((page) => page.path)
+        .sort();
+      const nonMigratedPaths = nonMigratedPages.map((page) => page.path).sort();
+
+      const expectedMigratedPaths = allPossiblePagePaths
+        .filter((path) => path !== '/')
+        .sort();
+      const expectedMigratedEmptyPaths = [
+        '/publicA/privateB',
+        '/parenthesis',
+      ].sort();
       const expectedNonMigratedPaths = ['/publicA/privateB', '/'].sort();
 
       expect(migratedPaths).toStrictEqual(expectedMigratedPaths);
@@ -1039,9 +1314,17 @@ describe('V5 page migration', () => {
   });
 
   describe('normalizeParentByPage()', () => {
-    test('it should normalize not v5 page with usergroup that has parent group', async() => {
-      const page1 = await Page.findOne({ _id: pageId1, path: '/normalize_1', isEmpty: true });
-      const page2 = await Page.findOne({ _id: pageId2, path: '/normalize_1/normalize_2', parent: page1._id });
+    test('it should normalize not v5 page with usergroup that has parent group', async () => {
+      const page1 = await Page.findOne({
+        _id: pageId1,
+        path: '/normalize_1',
+        isEmpty: true,
+      });
+      const page2 = await Page.findOne({
+        _id: pageId2,
+        path: '/normalize_1/normalize_2',
+        parent: page1._id,
+      });
       const page3 = await Page.findOne({ _id: pageId3, path: '/normalize_1' }); // NOT v5
       expect(page1).toBeTruthy();
       expect(page2).toBeTruthy();
@@ -1050,9 +1333,19 @@ describe('V5 page migration', () => {
       await normalizeParentByPage(page3, testUser1);
 
       // AM => After Migration
-      const page1AM = await Page.findOne({ _id: pageId1, path: '/normalize_1', isEmpty: true });
-      const page2AM = await Page.findOne({ _id: pageId2, path: '/normalize_1/normalize_2' });
-      const page3AM = await Page.findOne({ _id: pageId3, path: '/normalize_1' }); // v5 compatible
+      const page1AM = await Page.findOne({
+        _id: pageId1,
+        path: '/normalize_1',
+        isEmpty: true,
+      });
+      const page2AM = await Page.findOne({
+        _id: pageId2,
+        path: '/normalize_1/normalize_2',
+      });
+      const page3AM = await Page.findOne({
+        _id: pageId3,
+        path: '/normalize_1',
+      }); // v5 compatible
       expect(page2AM).toBeTruthy();
       expect(page3AM).toBeTruthy();
       expect(page1AM).toBeNull();
@@ -1061,9 +1354,17 @@ describe('V5 page migration', () => {
       expect(page3AM.parent).toStrictEqual(rootPage._id);
     });
 
-    test('should throw error if a page with isolated group becomes the parent of other page with different group after normalizing', async() => {
-      const page4 = await Page.findOne({ _id: pageId4, path: '/normalize_4', isEmpty: true });
-      const page5 = await Page.findOne({ _id: pageId5, path: '/normalize_4/normalize_5', parent: page4._id });
+    test('should throw error if a page with isolated group becomes the parent of other page with different group after normalizing', async () => {
+      const page4 = await Page.findOne({
+        _id: pageId4,
+        path: '/normalize_4',
+        isEmpty: true,
+      });
+      const page5 = await Page.findOne({
+        _id: pageId5,
+        path: '/normalize_4/normalize_5',
+        parent: page4._id,
+      });
       const page6 = await Page.findOne({ _id: pageId6, path: '/normalize_4' }); // NOT v5
       expect(page4).toBeTruthy();
       expect(page5).toBeTruthy();
@@ -1072,15 +1373,25 @@ describe('V5 page migration', () => {
       let isThrown;
       try {
         await normalizeParentByPage(page6, testUser1);
-      }
-      catch (err) {
+      } catch (err) {
         isThrown = true;
       }
 
       // AM => After Migration
-      const page4AM = await Page.findOne({ _id: pageId4, path: '/normalize_4', isEmpty: true });
-      const page5AM = await Page.findOne({ _id: pageId5, path: '/normalize_4/normalize_5', parent: page4._id });
-      const page6AM = await Page.findOne({ _id: pageId6, path: '/normalize_4' }); // NOT v5
+      const page4AM = await Page.findOne({
+        _id: pageId4,
+        path: '/normalize_4',
+        isEmpty: true,
+      });
+      const page5AM = await Page.findOne({
+        _id: pageId5,
+        path: '/normalize_4/normalize_5',
+        parent: page4._id,
+      });
+      const page6AM = await Page.findOne({
+        _id: pageId6,
+        path: '/normalize_4',
+      }); // NOT v5
       expect(isThrown).toBe(true);
       expect(page4AM).toBeTruthy();
       expect(page5AM).toBeTruthy();
@@ -1091,10 +1402,12 @@ describe('V5 page migration', () => {
     });
   });
 
-  test('replace private parents with empty pages', async() => {
+  test('replace private parents with empty pages', async () => {
     const replacedPathPages = await Page.find({ path: '/publicA/privateB' }); // ex-private page
 
-    const _newEmptyPage = replacedPathPages.filter(page => page.parent != null)[0];
+    const _newEmptyPage = replacedPathPages.filter(
+      (page) => page.parent != null,
+    )[0];
     const newEmptyPage = {
       path: _newEmptyPage.path,
       grant: _newEmptyPage.grant,
@@ -1106,7 +1419,9 @@ describe('V5 page migration', () => {
       isEmpty: true,
     };
 
-    const _privatePage = replacedPathPages.filter(page => page.parent == null)[0];
+    const _privatePage = replacedPathPages.filter(
+      (page) => page.parent == null,
+    )[0];
     const privatePage = {
       path: _privatePage.path,
       grant: _privatePage.grant,
@@ -1124,8 +1439,10 @@ describe('V5 page migration', () => {
   });
 
   describe('normalizeParentByPath', () => {
-    const normalizeParentByPath = async(path, user) => {
-      const mock = jest.spyOn(crowi.pageService, 'normalizeParentRecursivelyMainOperation').mockReturnValue(null);
+    const normalizeParentByPath = async (path, user) => {
+      const mock = jest
+        .spyOn(crowi.pageService, 'normalizeParentRecursivelyMainOperation')
+        .mockReturnValue(null);
       const result = await crowi.pageService.normalizeParentByPath(path, user);
       const args = mock.mock.calls[0];
 
@@ -1136,7 +1453,7 @@ describe('V5 page migration', () => {
       return result;
     };
 
-    beforeAll(async() => {
+    beforeAll(async () => {
       const pageIdD = new mongoose.Types.ObjectId();
       const pageIdG = new mongoose.Types.ObjectId();
 
@@ -1203,17 +1520,23 @@ describe('V5 page migration', () => {
       ]);
     });
 
-    test('should fail when the user is not allowed to edit the target page found by path', async() => {
-      const pageTestUser1 = await Page.findOne(ownedByTestUser1({ path: '/norm_parent_by_path_A' }));
+    test('should fail when the user is not allowed to edit the target page found by path', async () => {
+      const pageTestUser1 = await Page.findOne(
+        ownedByTestUser1({ path: '/norm_parent_by_path_A' }),
+      );
 
       expect(pageTestUser1).not.toBeNull();
 
-      await expect(normalizeParentByPath('/norm_parent_by_path_A', rootUser)).rejects.toThrowError();
+      await expect(
+        normalizeParentByPath('/norm_parent_by_path_A', rootUser),
+      ).rejects.toThrowError();
     });
 
-    test('should normalize all granted pages under the path when no page exists at the path', async() => {
+    test('should normalize all granted pages under the path when no page exists at the path', async () => {
       const _pageB = await Page.findOne({ path: '/norm_parent_by_path_B' });
-      const _pageBC = await Page.findOne(root({ path: '/norm_parent_by_path_B/norm_parent_by_path_C' }));
+      const _pageBC = await Page.findOne(
+        root({ path: '/norm_parent_by_path_B/norm_parent_by_path_C' }),
+      );
 
       expect(_pageB).toBeNull();
       expect(_pageBC).not.toBeNull();
@@ -1241,10 +1564,24 @@ describe('V5 page migration', () => {
       expect(pageBC.descendantCount).toBe(0);
     });
 
-    test('should normalize all granted pages under the path when an empty page exists at the path', async() => {
-      const _emptyD = await Page.findOne({ path: '/norm_parent_by_path_D', ...empty, ...normalized });
-      const _pageDE = await Page.findOne(onlyPublic({ path: '/norm_parent_by_path_D/norm_parent_by_path_E', ...normalized }));
-      const _pageDF = await Page.findOne(root({ path: '/norm_parent_by_path_D/norm_parent_by_path_F', ...notNormalized }));
+    test('should normalize all granted pages under the path when an empty page exists at the path', async () => {
+      const _emptyD = await Page.findOne({
+        path: '/norm_parent_by_path_D',
+        ...empty,
+        ...normalized,
+      });
+      const _pageDE = await Page.findOne(
+        onlyPublic({
+          path: '/norm_parent_by_path_D/norm_parent_by_path_E',
+          ...normalized,
+        }),
+      );
+      const _pageDF = await Page.findOne(
+        root({
+          path: '/norm_parent_by_path_D/norm_parent_by_path_F',
+          ...notNormalized,
+        }),
+      );
 
       expect(_emptyD).not.toBeNull();
       expect(_pageDE).not.toBeNull();
@@ -1281,10 +1618,22 @@ describe('V5 page migration', () => {
       expect(pageDF.descendantCount).toBe(0);
     });
 
-    test('should normalize all granted pages under the path when a non-empty page exists at the path', async() => {
-      const _pageG = await Page.findOne(onlyPublic({ path: '/norm_parent_by_path_G', ...normalized }));
-      const _pageGH = await Page.findOne(onlyPublic({ path: '/norm_parent_by_path_G/norm_parent_by_path_H', ...normalized }));
-      const _pageGI = await Page.findOne(root({ path: '/norm_parent_by_path_G/norm_parent_by_path_I', ...notNormalized }));
+    test('should normalize all granted pages under the path when a non-empty page exists at the path', async () => {
+      const _pageG = await Page.findOne(
+        onlyPublic({ path: '/norm_parent_by_path_G', ...normalized }),
+      );
+      const _pageGH = await Page.findOne(
+        onlyPublic({
+          path: '/norm_parent_by_path_G/norm_parent_by_path_H',
+          ...normalized,
+        }),
+      );
+      const _pageGI = await Page.findOne(
+        root({
+          path: '/norm_parent_by_path_G/norm_parent_by_path_I',
+          ...notNormalized,
+        }),
+      );
 
       expect(_pageG).not.toBeNull();
       expect(_pageGH).not.toBeNull();
@@ -1317,5 +1666,4 @@ describe('V5 page migration', () => {
       expect(pageGI.descendantCount).toBe(0);
     });
   });
-
 });

Разница между файлами не показана из-за своего большого размера
+ 426 - 139
apps/app/test/integration/service/v5.non-public-page.test.ts


+ 78 - 26
apps/app/test/integration/service/v5.page.test.ts

@@ -1,10 +1,12 @@
 import { addSeconds } from 'date-fns/addSeconds';
 import mongoose from 'mongoose';
 
-import { PageActionStage, PageActionType } from '../../../src/interfaces/page-operation';
+import {
+  PageActionStage,
+  PageActionType,
+} from '../../../src/interfaces/page-operation';
 import { getInstance } from '../setup-crowi';
 
-
 describe('Test page service methods', () => {
   let crowi;
   let Page;
@@ -32,7 +34,7 @@ describe('Test page service methods', () => {
   let pageOpId5;
   let pageOpId6;
 
-  beforeAll(async() => {
+  beforeAll(async () => {
     crowi = await getInstance();
     await crowi.configManager.updateConfig('app:isV5Compatible', true);
 
@@ -62,7 +64,6 @@ describe('Test page service methods', () => {
     // page
     rootPage = await Page.findOne({ path: '/' });
 
-
     /**
      * pages
      */
@@ -470,17 +471,26 @@ describe('Test page service methods', () => {
   });
 
   describe('restart renameOperation', () => {
-    const resumeRenameSubOperation = async(renamePage, pageOp, activity?) => {
-      const mockedPathsAndDescendantCountOfAncestors = jest.spyOn(crowi.pageService, 'fixPathsAndDescendantCountOfAncestors').mockReturnValue(null);
-      await crowi.pageService.resumeRenameSubOperation(renamePage, pageOp, activity);
-
-      const argsForRenameSubOperation = mockedPathsAndDescendantCountOfAncestors.mock.calls[0];
+    const resumeRenameSubOperation = async (renamePage, pageOp, activity?) => {
+      const mockedPathsAndDescendantCountOfAncestors = jest
+        .spyOn(crowi.pageService, 'fixPathsAndDescendantCountOfAncestors')
+        .mockReturnValue(null);
+      await crowi.pageService.resumeRenameSubOperation(
+        renamePage,
+        pageOp,
+        activity,
+      );
+
+      const argsForRenameSubOperation =
+        mockedPathsAndDescendantCountOfAncestors.mock.calls[0];
 
       mockedPathsAndDescendantCountOfAncestors.mockRestore();
-      await crowi.pageService.fixPathsAndDescendantCountOfAncestors(...argsForRenameSubOperation);
+      await crowi.pageService.fixPathsAndDescendantCountOfAncestors(
+        ...argsForRenameSubOperation,
+      );
     };
 
-    test('it should successfully restart rename operation', async() => {
+    test('it should successfully restart rename operation', async () => {
       // paths before renaming
       const _path0 = '/resume_rename_0'; // out of renaming scope
       const _path1 = '/resume_rename_0/resume_rename_1'; // renamed already
@@ -491,7 +501,8 @@ describe('Test page service methods', () => {
       const path0 = '/resume_rename_0';
       const path1 = '/resume_rename_0/resume_rename_1';
       const path2 = '/resume_rename_0/resume_rename_1/resume_rename_2';
-      const path3 = '/resume_rename_0/resume_rename_1/resume_rename_2/resume_rename_3';
+      const path3 =
+        '/resume_rename_0/resume_rename_1/resume_rename_2/resume_rename_3';
 
       // activity options
       const activity = 'randomActivityId';
@@ -515,7 +526,12 @@ describe('Test page service methods', () => {
       const fromPath = '/resume_rename_1';
       const toPath = '/resume_rename_0/resume_rename_1';
       const _pageOperation = await PageOperation.findOne({
-        _id: pageOpId1, fromPath, toPath, 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub,
+        _id: pageOpId1,
+        fromPath,
+        toPath,
+        'page._id': _page1._id,
+        actionType: PageActionType.Rename,
+        actionStage: PageActionStage.Sub,
       });
       expect(_pageOperation).toBeTruthy();
 
@@ -546,7 +562,7 @@ describe('Test page service methods', () => {
       expect(page2.descendantCount).toBe(1);
       expect(page3.descendantCount).toBe(0);
     });
-    test('it should successfully restart rename operation when unprocessableExpiryDate is null', async() => {
+    test('it should successfully restart rename operation when unprocessableExpiryDate is null', async () => {
       // paths before renaming
       const _path0 = '/resume_rename_8'; // out of renaming scope
       const _path1 = '/resume_rename_8/resume_rename_9'; // renamed already
@@ -580,12 +596,21 @@ describe('Test page service methods', () => {
       const fromPath = '/resume_rename_9';
       const toPath = '/resume_rename_8/resume_rename_9';
       const _pageOperation = await PageOperation.findOne({
-        _id: pageOpId4, fromPath, toPath, 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub,
+        _id: pageOpId4,
+        fromPath,
+        toPath,
+        'page._id': _page1._id,
+        actionType: PageActionType.Rename,
+        actionStage: PageActionStage.Sub,
       });
       expect(_pageOperation).toBeTruthy();
 
       // rename
-      await resumeRenameSubOperation(_page1, _pageOperation, activityParameters);
+      await resumeRenameSubOperation(
+        _page1,
+        _pageOperation,
+        activityParameters,
+      );
 
       // page
       const page0 = await Page.findById(_page0._id);
@@ -610,7 +635,7 @@ describe('Test page service methods', () => {
       expect(page1.descendantCount).toBe(1);
       expect(page2.descendantCount).toBe(0);
     });
-    test('it should fail and throw error if the current time is behind unprocessableExpiryDate', async() => {
+    test('it should fail and throw error if the current time is behind unprocessableExpiryDate', async () => {
       // path before renaming
       const _path0 = '/resume_rename_4'; // out of renaming scope
       const _path1 = '/resume_rename_4/resume_rename_5'; // renamed already
@@ -627,21 +652,34 @@ describe('Test page service methods', () => {
       const fromPath = '/resume_rename_5';
       const toPath = '/resume_rename_4/resume_rename_5';
       const _pageOperation = await PageOperation.findOne({
-        _id: pageOpId2, fromPath, toPath, 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub,
+        _id: pageOpId2,
+        fromPath,
+        toPath,
+        'page._id': _page1._id,
+        actionType: PageActionType.Rename,
+        actionStage: PageActionStage.Sub,
       });
       expect(_pageOperation).toBeTruthy();
 
       // Make `unprocessableExpiryDate` 15 seconds ahead of current time.
       // The number 15 seconds has no meaning other than placing time in the furue.
-      const pageOperation = await PageOperation.findByIdAndUpdate(_pageOperation._id, { unprocessableExpiryDate: addSeconds(new Date(), 15) }, { new: true });
+      const pageOperation = await PageOperation.findByIdAndUpdate(
+        _pageOperation._id,
+        { unprocessableExpiryDate: addSeconds(new Date(), 15) },
+        { new: true },
+      );
       expect(pageOperation).toBeTruthy();
 
-      await expect(resumeRenameSubOperation(_page1, pageOperation)).rejects.toThrow(new Error('This page operation is currently being processed'));
+      await expect(
+        resumeRenameSubOperation(_page1, pageOperation),
+      ).rejects.toThrow(
+        new Error('This page operation is currently being processed'),
+      );
 
       // cleanup
       await PageOperation.findByIdAndDelete(pageOperation._id);
     });
-    test('Missing property(toPath) for PageOperation should throw error', async() => {
+    test('Missing property(toPath) for PageOperation should throw error', async () => {
       // page
       const _path1 = '/resume_rename_7';
       const _page1 = await Page.findOne({ path: _path1 });
@@ -649,23 +687,31 @@ describe('Test page service methods', () => {
 
       // page operation
       const pageOperation = await PageOperation.findOne({
-        _id: pageOpId3, 'page._id': _page1._id, actionType: PageActionType.Rename, actionStage: PageActionStage.Sub,
+        _id: pageOpId3,
+        'page._id': _page1._id,
+        actionType: PageActionType.Rename,
+        actionStage: PageActionStage.Sub,
       });
       expect(pageOperation).toBeTruthy();
 
       const promise = resumeRenameSubOperation(_page1, pageOperation);
-      await expect(promise).rejects.toThrow(new Error(`Property toPath is missing which is needed to resume rename operation(${pageOperation._id})`));
+      await expect(promise).rejects.toThrow(
+        new Error(
+          `Property toPath is missing which is needed to resume rename operation(${pageOperation._id})`,
+        ),
+      );
 
       // cleanup
       await PageOperation.findByIdAndDelete(pageOperation._id);
     });
   });
   describe('updateDescendantCountOfPagesWithPaths', () => {
-    test('should fix descendantCount of pages with one of the given paths', async() => {
+    test('should fix descendantCount of pages with one of the given paths', async () => {
       // path
       const _path1 = '/fix_descendantCount_1';
       const _path2 = '/fix_descendantCount_1/fix_descendantCount_2'; // empty
-      const _path3 = '/fix_descendantCount_1/fix_descendantCount_2/fix_descendantCount_3';
+      const _path3 =
+        '/fix_descendantCount_1/fix_descendantCount_2/fix_descendantCount_3';
       const _path4 = '/fix_descendantCount_4';
       const _path5 = '/fix_descendantCount_4/fix_descendantCount_5';
       // page
@@ -699,7 +745,13 @@ describe('Test page service methods', () => {
       expect(_page4.parent).toStrictEqual(rootPage._id);
       expect(_page5.parent).toStrictEqual(_page4._id);
 
-      await crowi.pageService.updateDescendantCountOfPagesWithPaths([_path1, _path2, _path3, _path4, _path5]);
+      await crowi.pageService.updateDescendantCountOfPagesWithPaths([
+        _path1,
+        _path2,
+        _path3,
+        _path4,
+        _path5,
+      ]);
 
       // page
       const page1 = await Page.findById(_page1._id);

Разница между файлами не показана из-за своего большого размера
+ 455 - 172
apps/app/test/integration/service/v5.public-page.test.ts


+ 0 - 1
biome.json

@@ -30,7 +30,6 @@
       "!apps/app/public/**",
       "!apps/app/resource/**",
       "!apps/app/src/**",
-      "!apps/app/test/integration/service/**",
       "!apps/app/test-with-vite/**",
       "!apps/app/tmp/**"
     ]

Некоторые файлы не были показаны из-за большого количества измененных файлов