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

feat: Add MongoDB migration and utility setup for Vitest tests

Yuki Takei 2 месяцев назад
Родитель
Сommit
3447f6d82f

+ 1 - 1
.serena/memories/project_structure.md

@@ -39,7 +39,7 @@ apps/app/
 ### test/
 ### test/
 - Vitest用のファイル
 - Vitest用のファイル
 - 新規テスト用のユーティリティはここに作成
 - 新規テスト用のユーティリティはここに作成
-- セットアップファイル: `setup/mongoms.ts` (MongoDB用)
+- セットアップファイル: `setup/mongo.ts` (MongoDB用)
 
 
 ### playwright/
 ### playwright/
 - Playwright による E2E テスト用ディレクトリ
 - Playwright による E2E テスト用ディレクトリ

+ 56 - 0
apps/app/test/setup/migrate-mongo.ts

@@ -0,0 +1,56 @@
+import path from 'node:path';
+import { beforeAll } from 'vitest';
+
+import { mongoOptions } from '~/server/util/mongoose-utils';
+
+import { getTestDbConfig } from './mongo';
+
+/**
+ * Run database migrations using migrate-mongo API.
+ * This is necessary when using external MongoDB in CI to ensure each worker's
+ * database has the required schema and indexes.
+ */
+async function runMigrations(mongoUri: string, dbName: string): Promise<void> {
+  // Dynamic import for migrate-mongo (CommonJS module)
+  // @ts-expect-error migrate-mongo does not have type definitions
+  const { config, up, database } = await import('migrate-mongo');
+
+  // Set custom config for this worker's database
+  config.set({
+    mongodb: {
+      url: mongoUri,
+      databaseName: dbName,
+      options: mongoOptions,
+    },
+    // Use process.cwd() for reliability in Vitest environment
+    // In CI, tests run from apps/app directory
+    migrationsDir: path.resolve(process.cwd(), 'src/migrations'),
+    changelogCollectionName: 'migrations',
+  });
+
+  // Connect and run migrations
+  const { db, client } = await database.connect();
+  try {
+    const migrated = await up(db, client);
+    if (migrated.length > 0) {
+      // biome-ignore lint/suspicious/noConsole: Allow logging
+      console.log(`Migrations applied: ${migrated.join(', ')}`);
+    }
+  } finally {
+    await client.close();
+  }
+}
+
+beforeAll(async () => {
+  const { dbName, mongoUri } = getTestDbConfig();
+
+  // Only run migrations when using external MongoDB (CI environment)
+  if (mongoUri == null) {
+    return;
+  }
+
+  // biome-ignore lint/suspicious/noConsole: Allow logging
+  console.log(`Running migrations for ${dbName}...`);
+
+  await runMigrations(mongoUri, dbName);
+});

+ 1 - 1
apps/app/test/setup/mongoms.spec.ts → apps/app/test/setup/mongo.spec.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it } from 'vitest';
 import { describe, expect, it } from 'vitest';
 
 
-import { replaceMongoDbName } from './mongoms';
+import { replaceMongoDbName } from './mongo';
 
 
 describe('replaceMongoDbName', () => {
 describe('replaceMongoDbName', () => {
   describe('single-host URIs', () => {
   describe('single-host URIs', () => {

+ 22 - 5
apps/app/test/setup/mongoms.ts → apps/app/test/setup/mongo.ts

@@ -1,6 +1,7 @@
 import ConnectionString from 'mongodb-connection-string-url';
 import ConnectionString from 'mongodb-connection-string-url';
 import { MongoMemoryServer } from 'mongodb-memory-server-core';
 import { MongoMemoryServer } from 'mongodb-memory-server-core';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
+import { afterAll, beforeAll } from 'vitest';
 
 
 import { mongoOptions } from '~/server/util/mongoose-utils';
 import { mongoOptions } from '~/server/util/mongoose-utils';
 
 
@@ -21,18 +22,34 @@ export function replaceMongoDbName(uri: string, newDbName: string): string {
   return cs.href;
   return cs.href;
 }
 }
 
 
-beforeAll(async () => {
-  // Generate unique database name for each test worker to avoid conflicts in parallel execution
+/**
+ * Get test database configuration for the current Vitest worker.
+ * Each worker gets a unique database name to avoid conflicts in parallel execution.
+ */
+export function getTestDbConfig(): {
+  workerId: string;
+  dbName: string;
+  mongoUri: string | null;
+} {
   // VITEST_WORKER_ID is provided by Vitest (e.g., "1", "2", "3"...)
   // VITEST_WORKER_ID is provided by Vitest (e.g., "1", "2", "3"...)
   const workerId = process.env.VITEST_WORKER_ID || '1';
   const workerId = process.env.VITEST_WORKER_ID || '1';
   const dbName = `growi_test_${workerId}`;
   const dbName = `growi_test_${workerId}`;
+  const mongoUri = process.env.MONGO_URI
+    ? replaceMongoDbName(process.env.MONGO_URI, dbName)
+    : null;
 
 
-  // Use external MongoDB if MONGO_URI is provided (e.g., in CI with GitHub Actions services)
-  if (process.env.MONGO_URI) {
-    const mongoUri = replaceMongoDbName(process.env.MONGO_URI, dbName);
+  return { workerId, dbName, mongoUri };
+}
 
 
+beforeAll(async () => {
+  const { workerId, dbName, mongoUri } = getTestDbConfig();
+
+  // Use external MongoDB if MONGO_URI is provided (e.g., in CI with GitHub Actions services)
+  if (mongoUri != null) {
     // biome-ignore lint/suspicious/noConsole: Allow logging
     // biome-ignore lint/suspicious/noConsole: Allow logging
     console.log(`Using external MongoDB at ${mongoUri} (worker: ${workerId})`);
     console.log(`Using external MongoDB at ${mongoUri} (worker: ${workerId})`);
+
+    // Migrations are run by migrate-mongo.ts setup file
     await mongoose.connect(mongoUri, mongoOptions);
     await mongoose.connect(mongoUri, mongoOptions);
     return;
     return;
   }
   }

+ 1 - 1
apps/app/vitest.workspace.mts

@@ -31,7 +31,7 @@ export default defineWorkspace([
       name: 'app-integration',
       name: 'app-integration',
       environment: 'node',
       environment: 'node',
       include: ['**/*.integ.ts'],
       include: ['**/*.integ.ts'],
-      setupFiles: ['./test/setup/mongoms.ts'],
+      setupFiles: ['./test/setup/migrate-mongo.ts', './test/setup/mongo.ts'],
       deps: {
       deps: {
         // Transform inline modules (allows ESM in require context)
         // Transform inline modules (allows ESM in require context)
         interopDefault: true,
         interopDefault: true,