Parcourir la source

refactor: Use robust URI parsing with URL API and support replica sets, fix MongoMemoryServer to use unique DB per worker

Co-authored-by: yuki-takei <1638767+yuki-takei@users.noreply.github.com>
copilot-swe-agent[bot] il y a 2 mois
Parent
commit
8c550f8fe2
1 fichiers modifiés avec 49 ajouts et 22 suppressions
  1. 49 22
      apps/app/test/setup/mongoms.ts

+ 49 - 22
apps/app/test/setup/mongoms.ts

@@ -5,30 +5,56 @@ import { mongoOptions } from '~/server/util/mongoose-utils';
 
 
 let mongoServer: MongoMemoryServer | undefined;
 let mongoServer: MongoMemoryServer | undefined;
 
 
-beforeAll(async () => {
-  // Use external MongoDB if MONGO_URI is provided (e.g., in CI with GitHub Actions services)
-  if (process.env.MONGO_URI) {
-    // Generate unique database name for each test worker to avoid conflicts in parallel execution
-    // VITEST_WORKER_ID is provided by Vitest (e.g., "1", "2", "3"...)
-    const workerId = process.env.VITEST_WORKER_ID || '1';
-    const dbName = `growi_test_${workerId}`;
+/**
+ * Replace the database name in a MongoDB connection URI.
+ * Supports various URI formats including authentication, replica sets, and query parameters.
+ * Uses a simple string-based approach that handles most MongoDB URI formats correctly.
+ * 
+ * @param uri - MongoDB connection URI
+ * @param newDbName - New database name to use
+ * @returns Modified URI with the new database name
+ */
+function replaceMongoDbName(uri: string, newDbName: string): string {
+  try {
+    // For standard single-host URIs, use URL API for robust parsing
+    // Format: mongodb://[username:password@]host[:port][/database][?options]
+    if (!uri.includes(',')) {
+      const url = new URL(uri);
+      url.pathname = `/${newDbName}`;
+      return url.toString();
+    }
     
     
-    // Parse base URI and append database name
-    // Extract base URI (protocol + host + port) and query parameters
-    const [uriWithoutQuery, queryString] = process.env.MONGO_URI.split('?');
+    // For replica set URIs with multiple hosts (contains comma)
+    // Format: mongodb://host1:port1,host2:port2[/database][?options]
+    // URL API doesn't support multiple hosts, so use string manipulation
+    const [beforeDb, afterDb] = uri.split('?');
+    const queryString = afterDb ? `?${afterDb}` : '';
     
     
-    // Find the last slash after the protocol (mongodb://)
-    // and replace everything after it with the new database name
-    let baseUri: string;
-    const protocolMatch = uriWithoutQuery.match(/^mongodb:\/\/[^/]+/);
-    if (protocolMatch) {
-      baseUri = protocolMatch[0];
-    } else {
-      // Fallback: if no match, use the whole URI
-      baseUri = uriWithoutQuery;
+    // Find the last slash before the database name (after all hosts)
+    const lastSlashIndex = beforeDb.lastIndexOf('/');
+    if (lastSlashIndex > 'mongodb://'.length) {
+      // URI has a database name, replace it
+      const baseUri = beforeDb.substring(0, lastSlashIndex);
+      return `${baseUri}/${newDbName}${queryString}`;
     }
     }
     
     
-    const mongoUri = `${baseUri}/${dbName}${queryString ? '?' + queryString : ''}`;
+    // URI has no database name, append it
+    return `${beforeDb}/${newDbName}${queryString}`;
+  } catch (error) {
+    // If parsing fails, throw an error with helpful message
+    throw new Error(`Failed to parse MongoDB URI: ${error instanceof Error ? error.message : String(error)}`);
+  }
+}
+
+beforeAll(async () => {
+  // Generate unique database name for each test worker to avoid conflicts in parallel execution
+  // VITEST_WORKER_ID is provided by Vitest (e.g., "1", "2", "3"...)
+  const workerId = process.env.VITEST_WORKER_ID || '1';
+  const dbName = `growi_test_${workerId}`;
+
+  // 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);
     
     
     // 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})`);
@@ -43,7 +69,8 @@ beforeAll(async () => {
   // set version
   // set version
   mongoServer = await MongoMemoryServer.create({
   mongoServer = await MongoMemoryServer.create({
     instance: {
     instance: {
-      dbName: 'growi_test',
+      // Use unique database name per worker to avoid conflicts in parallel execution
+      dbName,
     },
     },
     binary: {
     binary: {
       version: process.env.VITE_MONGOMS_VERSION,
       version: process.env.VITE_MONGOMS_VERSION,
@@ -52,7 +79,7 @@ beforeAll(async () => {
   });
   });
 
 
   // biome-ignore lint/suspicious/noConsole: Allow logging
   // biome-ignore lint/suspicious/noConsole: Allow logging
-  console.log(`MongoMemoryServer is running on ${mongoServer.getUri()}`);
+  console.log(`MongoMemoryServer is running on ${mongoServer.getUri()} (worker: ${workerId})`);
 
 
   await mongoose.connect(mongoServer.getUri(), mongoOptions);
   await mongoose.connect(mongoServer.getUri(), mongoOptions);
 });
 });