From b8a25fda12c449a6af9f8c62a199eb06f714e90b Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Tue, 17 Dec 2024 20:31:57 +0530 Subject: [PATCH 01/11] updated setup.ts to support docker compose v2 --- setup.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.ts b/setup.ts index b8ad43ecc3..543c8c1ccf 100644 --- a/setup.ts +++ b/setup.ts @@ -496,8 +496,10 @@ async function runDockerComposeWithLogs(): Promise { }, 300000); const dockerCompose = spawn( - process.platform === "win32" ? "docker-compose.exe" : "docker-compose", - ["-f", "docker-compose.dev.yaml", "up", "--build", "-d"], + process.platform === "win32" ? "docker-compose.exe" : "docker", + process.platform === "win32" + ? ["-f", "docker-compose.dev.yaml", "up", "--build", "-d"] + : ["compose", "-f", "docker-compose.dev.yaml", "up", "--build", "-d"], { stdio: "inherit" }, ); From 3a74c00fc4c8687707f79087cc039e207b77957f Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Tue, 17 Dec 2024 21:23:18 +0530 Subject: [PATCH 02/11] updated setup.ts to support docker compose v1 and v2 --- setup.ts | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/setup.ts b/setup.ts index 543c8c1ccf..1c8781a206 100644 --- a/setup.ts +++ b/setup.ts @@ -5,7 +5,7 @@ import fs from "fs"; import inquirer from "inquirer"; import path from "path"; import type { ExecException } from "child_process"; -import { exec, spawn } from "child_process"; +import { exec, spawn, execSync } from "child_process"; import { MongoClient } from "mongodb"; import { MAXIMUM_IMAGE_SIZE_LIMIT_KB } from "./src/constants"; import { @@ -466,6 +466,24 @@ export async function mongoDB(): Promise { For Docker setup */ +function getDockerComposeCommand(): { command: string; args: string[] } { + let dockerComposeCmd = "docker-compose"; // Default to v1 + let args = ["-f", "docker-compose.dev.yaml", "up", "--build", "-d"]; + + try { + // Test if 'docker compose' works (v2) + execSync("docker compose version", { stdio: "ignore" }); + dockerComposeCmd = "docker"; + args = ["compose", ...args.slice(0)]; // Prefix 'compose' for v2 + } catch (error) { + console.log(error); + dockerComposeCmd = + process.platform === "win32" ? "docker-compose.exe" : "docker-compose"; + } + + return { command: dockerComposeCmd, args }; +} + async function runDockerComposeWithLogs(): Promise { // Check if Docker daemon is running try { @@ -490,19 +508,14 @@ async function runDockerComposeWithLogs(): Promise { } return new Promise((resolve, reject) => { + const { command, args } = getDockerComposeCommand(); + + const dockerCompose = spawn(command, args, { stdio: "inherit" }); const timeout = setTimeout(() => { dockerCompose.kill(); reject(new Error("Docker compose operation timed out after 5 minutes")); }, 300000); - const dockerCompose = spawn( - process.platform === "win32" ? "docker-compose.exe" : "docker", - process.platform === "win32" - ? ["-f", "docker-compose.dev.yaml", "up", "--build", "-d"] - : ["compose", "-f", "docker-compose.dev.yaml", "up", "--build", "-d"], - { stdio: "inherit" }, - ); - dockerCompose.on("error", (error) => { clearTimeout(timeout); console.error("Error running docker-compose:", error); From 034fdbc82eb990fa8e9c77b3bd4d6730ae4e3f54 Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Wed, 18 Dec 2024 00:02:26 +0530 Subject: [PATCH 03/11] removed erroneous empty gql block from unions.ts --- src/typeDefs/unions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/typeDefs/unions.ts b/src/typeDefs/unions.ts index d9c6501960..d19e478891 100644 --- a/src/typeDefs/unions.ts +++ b/src/typeDefs/unions.ts @@ -1,4 +1,4 @@ -import { gql } from "graphql-tag"; +// import { gql } from "graphql-tag"; // Place fields alphabetically to ensure easier lookup and navigation. -export const unions = gql``; +// export const unions = gql``; From cd5b86bff85ba9939261073d498034c34fc31942 Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Fri, 10 Jan 2025 23:47:43 +0530 Subject: [PATCH 04/11] fixed docker issue --- setup.ts | 75 +++++++++++++++++++++++++------------------------------- 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/setup.ts b/setup.ts index 760de987aa..15b0aedf90 100644 --- a/setup.ts +++ b/setup.ts @@ -267,7 +267,7 @@ export async function importData(): Promise { console.log("Importing sample data..."); if (process.env.NODE_ENV !== "test") { - await exec( + exec( "npm run import:sample-data", (error: ExecException | null, stdout: string, stderr: string) => { if (error) { @@ -1227,36 +1227,32 @@ async function main(): Promise { console.log("Couldn't find mongodb url"); return; } + const isDbEmpty = await checkDb(process.env.MONGO_DB_URL); if (!isDbEmpty) { const { shouldOverwriteData } = await inquirer.prompt({ type: "confirm", name: "shouldOverwriteData", - message: "Do you want to overwrite the existing data?", + message: + "Do you want to delete the existing data and import required default Data to start using Talawa?", default: false, }); if (shouldOverwriteData) { await wipeExistingData(process.env.MONGO_DB_URL); - const { overwriteDefaultData } = await inquirer.prompt({ + // Import Default Data + await importDefaultData(); + + // Prompt to import Sample Data + const { importSampleData } = await inquirer.prompt({ type: "confirm", - name: "overwriteDefaultData", + name: "importSampleData", message: - "Do you want to import the required default data to start using Talawa in a production environment?", + "Do you want to import Talawa sample data for testing and evaluation purposes?", default: false, }); - if (overwriteDefaultData) { - await importDefaultData(); - } else { - const { overwriteSampleData } = await inquirer.prompt({ - type: "confirm", - name: "overwriteSampleData", - message: - "Do you want to import Talawa sample data for testing and evaluation purposes?", - default: false, - }); - if (overwriteSampleData) { - await importData(); - } + + if (importSampleData) { + await importData(); } } } else { @@ -1267,35 +1263,19 @@ async function main(): Promise { "Do you want to import Talawa sample data for testing and evaluation purposes?", default: false, }); - await wipeExistingData(process.env.MONGO_DB_URL); if (shouldImportSampleData) { await importData(); } else { await importDefaultData(); } } - } - - console.log( - "\nCongratulations! Talawa API has been successfully setup! 🥂🎉", - ); - - const { shouldStartDockerContainers } = await inquirer.prompt({ - type: "confirm", - name: "shouldStartDockerContainers", - message: "Do you want to start the Docker containers now?", - default: true, - }); - - const { shouldImportSampleData } = await inquirer.prompt({ - type: "confirm", - name: "shouldImportSampleData", - message: - "Do you want to import Talawa sample data for testing and evaluation purposes?", - default: true, - }); - - if (isDockerInstallation) { + } else { + const { shouldStartDockerContainers } = await inquirer.prompt({ + type: "confirm", + name: "shouldStartDockerContainers", + message: "Do you want to start the Docker containers now?", + default: true, + }); if (shouldStartDockerContainers) { console.log("Starting docker container..."); try { @@ -1329,6 +1309,15 @@ async function main(): Promise { } } + await importDefaultData(); + const { shouldImportSampleData } = await inquirer.prompt({ + type: "confirm", + name: "shouldImportSampleData", + message: + "Do you want to import Talawa sample data for testing and evaluation purposes?", + default: true, + }); + if (shouldImportSampleData) { await importData(); } @@ -1337,6 +1326,10 @@ async function main(): Promise { } } } + + console.log( + "\nCongratulations! Talawa API has been successfully setup! 🥂🎉", + ); } main(); From a5f95942babd0b6c2733338c19c73d548376b983 Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Sat, 11 Jan 2025 01:08:39 +0530 Subject: [PATCH 05/11] added dirname --- setup.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/setup.ts b/setup.ts index 15b0aedf90..d435cba216 100644 --- a/setup.ts +++ b/setup.ts @@ -2,7 +2,7 @@ import * as cryptolib from "crypto"; import dotenv from "dotenv"; import fs from "fs"; import inquirer from "inquirer"; -import path from "path"; +import path, { dirname } from "path"; import type { ExecException } from "child_process"; import { exec, spawn, execSync } from "child_process"; import { MongoClient } from "mongodb"; @@ -31,6 +31,7 @@ import { verifySmtpConnection } from "@setup/verifySmtpConnection"; import { loadDefaultOrganiation } from "@utilities/loadDefaultOrg"; import { isMinioInstalled } from "@setup/isMinioInstalled"; import { installMinio } from "@setup/installMinio"; +import { fileURLToPath } from "url"; dotenv.config(); @@ -141,10 +142,11 @@ export async function accessAndRefreshTokens( function transactionLogPath(logPath: string | null): void { const config = dotenv.parse(fs.readFileSync(".env")); + const currDir = dirname(fileURLToPath(import.meta.url)); config.LOG = "true"; if (!logPath) { // Check if the logs/transaction.log file exists, if not, create it - const defaultLogPath = path.resolve(__dirname, "logs"); + const defaultLogPath = path.resolve(currDir, "logs"); const defaultLogFile = path.join(defaultLogPath, "transaction.log"); if (!fs.existsSync(defaultLogPath)) { console.log("Creating logs/transaction.log file..."); @@ -154,7 +156,7 @@ function transactionLogPath(logPath: string | null): void { config.LOG_PATH = defaultLogFile; } else { // Remove the logs files, if exists - const logsDirPath = path.resolve(__dirname, "logs"); + const logsDirPath = path.resolve(currDir, "logs"); if (fs.existsSync(logsDirPath)) { fs.readdirSync(logsDirPath).forEach((file: string) => { if (file !== "README.md") { @@ -221,7 +223,7 @@ export async function wipeExistingData(url: string): Promise { console.log("All existing data has been deleted."); } } catch { - console.error("Could not connect to database to check for data"); + throw new Error("Could not connect to database to check for data"); } client.close(); // return shouldImport; @@ -237,6 +239,7 @@ export async function wipeExistingData(url: string): Promise { export async function checkDb(url: string): Promise { let dbEmpty = false; const client = new MongoClient(`${url}`); + console.log(`Connecting to database...`); try { await client.connect(); const db = client.db(); @@ -248,7 +251,7 @@ export async function checkDb(url: string): Promise { dbEmpty = true; } } catch { - console.error("Could not connect to database to check for data"); + throw new Error("Could not connect to database to check for data"); } client.close(); return dbEmpty; @@ -278,7 +281,7 @@ export async function importData(): Promise { console.error(`Error: ${stderr}`); abort(); } - console.log(`Output: ${stdout}`); + console.log(`\nOutput: ${stdout}`); }, ); } From 9de22b44798f9093b34acf1884fc066b0675d150 Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Sat, 11 Jan 2025 23:52:27 +0530 Subject: [PATCH 06/11] Added tests for custom log path --- setup.ts | 10 +++---- tests/setup/transactionLog.spec.ts | 46 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 tests/setup/transactionLog.spec.ts diff --git a/setup.ts b/setup.ts index d435cba216..db9d931ab0 100644 --- a/setup.ts +++ b/setup.ts @@ -140,7 +140,7 @@ export async function accessAndRefreshTokens( } } -function transactionLogPath(logPath: string | null): void { +export function transactionLogPath(logPath: string | null): void { const config = dotenv.parse(fs.readFileSync(".env")); const currDir = dirname(fileURLToPath(import.meta.url)); config.LOG = "true"; @@ -169,12 +169,12 @@ function transactionLogPath(logPath: string | null): void { } } -async function askForTransactionLogPath(): Promise { +export async function askForTransactionLogPath(): Promise { let logPath: string | null = null; let isValidPath = false; while (!isValidPath) { - const response = await inquirer.prompt([ + const { logpath } = await inquirer.prompt([ { type: "input", name: "logPath", @@ -182,7 +182,7 @@ async function askForTransactionLogPath(): Promise { default: null, }, ]); - logPath = response.logPath; + logPath = logpath; if (logPath && fs.existsSync(logPath)) { try { @@ -468,7 +468,7 @@ export async function mongoDB(): Promise { For Docker setup */ -function getDockerComposeCommand(): { command: string; args: string[] } { +export function getDockerComposeCommand(): { command: string; args: string[] } { let dockerComposeCmd = "docker-compose"; // Default to v1 let args = ["-f", "docker-compose.dev.yaml", "up", "--build", "-d"]; diff --git a/tests/setup/transactionLog.spec.ts b/tests/setup/transactionLog.spec.ts new file mode 100644 index 0000000000..c1d785db75 --- /dev/null +++ b/tests/setup/transactionLog.spec.ts @@ -0,0 +1,46 @@ +import { it, expect, vi, describe, beforeEach } from "vitest"; +import inquirer from "inquirer"; +import fs from "fs"; +import { askForTransactionLogPath } from "../../setup"; + +/* + Test Case 1: + Description: function askForSuperAdminEmail should return email as entered. + Expected Behavior: When the askForSuperAdminEmail function is called, it should prompt the user to enter an email address, and upon entering, it should return the entered email address. + + Test Case 2: + Description: superAdmin prompts user, updates .env_test, and does not throw errors. + Expected Behavior: When the superAdmin function is called, it should prompt the user to enter an email address for the last resort super admin, update the .env_test file with the entered email address, and it should not throw any errors during execution. + + Note: Each test case involves mocking user input using inquirer, executing the relevant function, and asserting the expected behavior by checking the returned email or the updated .env_test file. +*/ +describe("Setup -> transactionLogPath", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("should return the transaction log path as entered by the user", async () => { + const currDir = process.cwd(); + const testPath = `${currDir}/tests/setup/test-transaction.log`; + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ logpath: testPath }); + vi.spyOn(fs, "existsSync").mockReturnValueOnce(true); + vi.spyOn(fs, "accessSync").mockReturnValueOnce(undefined); + + const result = await askForTransactionLogPath(); + expect(result).toEqual(testPath); + }); + + it("should handle invalid path and prompt user again", async () => { + const currDir = process.cwd(); + const testPath = `${currDir}/tests/setup/test-transaction.log`; + vi.spyOn(inquirer, "prompt") + .mockResolvedValueOnce({ logpath: "invalidpath" }) + .mockResolvedValueOnce({ logpath: testPath }); + vi.spyOn(fs, "existsSync") + .mockReturnValueOnce(false) + .mockReturnValueOnce(true); + vi.spyOn(fs, "accessSync").mockReturnValueOnce(undefined); + const result = await askForTransactionLogPath(); + expect(result).toEqual(testPath); + }); +}); From 344be334e18d647df1b34eddd4d94ab3dac3ef37 Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Tue, 14 Jan 2025 03:22:46 +0530 Subject: [PATCH 07/11] added tests for data importation --- setup.ts | 235 +++++++++-------- tests/setup/dataImportFlow.spec.ts | 399 +++++++++++++++++++++++++++++ 2 files changed, 532 insertions(+), 102 deletions(-) create mode 100644 tests/setup/dataImportFlow.spec.ts diff --git a/setup.ts b/setup.ts index db9d931ab0..818440ebbd 100644 --- a/setup.ts +++ b/setup.ts @@ -487,7 +487,7 @@ export function getDockerComposeCommand(): { command: string; args: string[] } { } const DOCKER_COMPOSE_TIMEOUT_MS = 300000; -async function runDockerComposeWithLogs(): Promise { +export async function runDockerComposeWithLogs(): Promise { // Check if Docker daemon is running try { await new Promise((resolve, reject) => { @@ -915,6 +915,126 @@ export async function configureMinio( console.log("[MINIO] MinIO configuration added successfully.\n"); } +export async function dataImportWithoutDocker( + checkDb: (url: string) => Promise, + wipeExistingData: (url: string) => Promise, + importDefaultData: () => Promise, + importData: () => Promise, +): Promise { + if (!process.env.MONGO_DB_URL) { + console.log("Couldn't find mongodb url"); + return; + } + + const isDbEmpty = await checkDb(process.env.MONGO_DB_URL); + if (!isDbEmpty) { + const { shouldOverwriteData } = await inquirer.prompt({ + type: "confirm", + name: "shouldOverwriteData", + message: + "Do you want to delete the existing data and import required default Data to start using Talawa?", + default: false, + }); + if (shouldOverwriteData) { + await wipeExistingData(process.env.MONGO_DB_URL); + // Import Default Data + + // Prompt to import Sample Data + const { importSampleData } = await inquirer.prompt({ + type: "confirm", + name: "importSampleData", + message: + "Do you want to import Talawa sample data for testing and evaluation purposes?", + default: false, + }); + + if (importSampleData) { + await importData(); + } else await importDefaultData(); + } + } else { + const { shouldImportSampleData } = await inquirer.prompt({ + type: "confirm", + name: "shouldImportSampleData", + message: + "Do you want to import Talawa sample data for testing and evaluation purposes?", + default: false, + }); + if (shouldImportSampleData) { + await importData(); + } else { + await importDefaultData(); + } + } +} + +async function connectToDatabase(): Promise { + console.log("Waiting for mongoDB to be ready..."); + let isConnected = false; + const maxRetries = 30; // 30 seconds timeout + let retryCount = 0; + while (!isConnected) { + if (retryCount >= maxRetries) { + throw new Error( + "Timed out waiting for MongoDB to be ready after 30 seconds", + ); + } + try { + const client = new MongoClient(process.env.MONGO_DB_URL as string); + await client.connect(); + await client.db().command({ ping: 1 }); + client.close(); + isConnected = true; + console.log("MongoDB is ready!"); + } catch (err) { + const error = err instanceof Error ? err.message : String(err); + console.log( + `Waiting for MongoDB to be ready... Retry ${retryCount + 1}/${maxRetries}. Details: ${error}`, + ); + await new Promise((resolve) => setTimeout(resolve, 1000)); + retryCount++; + } + } + return Promise.resolve(); +} + +export async function dataImportWithDocker( + runDockerComposeWithLogs: () => Promise, + importDefaultData: () => Promise, + importData: () => Promise, + connectToDatabase: () => Promise, +): Promise { + const { shouldStartDockerContainers } = await inquirer.prompt({ + type: "confirm", + name: "shouldStartDockerContainers", + message: "Do you want to start the Docker containers now?", + default: true, + }); + if (shouldStartDockerContainers) { + console.log("Starting docker container..."); + try { + await runDockerComposeWithLogs(); + console.log("Docker containers have been built successfully!"); + // Wait for mongoDB to be ready + await connectToDatabase(); + + const { shouldImportSampleData } = await inquirer.prompt({ + type: "confirm", + name: "shouldImportSampleData", + message: + "Do you want to import Talawa sample data for testing and evaluation purposes?", + default: true, + }); + + if (shouldImportSampleData) { + await importData(); + } else await importDefaultData(); + } catch (err) { + console.log("Some error occurred: " + err); + } + } +} + /** * The main function sets up the Talawa API by prompting the user to configure various environment * variables and import sample data if desired. @@ -1226,108 +1346,19 @@ async function main(): Promise { await setImageUploadSize(imageSizeLimit * 1000); if (!isDockerInstallation) { - if (!process.env.MONGO_DB_URL) { - console.log("Couldn't find mongodb url"); - return; - } - - const isDbEmpty = await checkDb(process.env.MONGO_DB_URL); - if (!isDbEmpty) { - const { shouldOverwriteData } = await inquirer.prompt({ - type: "confirm", - name: "shouldOverwriteData", - message: - "Do you want to delete the existing data and import required default Data to start using Talawa?", - default: false, - }); - if (shouldOverwriteData) { - await wipeExistingData(process.env.MONGO_DB_URL); - // Import Default Data - await importDefaultData(); - - // Prompt to import Sample Data - const { importSampleData } = await inquirer.prompt({ - type: "confirm", - name: "importSampleData", - message: - "Do you want to import Talawa sample data for testing and evaluation purposes?", - default: false, - }); - - if (importSampleData) { - await importData(); - } - } - } else { - const { shouldImportSampleData } = await inquirer.prompt({ - type: "confirm", - name: "shouldImportSampleData", - message: - "Do you want to import Talawa sample data for testing and evaluation purposes?", - default: false, - }); - if (shouldImportSampleData) { - await importData(); - } else { - await importDefaultData(); - } - } + await dataImportWithoutDocker( + checkDb, + wipeExistingData, + importDefaultData, + importData, + ); } else { - const { shouldStartDockerContainers } = await inquirer.prompt({ - type: "confirm", - name: "shouldStartDockerContainers", - message: "Do you want to start the Docker containers now?", - default: true, - }); - if (shouldStartDockerContainers) { - console.log("Starting docker container..."); - try { - await runDockerComposeWithLogs(); - console.log("Docker containers have been built successfully!"); - // Wait for mongoDB to be ready - console.log("Waiting for mongoDB to be ready..."); - let isConnected = false; - const maxRetries = 30; // 30 seconds timeout - let retryCount = 0; - while (!isConnected) { - if (retryCount >= maxRetries) { - throw new Error( - "Timed out waiting for MongoDB to be ready after 30 seconds", - ); - } - try { - const client = new MongoClient(process.env.MONGO_DB_URL as string); - await client.connect(); - await client.db().command({ ping: 1 }); - client.close(); - isConnected = true; - console.log("MongoDB is ready!"); - } catch (err) { - const error = err instanceof Error ? err.message : String(err); - console.log( - `Waiting for MongoDB to be ready... Retry ${retryCount + 1}/${maxRetries}. Details: ${error}`, - ); - await new Promise((resolve) => setTimeout(resolve, 1000)); - retryCount++; - } - } - - await importDefaultData(); - const { shouldImportSampleData } = await inquirer.prompt({ - type: "confirm", - name: "shouldImportSampleData", - message: - "Do you want to import Talawa sample data for testing and evaluation purposes?", - default: true, - }); - - if (shouldImportSampleData) { - await importData(); - } - } catch (err) { - console.log("Some error occurred: " + err); - } - } + await dataImportWithDocker( + runDockerComposeWithLogs, + importDefaultData, + importData, + connectToDatabase, + ); } console.log( diff --git a/tests/setup/dataImportFlow.spec.ts b/tests/setup/dataImportFlow.spec.ts new file mode 100644 index 0000000000..02a14a053a --- /dev/null +++ b/tests/setup/dataImportFlow.spec.ts @@ -0,0 +1,399 @@ +import { describe, it, vi, expect, beforeEach, afterEach } from "vitest"; +import { dataImportWithoutDocker, dataImportWithDocker } from "../../setup"; +import inquirer from "inquirer"; + +describe("Data Importation Without Docker", () => { + const mockEnv = { ...process.env }; // Backup the environment variables + + beforeEach(() => { + process.env.MONGO_DB_URL = "mongodb://localhost:27017/sample-table"; + vi.resetAllMocks(); + }); + + afterEach(() => { + process.env = { ...mockEnv }; // Restore environment variables + }); + + it("should import sample data if the database is empty and user opts to import", async () => { + const checkDbMock = vi + .fn() + .mockImplementation(async (): Promise => { + return true; + }); + const wipeExistingDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + shouldImportSampleData: true, + }); + + await dataImportWithoutDocker( + checkDbMock, + wipeExistingDataMock, + importDefaultDataMock, + importDataMock, + ); + expect(checkDbMock).toBeCalled(); + expect(wipeExistingDataMock).not.toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); + expect(importDataMock).toBeCalled(); + }); + + it("should import sample data if the database is empty and user opts not to import", async () => { + const checkDbMock = vi + .fn() + .mockImplementation(async (): Promise => { + return true; + }); + const wipeExistingDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + shouldImportSampleData: false, + }); + + await dataImportWithoutDocker( + checkDbMock, + wipeExistingDataMock, + importDefaultDataMock, + importDataMock, + ); + expect(checkDbMock).toBeCalled(); + expect(wipeExistingDataMock).not.toBeCalled(); + expect(importDefaultDataMock).toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); + + it("should import sample data if the database is not empty and user opts to overwrite and import sample data", async () => { + const checkDbMock = vi + .fn() + .mockImplementation(async (): Promise => { + return false; + }); + const wipeExistingDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt") + .mockResolvedValueOnce({ shouldOverwriteData: true }) + .mockResolvedValueOnce({ importSampleData: true }); + + await dataImportWithoutDocker( + checkDbMock, + wipeExistingDataMock, + importDefaultDataMock, + importDataMock, + ); + expect(checkDbMock).toBeCalled(); + expect(wipeExistingDataMock).toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); + expect(importDataMock).toBeCalled(); + }); + + it("should not import sample data if the database is not empty and user opts to overwrite and not import sample data", async () => { + const checkDbMock = vi + .fn() + .mockImplementation(async (): Promise => { + return false; + }); + const wipeExistingDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt") + .mockResolvedValueOnce({ shouldOverwriteData: true }) + .mockResolvedValueOnce({ importSampleData: false }); + + await dataImportWithoutDocker( + checkDbMock, + wipeExistingDataMock, + importDefaultDataMock, + importDataMock, + ); + expect(checkDbMock).toBeCalled(); + expect(wipeExistingDataMock).toBeCalled(); + expect(importDefaultDataMock).toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); + + it("should complete the data importation if the database is not empty and user does not opt to overwrite", async () => { + const checkDbMock = vi + .fn() + .mockImplementation(async (): Promise => { + return false; + }); + const wipeExistingDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + shouldOverwriteData: false, + }); + + await dataImportWithoutDocker( + checkDbMock, + wipeExistingDataMock, + importDefaultDataMock, + importDataMock, + ); + expect(checkDbMock).toBeCalled(); + expect(wipeExistingDataMock).not.toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); +}); + +describe("Data Importation With Docker", () => { + const mockEnv = { ...process.env }; // Backup the environment variables + + beforeEach(() => { + process.env.MONGO_DB_URL = "mongodb://localhost:27017/sample-table"; + vi.resetAllMocks(); + }); + + afterEach(() => { + process.env = { ...mockEnv }; // Restore environment variables + }); + + it("should complete data importation if user opts not to start containers", async () => { + const runDockerComposeMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const connectDatabaseMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + shouldStartDockerContainers: false, + }); + + await dataImportWithDocker( + runDockerComposeMock, + importDefaultDataMock, + importDataMock, + connectDatabaseMock, + ); + expect(runDockerComposeMock).not.toBeCalled(); + expect(connectDatabaseMock).not.toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); + + it("should terminate execution if error is encountered while starting containers", async () => { + const runDockerComposeMock = vi + .fn() + .mockImplementation(async (): Promise => { + throw new Error("Error starting containers"); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const connectDatabaseMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + shouldStartDockerContainers: true, + }); + + await dataImportWithDocker( + runDockerComposeMock, + importDefaultDataMock, + importDataMock, + connectDatabaseMock, + ); + expect(runDockerComposeMock).toBeCalled(); + expect(connectDatabaseMock).not.toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); + + it("should terminate execution if error is encountered while connecting to database", async () => { + const runDockerComposeMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const connectDatabaseMock = vi + .fn() + .mockImplementation(async (): Promise => { + throw new Error("Error starting containers"); + }); + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + shouldStartDockerContainers: true, + }); + + await dataImportWithDocker( + runDockerComposeMock, + importDefaultDataMock, + importDataMock, + connectDatabaseMock, + ); + expect(runDockerComposeMock).toBeCalled(); + expect(connectDatabaseMock).toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); + + it("should import sample data if user opts to import sample data", async () => { + const runDockerComposeMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const connectDatabaseMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt") + .mockResolvedValueOnce({ shouldStartDockerContainers: true }) + .mockResolvedValueOnce({ shouldImportSampleData: true }); + + await dataImportWithDocker( + runDockerComposeMock, + importDefaultDataMock, + importDataMock, + connectDatabaseMock, + ); + expect(runDockerComposeMock).toBeCalled(); + expect(connectDatabaseMock).toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); + expect(importDataMock).toBeCalled(); + }); + + it("should import default data if user does not opt to import sample data", async () => { + const runDockerComposeMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const connectDatabaseMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt") + .mockResolvedValueOnce({ shouldStartDockerContainers: true }) + .mockResolvedValueOnce({ shouldImportSampleData: false }); + + await dataImportWithDocker( + runDockerComposeMock, + importDefaultDataMock, + importDataMock, + connectDatabaseMock, + ); + expect(runDockerComposeMock).toBeCalled(); + expect(connectDatabaseMock).toBeCalled(); + expect(importDefaultDataMock).toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); +}); From d9494cb7caf4d13056699760add48c9b97a5db50 Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Tue, 14 Jan 2025 03:31:55 +0530 Subject: [PATCH 08/11] improved error handling --- setup.ts | 12 +++++++--- tests/setup/dataImportFlow.spec.ts | 38 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/setup.ts b/setup.ts index 818440ebbd..06af92325b 100644 --- a/setup.ts +++ b/setup.ts @@ -922,11 +922,17 @@ export async function dataImportWithoutDocker( importData: () => Promise, ): Promise { if (!process.env.MONGO_DB_URL) { - console.log("Couldn't find mongodb url"); - return; + throw new Error("MongoDB URL is not configured. Please run setup first."); } - const isDbEmpty = await checkDb(process.env.MONGO_DB_URL); + let isDbEmpty: boolean; + try { + isDbEmpty = await checkDb(process.env.MONGO_DB_URL); + } catch (error) { + throw new Error( + `Failed to check database: ${error instanceof Error ? error.message : String(error)}`, + ); + } if (!isDbEmpty) { const { shouldOverwriteData } = await inquirer.prompt({ type: "confirm", diff --git a/tests/setup/dataImportFlow.spec.ts b/tests/setup/dataImportFlow.spec.ts index 02a14a053a..2ce8234e9b 100644 --- a/tests/setup/dataImportFlow.spec.ts +++ b/tests/setup/dataImportFlow.spec.ts @@ -198,6 +198,44 @@ describe("Data Importation Without Docker", () => { expect(importDefaultDataMock).not.toBeCalled(); expect(importDataMock).not.toBeCalled(); }); + + it("should handle database connection failure gracefully", async () => { + const checkDbMock = vi + .fn() + .mockImplementation(async (): Promise => { + return false; + }); + const wipeExistingDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const errorMessage = "Database connection failed"; + checkDbMock.mockRejectedValueOnce(new Error(errorMessage)); + + await expect( + dataImportWithoutDocker( + checkDbMock, + wipeExistingDataMock, + importDefaultDataMock, + importDataMock, + ), + ).rejects.toThrow(errorMessage); + + expect(wipeExistingDataMock).not.toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); }); describe("Data Importation With Docker", () => { From 96ea0be4916a1eae88352995331bf9cea06b4b4c Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Tue, 14 Jan 2025 06:02:23 +0530 Subject: [PATCH 09/11] added test for docker timeout --- tests/setup/dataImportFlow.spec.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/setup/dataImportFlow.spec.ts b/tests/setup/dataImportFlow.spec.ts index 2ce8234e9b..7239627acf 100644 --- a/tests/setup/dataImportFlow.spec.ts +++ b/tests/setup/dataImportFlow.spec.ts @@ -434,4 +434,31 @@ describe("Data Importation With Docker", () => { expect(importDefaultDataMock).toBeCalled(); expect(importDataMock).not.toBeCalled(); }); + + it("should handle Docker container startup timeout", async () => { + const runDockerComposeMock = vi.fn().mockImplementation(async () => { + return new Promise((_, reject) => { + setTimeout(() => { + reject(new Error("Docker compose operation timed out")); + }, 3000); + }); + }); + const importDataMock = vi.fn(); + const importDefaultDataMock = vi.fn(); + const connectDatabaseMock = vi.fn(); + + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + shouldStartDockerContainers: true, + }); + + await dataImportWithDocker( + runDockerComposeMock, + importDefaultDataMock, + importDataMock, + connectDatabaseMock, + ); + + expect(runDockerComposeMock).toBeCalled(); + expect(connectDatabaseMock).not.toBeCalled(); + }); }); From 5f6b0a0503b155d790eadd89488bf08d52f3b062 Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Tue, 14 Jan 2025 06:17:54 +0530 Subject: [PATCH 10/11] updated test name --- tests/setup/dataImportFlow.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/setup/dataImportFlow.spec.ts b/tests/setup/dataImportFlow.spec.ts index 7239627acf..2d9a3a9722 100644 --- a/tests/setup/dataImportFlow.spec.ts +++ b/tests/setup/dataImportFlow.spec.ts @@ -14,7 +14,7 @@ describe("Data Importation Without Docker", () => { process.env = { ...mockEnv }; // Restore environment variables }); - it("should import sample data if the database is empty and user opts to import", async () => { + it("should import sample data if the database is empty and user opts to import sample data", async () => { const checkDbMock = vi .fn() .mockImplementation(async (): Promise => { @@ -51,7 +51,7 @@ describe("Data Importation Without Docker", () => { expect(importDataMock).toBeCalled(); }); - it("should import sample data if the database is empty and user opts not to import", async () => { + it("should not import sample data if the database is empty and user opts not to import sample data", async () => { const checkDbMock = vi .fn() .mockImplementation(async (): Promise => { From d1cccc082b9c07a7c572823ef83bf446474b5df4 Mon Sep 17 00:00:00 2001 From: Aad1tya27 Date: Wed, 15 Jan 2025 04:31:30 +0530 Subject: [PATCH 11/11] changed the flow of prompts and unit tests --- setup.ts | 83 +++++++++++------ tests/setup/dataImportFlow.spec.ts | 143 ++++++++++++++++++++++++++--- 2 files changed, 184 insertions(+), 42 deletions(-) diff --git a/setup.ts b/setup.ts index 06af92325b..0d42f47d5a 100644 --- a/setup.ts +++ b/setup.ts @@ -922,7 +922,8 @@ export async function dataImportWithoutDocker( importData: () => Promise, ): Promise { if (!process.env.MONGO_DB_URL) { - throw new Error("MongoDB URL is not configured. Please run setup first."); + console.log("Couldn't find mongodb url"); + return; } let isDbEmpty: boolean; @@ -937,39 +938,55 @@ export async function dataImportWithoutDocker( const { shouldOverwriteData } = await inquirer.prompt({ type: "confirm", name: "shouldOverwriteData", - message: - "Do you want to delete the existing data and import required default Data to start using Talawa?", + message: "Do you want to overwrite the existing data?", default: false, }); if (shouldOverwriteData) { - await wipeExistingData(process.env.MONGO_DB_URL); - // Import Default Data - - // Prompt to import Sample Data - const { importSampleData } = await inquirer.prompt({ + const { overwriteDefaultData } = await inquirer.prompt({ type: "confirm", - name: "importSampleData", + name: "overwriteDefaultData", message: - "Do you want to import Talawa sample data for testing and evaluation purposes?", + "Do you want to DESTROY the existing data, replacing it with the default data required for a fresh production system?", default: false, }); - - if (importSampleData) { - await importData(); - } else await importDefaultData(); + if (overwriteDefaultData) { + await wipeExistingData(process.env.MONGO_DB_URL); + await importDefaultData(); + } else { + const { overwriteSampleData } = await inquirer.prompt({ + type: "confirm", + name: "overwriteSampleData", + message: + "Do you want to DESTROY the existing data, replacing it with data suitable for testing and evaluation?", + default: false, + }); + if (overwriteSampleData) { + await wipeExistingData(process.env.MONGO_DB_URL); + await importData(); + } + } } } else { - const { shouldImportSampleData } = await inquirer.prompt({ + const { shouldImportDefaultData } = await inquirer.prompt({ type: "confirm", - name: "shouldImportSampleData", + name: "shouldImportDefaultData", message: - "Do you want to import Talawa sample data for testing and evaluation purposes?", + "Do you want to import default data required for a fresh production system?", default: false, }); - if (shouldImportSampleData) { - await importData(); - } else { + if (shouldImportDefaultData) { await importDefaultData(); + } else { + const { shouldImportSampleData } = await inquirer.prompt({ + type: "confirm", + name: "shouldImportSampleData", + message: + "Do you want to import data suitable for testing and evaluation?", + default: false, + }); + if (shouldImportSampleData) { + await importData(); + } } } } @@ -1024,17 +1041,27 @@ export async function dataImportWithDocker( // Wait for mongoDB to be ready await connectToDatabase(); - const { shouldImportSampleData } = await inquirer.prompt({ + const { shouldImportDefaultData } = await inquirer.prompt({ type: "confirm", - name: "shouldImportSampleData", + name: "shouldImportDefaultData", message: - "Do you want to import Talawa sample data for testing and evaluation purposes?", - default: true, + "Do you want to import default data required for a fresh production system?", + default: false, }); - - if (shouldImportSampleData) { - await importData(); - } else await importDefaultData(); + if (shouldImportDefaultData) { + await importDefaultData(); + } else { + const { shouldImportSampleData } = await inquirer.prompt({ + type: "confirm", + name: "shouldImportSampleData", + message: + "Do you want to import data suitable for testing and evaluation?", + default: false, + }); + if (shouldImportSampleData) { + await importData(); + } + } } catch (err) { console.log("Some error occurred: " + err); } diff --git a/tests/setup/dataImportFlow.spec.ts b/tests/setup/dataImportFlow.spec.ts index 2d9a3a9722..674a6a06f2 100644 --- a/tests/setup/dataImportFlow.spec.ts +++ b/tests/setup/dataImportFlow.spec.ts @@ -14,7 +14,7 @@ describe("Data Importation Without Docker", () => { process.env = { ...mockEnv }; // Restore environment variables }); - it("should import sample data if the database is empty and user opts to import sample data", async () => { + it("should import default data if the database is empty and user opts to import default data", async () => { const checkDbMock = vi .fn() .mockImplementation(async (): Promise => { @@ -36,9 +36,46 @@ describe("Data Importation Without Docker", () => { return Promise.resolve(); }); vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ - shouldImportSampleData: true, + shouldImportDefaultData: true, }); + await dataImportWithoutDocker( + checkDbMock, + wipeExistingDataMock, + importDefaultDataMock, + importDataMock, + ); + expect(checkDbMock).toBeCalled(); + expect(wipeExistingDataMock).not.toBeCalled(); + expect(importDefaultDataMock).toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); + + it("should import sample data if the database is empty and user opts not to import default data but import sample data", async () => { + const checkDbMock = vi + .fn() + .mockImplementation(async (): Promise => { + return true; + }); + const wipeExistingDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt") + .mockResolvedValueOnce({ shouldImportDefaultData: false }) + .mockResolvedValueOnce({ shouldImportSampleData: true }); + await dataImportWithoutDocker( checkDbMock, wipeExistingDataMock, @@ -51,7 +88,7 @@ describe("Data Importation Without Docker", () => { expect(importDataMock).toBeCalled(); }); - it("should not import sample data if the database is empty and user opts not to import sample data", async () => { + it("should do no-op if the database is empty and user imports neither default nor sample data", async () => { const checkDbMock = vi .fn() .mockImplementation(async (): Promise => { @@ -72,9 +109,9 @@ describe("Data Importation Without Docker", () => { .mockImplementation(async (): Promise => { return Promise.resolve(); }); - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ - shouldImportSampleData: false, - }); + vi.spyOn(inquirer, "prompt") + .mockResolvedValueOnce({ shouldImportDefaultData: false }) + .mockResolvedValueOnce({ shouldImportSampleData: false }); await dataImportWithoutDocker( checkDbMock, @@ -84,7 +121,7 @@ describe("Data Importation Without Docker", () => { ); expect(checkDbMock).toBeCalled(); expect(wipeExistingDataMock).not.toBeCalled(); - expect(importDefaultDataMock).toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); expect(importDataMock).not.toBeCalled(); }); @@ -111,7 +148,8 @@ describe("Data Importation Without Docker", () => { }); vi.spyOn(inquirer, "prompt") .mockResolvedValueOnce({ shouldOverwriteData: true }) - .mockResolvedValueOnce({ importSampleData: true }); + .mockResolvedValueOnce({ overwriteDefaultData: false }) + .mockResolvedValueOnce({ overwriteSampleData: true }); await dataImportWithoutDocker( checkDbMock, @@ -125,7 +163,7 @@ describe("Data Importation Without Docker", () => { expect(importDataMock).toBeCalled(); }); - it("should not import sample data if the database is not empty and user opts to overwrite and not import sample data", async () => { + it("should import default data if the database is not empty and user opts to overwrite and import default data", async () => { const checkDbMock = vi .fn() .mockImplementation(async (): Promise => { @@ -148,7 +186,7 @@ describe("Data Importation Without Docker", () => { }); vi.spyOn(inquirer, "prompt") .mockResolvedValueOnce({ shouldOverwriteData: true }) - .mockResolvedValueOnce({ importSampleData: false }); + .mockResolvedValueOnce({ overwriteDefaultData: true }); await dataImportWithoutDocker( checkDbMock, @@ -162,7 +200,45 @@ describe("Data Importation Without Docker", () => { expect(importDataMock).not.toBeCalled(); }); - it("should complete the data importation if the database is not empty and user does not opt to overwrite", async () => { + it("should do no-op if db not empty and user imports neither default nor sample data", async () => { + const checkDbMock = vi + .fn() + .mockImplementation(async (): Promise => { + return false; + }); + const wipeExistingDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt") + .mockResolvedValueOnce({ shouldOverwriteData: true }) + .mockResolvedValueOnce({ overwriteDefaultData: false }) + .mockResolvedValueOnce({ overwriteSampleData: false }); + + await dataImportWithoutDocker( + checkDbMock, + wipeExistingDataMock, + importDefaultDataMock, + importDataMock, + ); + expect(checkDbMock).toBeCalled(); + expect(wipeExistingDataMock).not.toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); + + it("should do no-op if db not empty and user opts not to overwrite", async () => { const checkDbMock = vi .fn() .mockImplementation(async (): Promise => { @@ -250,7 +326,7 @@ describe("Data Importation With Docker", () => { process.env = { ...mockEnv }; // Restore environment variables }); - it("should complete data importation if user opts not to start containers", async () => { + it("should do no-op if user opts not to start containers", async () => { const runDockerComposeMock = vi .fn() .mockImplementation(async (): Promise => { @@ -361,6 +437,43 @@ describe("Data Importation With Docker", () => { expect(importDataMock).not.toBeCalled(); }); + it("should import default data if user opts to import default data", async () => { + const runDockerComposeMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const importDefaultDataMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + const connectDatabaseMock = vi + .fn() + .mockImplementation(async (): Promise => { + return Promise.resolve(); + }); + vi.spyOn(inquirer, "prompt") + .mockResolvedValueOnce({ shouldStartDockerContainers: true }) + .mockResolvedValueOnce({ shouldImportDefaultData: true }); + + await dataImportWithDocker( + runDockerComposeMock, + importDefaultDataMock, + importDataMock, + connectDatabaseMock, + ); + expect(runDockerComposeMock).toBeCalled(); + expect(connectDatabaseMock).toBeCalled(); + expect(importDefaultDataMock).toBeCalled(); + expect(importDataMock).not.toBeCalled(); + }); + it("should import sample data if user opts to import sample data", async () => { const runDockerComposeMock = vi .fn() @@ -384,6 +497,7 @@ describe("Data Importation With Docker", () => { }); vi.spyOn(inquirer, "prompt") .mockResolvedValueOnce({ shouldStartDockerContainers: true }) + .mockResolvedValueOnce({ shouldImportDefaultData: false }) .mockResolvedValueOnce({ shouldImportSampleData: true }); await dataImportWithDocker( @@ -398,7 +512,7 @@ describe("Data Importation With Docker", () => { expect(importDataMock).toBeCalled(); }); - it("should import default data if user does not opt to import sample data", async () => { + it("should do no-op if user opts to import neither sample data nor default data", async () => { const runDockerComposeMock = vi .fn() .mockImplementation(async (): Promise => { @@ -421,6 +535,7 @@ describe("Data Importation With Docker", () => { }); vi.spyOn(inquirer, "prompt") .mockResolvedValueOnce({ shouldStartDockerContainers: true }) + .mockResolvedValueOnce({ shouldImportDefaultData: false }) .mockResolvedValueOnce({ shouldImportSampleData: false }); await dataImportWithDocker( @@ -431,7 +546,7 @@ describe("Data Importation With Docker", () => { ); expect(runDockerComposeMock).toBeCalled(); expect(connectDatabaseMock).toBeCalled(); - expect(importDefaultDataMock).toBeCalled(); + expect(importDefaultDataMock).not.toBeCalled(); expect(importDataMock).not.toBeCalled(); });