Yuki Takei 1 mese fa
parent
commit
512ed4de45
1 ha cambiato i file con 48 aggiunte e 30 eliminazioni
  1. 48 30
      apps/app/src/server/routes/apiv3/page/get-page-info.integ.ts

+ 48 - 30
apps/app/src/server/routes/apiv3/page/get-page-info.integ.ts

@@ -1,11 +1,14 @@
 import type { NextFunction, Request, Response } from 'express';
 import express from 'express';
 import mockRequire from 'mock-require';
+import { Types } from 'mongoose';
 import request from 'supertest';
+import { mockDeep } from 'vitest-mock-extended';
 
 import { getInstance } from '^/test/setup/crowi';
 
 import type Crowi from '~/server/crowi';
+import type { PageDocument } from '~/server/models/page';
 import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
 import * as findPageModule from '~/server/service/page/find-page-and-meta-data-by-viewer';
 
@@ -73,38 +76,51 @@ describe('GET /info', () => {
 
     // Mock findPageAndMetaDataByViewer with default successful response
     const mockSpy = vi.spyOn(findPageModule, 'findPageAndMetaDataByViewer');
+
+    // Create type-safe mock PageDocument using vitest-mock-extended
+    // Note: mockDeep makes all properties optional, but _id must be required
+    const mockPageDoc = mockDeep<PageDocument>({
+      _id: new Types.ObjectId(validPageId),
+      path: '/test-page',
+      status: 'published',
+      isEmpty: false,
+      grant: 1,
+      descendantCount: 0,
+      commentCount: 0,
+    });
+
+    type PageInfoExt = Exclude<
+      Awaited<
+        ReturnType<typeof findPageModule.findPageAndMetaDataByViewer>
+      >['meta'],
+      { isNotFound: true }
+    >;
+
     mockSpy.mockResolvedValue({
-      data: {
-        _id: validPageId,
-        path: '/test-page',
-        revision: {
-          _id: '507f1f77bcf86cd799439013',
-          body: 'Test page content',
-        },
-      },
+      // mockDeep creates DeepMockProxy which conflicts with Required<{_id}>
+      // so we acknowledge this limitation for Mongoose documents
+      data: mockPageDoc as typeof mockPageDoc &
+        Required<{ _id: Types.ObjectId }>,
       meta: {
         isNotFound: false,
-        isForbidden: false,
+        isV5Compatible: true,
         isEmpty: false,
-        isMovable: true,
-        isDeletable: true,
-        isAbleToDeleteCompletely: true,
-        isRevertible: true,
+        isMovable: false,
+        isDeletable: false,
+        isAbleToDeleteCompletely: false,
+        isRevertible: false,
         bookmarkCount: 0,
-      },
-    } as unknown as Awaited<
-      ReturnType<typeof findPageModule.findPageAndMetaDataByViewer>
-    >);
+      } satisfies PageInfoExt,
+    });
 
     // Setup express app with middleware
     app = express();
     app.use(express.json());
 
     // Add apiv3 response helpers
-    app.use((_req, res, next) => {
-      const apiRes = res as ApiV3Response;
-      apiRes.apiv3 = (data: unknown) => res.json(data);
-      apiRes.apiv3Err = (error: unknown, statusCode?: number) => {
+    app.use((_req, res: ApiV3Response, next) => {
+      res.apiv3 = (data: unknown) => res.json(data);
+      res.apiv3Err = (error: unknown, statusCode?: number) => {
         // Validation errors come as arrays and should return 400
         const status = statusCode ?? (Array.isArray(error) ? 400 : 500);
         const errorMessage =
@@ -151,9 +167,10 @@ describe('GET /info', () => {
 
       expect(response.status).toBe(200);
       expect(response.body).toHaveProperty('isNotFound');
-      expect(response.body).toHaveProperty('isForbidden');
+      expect(response.body).toHaveProperty('isV5Compatible');
+      expect(response.body).toHaveProperty('isEmpty');
+      expect(response.body).toHaveProperty('bookmarkCount');
       expect(response.body.isNotFound).toBe(false);
-      expect(response.body.isForbidden).toBe(false);
     });
 
     it('should return 403 when page is forbidden', async () => {
@@ -164,7 +181,7 @@ describe('GET /info', () => {
           isNotFound: true,
           isForbidden: true,
         },
-      } as unknown as Awaited<
+      } satisfies Awaited<
         ReturnType<typeof findPageModule.findPageAndMetaDataByViewer>
       >);
 
@@ -176,16 +193,15 @@ describe('GET /info', () => {
       expect(response.body).toHaveProperty('error');
     });
 
-    it('should return 200 when page is empty (not found but not forbidden)', async () => {
+    it('should return 200 when page is not found but not forbidden', async () => {
       const mockSpy = vi.spyOn(findPageModule, 'findPageAndMetaDataByViewer');
       mockSpy.mockResolvedValue({
         data: null,
         meta: {
           isNotFound: true,
           isForbidden: false,
-          isEmpty: true,
         },
-      } as Awaited<
+      } satisfies Awaited<
         ReturnType<typeof findPageModule.findPageAndMetaDataByViewer>
       >);
 
@@ -194,8 +210,9 @@ describe('GET /info', () => {
         .query({ pageId: validPageId });
 
       expect(response.status).toBe(200);
-      expect(response.body).toHaveProperty('isEmpty');
-      expect(response.body.isEmpty).toBe(true);
+      expect(response.body).toHaveProperty('isNotFound');
+      expect(response.body.isNotFound).toBe(true);
+      expect(response.body.isForbidden).toBe(false);
     });
   });
 
@@ -207,7 +224,8 @@ describe('GET /info', () => {
 
       expect(response.status).toBe(200);
       expect(response.body).toHaveProperty('isNotFound');
-      expect(response.body).toHaveProperty('isForbidden');
+      expect(response.body).toHaveProperty('bookmarkCount');
+      expect(response.body.isNotFound).toBe(false);
     });
 
     it('should accept shareLinkId as optional parameter', async () => {