From 043ef266fbfc2541c43d34fbc9732bf20e7897f4 Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 23 Sep 2025 14:44:01 +0200 Subject: [PATCH 1/9] chore: run format on test hooks --- testing/integration-testing-hooks.ts | 173 +++++++++++++++++---------- 1 file changed, 108 insertions(+), 65 deletions(-) diff --git a/testing/integration-testing-hooks.ts b/testing/integration-testing-hooks.ts index 8d3ee3fd2..7f7991671 100644 --- a/testing/integration-testing-hooks.ts +++ b/testing/integration-testing-hooks.ts @@ -1,13 +1,13 @@ -import child_process from 'child_process'; -import { promises as fs } from 'fs'; -import { MongoClient, MongoClientOptions } from 'mongodb'; -import path from 'path'; -import semver from 'semver'; -import { promisify } from 'util'; -import which from 'which'; -import { ConnectionString } from 'mongodb-connection-string-url'; -import { MongoCluster, MongoClusterOptions } from 'mongodb-runner'; -import { downloadCryptLibrary } from '../packages/build/src/packaging/download-crypt-library'; +import child_process from "child_process"; +import { promises as fs } from "fs"; +import { MongoClient, MongoClientOptions } from "mongodb"; +import path from "path"; +import semver from "semver"; +import { promisify } from "util"; +import which from "which"; +import { ConnectionString } from "mongodb-connection-string-url"; +import { MongoCluster, MongoClusterOptions } from "mongodb-runner"; +import { downloadCryptLibrary } from "../packages/build/src/packaging/download-crypt-library"; const execFile = promisify(child_process.execFile); @@ -20,7 +20,7 @@ function ciLog(...args: any[]) { // Return the path to the temporary directory and ensure that it exists. async function getTmpdir(): Promise { - const tmpdir = path.resolve(__dirname, '..', 'tmp'); + const tmpdir = path.resolve(__dirname, "..", "tmp"); await fs.mkdir(tmpdir, { recursive: true }); return tmpdir; } @@ -31,11 +31,11 @@ export class MongodSetup { _setConnectionString: (connectionString: string) => void; _serverVersion: string | null = null; _isCommunityServer: boolean | null = null; - _bindir = ''; + _bindir = ""; constructor(connectionString?: string) { - this._setConnectionString = (connectionString: string) => {}; // Make TypeScript happy. - this._connectionString = new Promise(resolve => { + this._setConnectionString = (connectionString: string) => {}; // Make TypeScript happy. + this._connectionString = new Promise((resolve) => { this._setConnectionString = resolve; }); @@ -45,15 +45,21 @@ export class MongodSetup { } async start(): Promise { - throw new Error('Server not managed'); + throw new Error("Server not managed"); } async stop(): Promise { - throw new Error('Server not managed'); + throw new Error("Server not managed"); } - async connectionString(searchParams: Partial> = {}, uriOptions: Partial = {}): Promise { - if (Object.keys(searchParams).length + Object.keys(uriOptions).length === 0) { + async connectionString( + searchParams: Partial> = {}, + uriOptions: Partial = {} + ): Promise { + if ( + Object.keys(searchParams).length + Object.keys(uriOptions).length === + 0 + ) { return this._connectionString; } @@ -70,7 +76,7 @@ export class MongodSetup { } async port(): Promise { - return (await this.hostport()).split(':').reverse()[0]; + return (await this.hostport()).split(":").reverse()[0]; } async hostport(): Promise { @@ -82,8 +88,8 @@ export class MongodSetup { return this._serverVersion; } - const { version } = await this.withClient(async client => { - return await client.db('db1').admin().serverStatus(); + const { version } = await this.withClient(async (client) => { + return await client.db("db1").admin().serverStatus(); }); this._serverVersion = version; return version; @@ -94,10 +100,10 @@ export class MongodSetup { return this._isCommunityServer; } - const { modules } = await this.withClient(async client => { - return await client.db('db1').admin().command({ buildInfo: 1 }); + const { modules } = await this.withClient(async (client) => { + return await client.db("db1").admin().command({ buildInfo: 1 }); }); - const isCommunityServer = !modules.includes('enterprise'); + const isCommunityServer = !modules.includes("enterprise"); this._isCommunityServer = isCommunityServer; return isCommunityServer; } @@ -123,8 +129,15 @@ export class MongodSetup { export class MongoRunnerSetup extends MongodSetup { private static _usedDirPrefix: Record = {}; - private static _buildDirPath(id: string, version?: string, topology?: string) { - const prefix = [id, version, topology].filter(Boolean).join('-').replace(/[^a-zA-Z0-9_.-]/g, ''); + private static _buildDirPath( + id: string, + version?: string, + topology?: string + ) { + const prefix = [id, version, topology] + .filter(Boolean) + .join("-") + .replace(/[^a-zA-Z0-9_.-]/g, ""); this._usedDirPrefix[prefix] ??= 0; @@ -148,15 +161,19 @@ export class MongoRunnerSetup extends MongodSetup { if (this._cluster) return; const tmpDir = await getTmpdir(); const version = process.env.MONGOSH_SERVER_TEST_VERSION; - const dirPath = MongoRunnerSetup._buildDirPath(this._id, version, this._opts.topology); + const dirPath = MongoRunnerSetup._buildDirPath( + this._id, + version, + this._opts.topology + ); this._cluster = await MongoCluster.start({ - topology: 'standalone', - tmpDir: path.join(tmpDir, 'mongodb-runner', 'dbs', dirPath), - logDir: path.join(tmpDir, 'mongodb-runner', 'logs', dirPath), + topology: "standalone", + tmpDir: path.join(tmpDir, "mongodb-runner", "dbs", dirPath), + logDir: path.join(tmpDir, "mongodb-runner", "logs", dirPath), version: version, - ...this._opts - }) + ...this._opts, + }); this._setConnectionString(this._cluster.connectionString); } @@ -168,17 +185,25 @@ export class MongoRunnerSetup extends MongodSetup { } async function getInstalledMongodVersion(): Promise { - await Promise.all([which('mongod'), which('mongos')]); - const { stdout } = await execFile('mongod', ['--version']); - const { version } = stdout.match(/^db version (?.+)$/m)!.groups as any; + await Promise.all([which("mongod"), which("mongos")]); + const { stdout } = await execFile("mongod", ["--version"]); + const { version } = stdout.match(/^db version (?.+)$/m)! + .groups as any; return version; } -export async function downloadCurrentCryptSharedLibrary(versionSpec?: string): Promise { - if (process.platform === 'linux') { - return (await downloadCryptLibrary(`linux-${process.arch.replace('ppc64', 'ppc64le')}` as any, versionSpec)).cryptLibrary; +export async function downloadCurrentCryptSharedLibrary( + versionSpec?: string +): Promise { + if (process.platform === "linux") { + return ( + await downloadCryptLibrary( + `linux-${process.arch.replace("ppc64", "ppc64le")}` as any, + versionSpec + ) + ).cryptLibrary; } - return (await downloadCryptLibrary('host', versionSpec)).cryptLibrary; + return (await downloadCryptLibrary("host", versionSpec)).cryptLibrary; } /** @@ -188,15 +213,18 @@ export async function downloadCurrentCryptSharedLibrary(versionSpec?: string): P * @export * @returns {MongodSetup} - Object with information about the started server. */ -let sharedSetup : MongodSetup | null = null; -export function startTestServer(id: string, args: Partial = {}): MongodSetup { +let sharedSetup: MongodSetup | null = null; +export function startTestServer( + id: string, + args: Partial = {} +): MongodSetup { const server = new MongoRunnerSetup(id, args); - before(async function() { - this.timeout(120_000); // Include potential mongod download time. + before(async function () { + this.timeout(120_000); // Include potential mongod download time. await server.start(); }); - after(async function() { + after(async function () { this.timeout(30_000); await server.stop(); }); @@ -204,7 +232,6 @@ export function startTestServer(id: string, args: Partial = return server; } - /** * Starts or reuse an existing shared local server managed by this process. * @@ -221,10 +248,10 @@ export function startSharedTestServer(): MongodSetup { return new MongodSetup(process.env.MONGOSH_TEST_SERVER_URL); } - const server = sharedSetup ?? (sharedSetup = new MongoRunnerSetup('shared')); + const server = sharedSetup ?? (sharedSetup = new MongoRunnerSetup("shared")); - before(async function() { - this.timeout(120_000); // Include potential mongod download time. + before(async function () { + this.timeout(120_000); // Include potential mongod download time. await server.start(); }); @@ -233,7 +260,7 @@ export function startSharedTestServer(): MongodSetup { return server; } -global.after?.(async function() { +global.after?.(async function () { if (sharedSetup !== null) { this.timeout(30_000); await sharedSetup.stop(); @@ -242,15 +269,18 @@ global.after?.(async function() { // The same as startTestServer(), except that this starts multiple servers // in parallel in the same before() call. -export function startTestCluster(id: string, ...argLists: Partial[]): MongodSetup[] { - const servers = argLists.map(args => new MongoRunnerSetup(id, args)); +export function startTestCluster( + id: string, + ...argLists: Partial[] +): MongodSetup[] { + const servers = argLists.map((args) => new MongoRunnerSetup(id, args)); - before(async function() { + before(async function () { this.timeout(90_000 + 30_000 * servers.length); await Promise.all(servers.map((server: MongodSetup) => server.start())); }); - after(async function() { + after(async function () { this.timeout(30_000 * servers.length); await Promise.all(servers.map((server: MongodSetup) => server.stop())); }); @@ -258,8 +288,16 @@ export function startTestCluster(id: string, ...argLists: Partial /[0-9]+/.test(num) ? num : '0') - .join('.'); + testServerVersion = testServerVersion + .split("-")[0] + .split(".") + .map((num) => (/[0-9]+/.test(num) ? num : "0")) + .join("."); } skipIfVersion(this, testServerVersion, semverCondition); }); From 8d69118a642e90b68950e29511ab75df248b921a Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 24 Oct 2025 17:49:44 +0200 Subject: [PATCH 2/9] chore(testing): prevent excessive MongoDB binary downloads in testing This is making use of the new locking mechanism with the runner and downloader in https://github.com/mongodb-js/devtools-shared/pull/580 as well as having a single place for all binaries. --- package-lock.json | 120 ++++++++++++++++++++++----- package.json | 2 +- testing/integration-testing-hooks.ts | 2 + 3 files changed, 102 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index c60df8514..6585d4f1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,7 +61,7 @@ "husky": "^9.0.11", "mocha": "^10.2.0", "mongodb": "^6.19.0", - "mongodb-runner": "^5.7.1", + "mongodb-runner": "^6.0.0", "node-gyp": "^9.0.0 || ^10.2.0", "nyc": "^15.1.0", "pkg-up": "^3.1.0", @@ -7282,9 +7282,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", - "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz", + "integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -25434,10 +25434,13 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "license": "MIT" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minimist-options": { "version": "4.1.0", @@ -25875,20 +25878,43 @@ } }, "node_modules/mongodb-download-url": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/mongodb-download-url/-/mongodb-download-url-1.5.5.tgz", - "integrity": "sha512-8HLqKVVuKQBinKRZbDu0YSzwLfD/Wb//vOIm3CMk0/2AzZzp0pg+8E+DAkx7VLEdoyuPVWLU5v/doODjXlPYSA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/mongodb-download-url/-/mongodb-download-url-1.7.0.tgz", + "integrity": "sha512-Dj9l3/MzvgAO+no7zaBIbQL0Ilsb0jo5Y8WijkB/sCnagoh3KgnN0Vxu5awglK/75QrEObduf9wa+U0fjxvYMw==", "license": "Apache-2.0", "dependencies": { - "debug": "^4.1.1", - "minimist": "^1.2.3", - "node-fetch": "^2.6.1", - "semver": "^7.1.1" + "debug": "^4.4.0", + "minimist": "^1.2.8", + "node-fetch": "^2.7.0", + "semver": "^7.7.1" }, "bin": { "mongodb-download-url": "bin/mongodb-download-url.js" } }, + "node_modules/mongodb-download-url/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mongodb-download-url/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/mongodb-log-writer": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/mongodb-log-writer/-/mongodb-log-writer-2.4.3.tgz", @@ -25909,15 +25935,15 @@ "optional": true }, "node_modules/mongodb-runner": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/mongodb-runner/-/mongodb-runner-5.7.1.tgz", - "integrity": "sha512-/MBEP2DcMpNbpSsXqG+lgFqYehCd2qasdWIfKuv4jGKwLoDPv/mWoQYAQDFAC2xaxjb576Y2LwUAeYeB1KPZdg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mongodb-runner/-/mongodb-runner-6.0.0.tgz", + "integrity": "sha512-ijhBVCcTWRlauxp4UdIuktfPjEqlt8yOo0u7XyE99HdaITzL0BjS4x/dOyNzddewC6gw5wxH1uYx+Uo7GfeUnw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@mongodb-js/mongodb-downloader": "^0.3.7", - "@mongodb-js/saslprep": "^1.1.9", - "debug": "^4.3.4", + "@mongodb-js/mongodb-downloader": "^1.0.0", + "@mongodb-js/saslprep": "^1.3.2", + "debug": "^4.4.0", "mongodb": "^6.9.0", "mongodb-connection-string-url": "^3.0.0", "yargs": "^17.7.2" @@ -25926,6 +25952,21 @@ "mongodb-runner": "bin/runner.js" } }, + "node_modules/mongodb-runner/node_modules/@mongodb-js/mongodb-downloader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/mongodb-downloader/-/mongodb-downloader-1.0.0.tgz", + "integrity": "sha512-xz4zr/5RWfAaHp9Kf7XPaLNxKaisl2NJGFEDQRNjB/ru79LdpLQbbSjQazTOl6JLcBvAjNNvauTeTwmgEvSUlA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.0", + "decompress": "^4.2.1", + "mongodb-download-url": "^1.7.0", + "node-fetch": "^2.7.0", + "proper-lockfile": "^4.1.2", + "tar": "^6.1.15" + } + }, "node_modules/mongodb-runner/node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -25941,6 +25982,31 @@ "node": ">=12" } }, + "node_modules/mongodb-runner/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mongodb-runner/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/mongodb-runner/node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -29102,6 +29168,18 @@ "node": ">= 8" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", diff --git a/package.json b/package.json index ee94a7f92..a7b666ea7 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "husky": "^9.0.11", "mocha": "^10.2.0", "mongodb": "^6.19.0", - "mongodb-runner": "^5.7.1", + "mongodb-runner": "^6.0.0", "node-gyp": "^9.0.0 || ^10.2.0", "nyc": "^15.1.0", "pkg-up": "^3.1.0", diff --git a/testing/integration-testing-hooks.ts b/testing/integration-testing-hooks.ts index 7f7991671..d43c8ef9e 100644 --- a/testing/integration-testing-hooks.ts +++ b/testing/integration-testing-hooks.ts @@ -8,6 +8,7 @@ import which from "which"; import { ConnectionString } from "mongodb-connection-string-url"; import { MongoCluster, MongoClusterOptions } from "mongodb-runner"; import { downloadCryptLibrary } from "../packages/build/src/packaging/download-crypt-library"; +import { dir } from "console"; const execFile = promisify(child_process.execFile); @@ -171,6 +172,7 @@ export class MongoRunnerSetup extends MongodSetup { topology: "standalone", tmpDir: path.join(tmpDir, "mongodb-runner", "dbs", dirPath), logDir: path.join(tmpDir, "mongodb-runner", "logs", dirPath), + binDir: path.join(tmpDir, "mongodb-runner"), version: version, ...this._opts, }); From c3c5abe9a826cf6bd2dd56a5701dfa59d3952c81 Mon Sep 17 00:00:00 2001 From: gagik Date: Mon, 27 Oct 2025 17:01:35 +0100 Subject: [PATCH 3/9] chore: use downloadDir, not binDir --- testing/integration-testing-hooks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/integration-testing-hooks.ts b/testing/integration-testing-hooks.ts index d43c8ef9e..73f59d2f6 100644 --- a/testing/integration-testing-hooks.ts +++ b/testing/integration-testing-hooks.ts @@ -172,7 +172,7 @@ export class MongoRunnerSetup extends MongodSetup { topology: "standalone", tmpDir: path.join(tmpDir, "mongodb-runner", "dbs", dirPath), logDir: path.join(tmpDir, "mongodb-runner", "logs", dirPath), - binDir: path.join(tmpDir, "mongodb-runner"), + downloadDir: path.join(tmpDir, "mongodb-runner"), version: version, ...this._opts, }); From d282ce077864deea3a552048371baed036894b27 Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 28 Oct 2025 14:44:57 +0100 Subject: [PATCH 4/9] chore: remove unnecessary import, fix type --- testing/integration-testing-hooks.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testing/integration-testing-hooks.ts b/testing/integration-testing-hooks.ts index 73f59d2f6..9c3c97ac9 100644 --- a/testing/integration-testing-hooks.ts +++ b/testing/integration-testing-hooks.ts @@ -8,7 +8,6 @@ import which from "which"; import { ConnectionString } from "mongodb-connection-string-url"; import { MongoCluster, MongoClusterOptions } from "mongodb-runner"; import { downloadCryptLibrary } from "../packages/build/src/packaging/download-crypt-library"; -import { dir } from "console"; const execFile = promisify(child_process.execFile); @@ -55,7 +54,7 @@ export class MongodSetup { async connectionString( searchParams: Partial> = {}, - uriOptions: Partial = {} + uriOptions: Partial = {} ): Promise { if ( Object.keys(searchParams).length + Object.keys(uriOptions).length === From 66f996e4365722d8a8c9cc348b0bc8fa96e67dba Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 28 Oct 2025 18:57:07 +0100 Subject: [PATCH 5/9] chore: fix formatting and lint --- packages/build/src/index.ts | 1 + testing/.eslintrc.js | 10 ++++ testing/fake-kms.ts | 68 ++++++++++++++++------------ testing/integration-testing-hooks.ts | 34 ++++++-------- testing/tsconfig-lint.json | 5 ++ testing/tsconfig.json | 4 ++ 6 files changed, 72 insertions(+), 50 deletions(-) create mode 100644 testing/.eslintrc.js create mode 100644 testing/tsconfig-lint.json create mode 100644 testing/tsconfig.json diff --git a/packages/build/src/index.ts b/packages/build/src/index.ts index 3ca60fb33..d2208f863 100644 --- a/packages/build/src/index.ts +++ b/packages/build/src/index.ts @@ -9,6 +9,7 @@ import type { Config, PackageVariant } from './config'; import { updateJsonFeedCTA } from './download-center'; import Ajv from 'ajv'; +export { downloadCryptLibrary } from './packaging/download-crypt-library'; export { getArtifactUrl, downloadMongoDb }; const validCommands: (ReleaseCommand | 'trigger-release' | 'update-cta')[] = [ diff --git a/testing/.eslintrc.js b/testing/.eslintrc.js new file mode 100644 index 000000000..029cc5860 --- /dev/null +++ b/testing/.eslintrc.js @@ -0,0 +1,10 @@ +const { fixCygwinPath } = require("@mongodb-js/eslint-config-mongosh/utils"); + +module.exports = { + root: true, + extends: ["@mongodb-js/eslint-config-mongosh"], + parserOptions: { + tsconfigRootDir: fixCygwinPath(__dirname), + project: ["./tsconfig-lint.json"], + }, +}; diff --git a/testing/fake-kms.ts b/testing/fake-kms.ts index 669b9df55..8dfa3edee 100644 --- a/testing/fake-kms.ts +++ b/testing/fake-kms.ts @@ -1,21 +1,23 @@ -import DuplexPair from 'duplexpair'; -import http from 'http'; +import DuplexPair from "duplexpair"; +import http from "http"; // Exact values specified by RFC6749 ;) -const oauthToken = { access_token: '2YotnFZFEjr1zCsicMWpAA', expires_in: 3600 }; +const oauthToken = { access_token: "2YotnFZFEjr1zCsicMWpAA", expires_in: 3600 }; -type RequestData = { url: string, body: string }; +type RequestData = { url: string; body: string }; type HandlerFunction = (data: RequestData) => any; -type HandlerList = { host: RegExp, handler: HandlerFunction }[]; +type HandlerList = { host: RegExp; handler: HandlerFunction }[]; type Duplex = NodeJS.ReadableStream & NodeJS.WritableStream; // Return a Duplex stream that behaves like an HTTP stream, with the 'server' // being provided by the handler function in this case (which is expected // to return JSON). -export function makeFakeHTTPConnection(handlerList: HandlerList): Duplex & { requests: http.IncomingMessage[] } { +export function makeFakeHTTPConnection( + handlerList: HandlerList +): Duplex & { requests: http.IncomingMessage[] } { const { socket1, socket2 } = new DuplexPair(); const server = makeFakeHTTPServer(handlerList); - server.emit('connection', socket2); + server.emit("connection", socket2); return Object.assign(socket1, { requests: server.requests }); } @@ -26,29 +28,31 @@ export function makeFakeHTTPServer(handlerList: HandlerList): FakeHTTPServer { const server = http.createServer((req, res) => { (server as FakeHTTPServer).requests.push(req); let foundHandler: HandlerFunction | undefined; - const host = req.headers['host']; + const host = req.headers["host"]; for (const potentialHandler of handlerList) { - if (potentialHandler.host.test(host ?? '')) { + if (potentialHandler.host.test(host ?? "")) { foundHandler = potentialHandler.handler; break; } } if (!foundHandler) { res.writeHead(404, { - 'Content-Type': 'text/plain' + "Content-Type": "text/plain", }); res.end(`Host ${host} not found`); return; } const handler = foundHandler; // Makes TS happy - let body = ''; - req.setEncoding('utf8').on('data', chunk => { body += chunk; }); - req.on('end', () => { + let body = ""; + req.setEncoding("utf8").on("data", (chunk) => { + body += chunk; + }); + req.on("end", () => { res.writeHead(200, { - 'Content-Type': 'application/json' + "Content-Type": "application/json", }); - res.end(JSON.stringify(handler({ url: req.url ?? '', body }))); + res.end(JSON.stringify(handler({ url: req.url ?? "", body }))); }); }); return Object.assign(server, { requests: [] }); @@ -57,7 +61,7 @@ export function makeFakeHTTPServer(handlerList: HandlerList): FakeHTTPServer { export const fakeAWSHandlers: HandlerList = [ { host: /\.amazonaws\.com$/, handler: awsHandler }, { host: /\.microsoftonline.com$|\.azure.net$/, handler: azureHandler }, - { host: /\.googleapis.com$/, handler: gcpHandler } + { host: /\.googleapis.com$/, handler: gcpHandler }, ]; function awsHandler({ body }: RequestData): any { @@ -68,31 +72,35 @@ function awsHandler({ body }: RequestData): any { // both KeyId and Plaintext so that they are available for generating // the decryption response, which also provides the KeyId and Plaintext // based on the CiphertextBlob alone. - const CiphertextBlob = Buffer.from(request.KeyId + '\0' + request.Plaintext).toString('base64') + const CiphertextBlob = Buffer.from( + request.KeyId + "\0" + request.Plaintext + ).toString("base64"); return { CiphertextBlob, - EncryptionAlgorithm: 'SYMMETRIC_DEFAULT', - KeyId: request.KeyId + EncryptionAlgorithm: "SYMMETRIC_DEFAULT", + KeyId: request.KeyId, }; } else { - let [ KeyId, Plaintext ] = Buffer.from(request.CiphertextBlob, 'base64').toString().split('\0'); + let [KeyId, Plaintext] = Buffer.from(request.CiphertextBlob, "base64") + .toString() + .split("\0"); // Do not return invalid base64 https://jira.mongodb.org/browse/MONGOCRYPT-525 - if (Buffer.from(KeyId, 'base64').toString('base64') !== KeyId) { - KeyId = 'invalid0'; + if (Buffer.from(KeyId, "base64").toString("base64") !== KeyId) { + KeyId = "invalid0"; } - if (Buffer.from(Plaintext, 'base64').toString('base64') !== Plaintext) { - Plaintext = 'invalid1'; + if (Buffer.from(Plaintext, "base64").toString("base64") !== Plaintext) { + Plaintext = "invalid1"; } return { Plaintext, - EncryptionAlgorithm: 'SYMMETRIC_DEFAULT', - KeyId + EncryptionAlgorithm: "SYMMETRIC_DEFAULT", + KeyId, }; } } function azureHandler({ body, url }: RequestData): any { - if (url.endsWith('/token')) { + if (url.endsWith("/token")) { return oauthToken; } else if (url.match(/\/(un)?wrapkey/)) { // Just act as if this was encrypted. @@ -101,12 +109,12 @@ function azureHandler({ body, url }: RequestData): any { } function gcpHandler({ body, url }: RequestData): any { - if (url.endsWith('/token')) { + if (url.endsWith("/token")) { return oauthToken; - } else if (url.endsWith(':encrypt')) { + } else if (url.endsWith(":encrypt")) { // Here we also just perform noop encryption. return { ciphertext: JSON.parse(body).plaintext }; - } else if (url.endsWith(':decrypt')) { + } else if (url.endsWith(":decrypt")) { return { plaintext: JSON.parse(body).ciphertext }; } } diff --git a/testing/integration-testing-hooks.ts b/testing/integration-testing-hooks.ts index 9c3c97ac9..15e3781bb 100644 --- a/testing/integration-testing-hooks.ts +++ b/testing/integration-testing-hooks.ts @@ -1,23 +1,17 @@ +/* eslint-disable mocha/no-exports */ import child_process from "child_process"; import { promises as fs } from "fs"; -import { MongoClient, MongoClientOptions } from "mongodb"; +import { MongoClient, type MongoClientOptions } from "mongodb"; import path from "path"; import semver from "semver"; import { promisify } from "util"; import which from "which"; import { ConnectionString } from "mongodb-connection-string-url"; -import { MongoCluster, MongoClusterOptions } from "mongodb-runner"; -import { downloadCryptLibrary } from "../packages/build/src/packaging/download-crypt-library"; +import { MongoCluster, type MongoClusterOptions } from "mongodb-runner"; +import { downloadCryptLibrary } from "@mongosh/build"; const execFile = promisify(child_process.execFile); -const isCI = !!process.env.IS_CI; -function ciLog(...args: any[]) { - if (isCI) { - console.error(...args); - } -} - // Return the path to the temporary directory and ensure that it exists. async function getTmpdir(): Promise { const tmpdir = path.resolve(__dirname, "..", "tmp"); @@ -26,6 +20,7 @@ async function getTmpdir(): Promise { } // Represents one running test server instance. +// eslint-disable-next-line mocha/no-exports export class MongodSetup { _connectionString: Promise; _setConnectionString: (connectionString: string) => void; @@ -34,7 +29,9 @@ export class MongodSetup { _bindir = ""; constructor(connectionString?: string) { - this._setConnectionString = (connectionString: string) => {}; // Make TypeScript happy. + this._setConnectionString = () => { + // no-op to make TypeScript happy. + }; this._connectionString = new Promise((resolve) => { this._setConnectionString = resolve; }); @@ -45,11 +42,11 @@ export class MongodSetup { } async start(): Promise { - throw new Error("Server not managed"); + await Promise.reject(new Error("Server not managed")); } async stop(): Promise { - throw new Error("Server not managed"); + await Promise.reject(new Error("Server not managed")); } async connectionString( @@ -115,7 +112,7 @@ export class MongodSetup { return await fn(client); } finally { if (client) { - client.close(); + await client.close(); } } } @@ -193,18 +190,15 @@ async function getInstalledMongodVersion(): Promise { return version; } -export async function downloadCurrentCryptSharedLibrary( - versionSpec?: string -): Promise { +export async function downloadCurrentCryptSharedLibrary(): Promise { if (process.platform === "linux") { return ( await downloadCryptLibrary( - `linux-${process.arch.replace("ppc64", "ppc64le")}` as any, - versionSpec + `linux-${process.arch.replace("ppc64", "ppc64le")}` as any ) ).cryptLibrary; } - return (await downloadCryptLibrary("host", versionSpec)).cryptLibrary; + return (await downloadCryptLibrary("host")).cryptLibrary; } /** diff --git a/testing/tsconfig-lint.json b/testing/tsconfig-lint.json new file mode 100644 index 000000000..981423b3e --- /dev/null +++ b/testing/tsconfig-lint.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["**/*"], + "exclude": ["node_modules", "dist", "lib"] +} diff --git a/testing/tsconfig.json b/testing/tsconfig.json new file mode 100644 index 000000000..bd920dc26 --- /dev/null +++ b/testing/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@mongodb-js/tsconfig-mongosh/tsconfig.common.json", + "include": ["**/*", "../packages/e2e-tests/test/integration-testing-hooks.ts"] +} From 48a7ec580bc66040a165959936bbaf86d04cab42 Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 28 Oct 2025 19:07:58 +0100 Subject: [PATCH 6/9] chore: fix formatting of all testing folder files --- testing/.eslintrc.js | 2 +- testing/disable-dns-srv.js | 21 ++++++++++++--------- testing/eventually.ts | 22 ++++++++++++++++------ testing/fake-kms.ts | 1 - testing/tsconfig-lint.json | 5 ----- testing/tsconfig.json | 3 ++- 6 files changed, 31 insertions(+), 23 deletions(-) delete mode 100644 testing/tsconfig-lint.json diff --git a/testing/.eslintrc.js b/testing/.eslintrc.js index 029cc5860..771e143e7 100644 --- a/testing/.eslintrc.js +++ b/testing/.eslintrc.js @@ -5,6 +5,6 @@ module.exports = { extends: ["@mongodb-js/eslint-config-mongosh"], parserOptions: { tsconfigRootDir: fixCygwinPath(__dirname), - project: ["./tsconfig-lint.json"], + project: ["./tsconfig.json"], }, }; diff --git a/testing/disable-dns-srv.js b/testing/disable-dns-srv.js index 7f75a0cef..122896d74 100644 --- a/testing/disable-dns-srv.js +++ b/testing/disable-dns-srv.js @@ -1,14 +1,18 @@ -'use strict'; -const dns = require('dns'); -console.log('!!! Disabling SRV and TXT DNS queries through the Node.js API !!!'); +"use strict"; +const dns = require("dns"); +// eslint-disable-next-line no-console +console.log( + "!!! Disabling SRV and TXT DNS queries through the Node.js API !!!" +); const origResolve = dns.resolve; const origPromiseResolve = dns.promises.resolve; -const err = Object.assign(new Error('SRV and TXT not available'), { code: 'ENODATA' }); +const err = Object.assign(new Error("SRV and TXT not available"), { + code: "ENODATA", +}); dns.resolve = (hostname, type, cb) => { - if (type === 'SRV' || type === 'TXT') - return process.nextTick(cb, err); + if (type === "SRV" || type === "TXT") return process.nextTick(cb, err); return origResolve(hostname, type, cb); }; dns.resolveSrv = (hostname, cb) => { @@ -17,9 +21,8 @@ dns.resolveSrv = (hostname, cb) => { dns.resolveTxt = (hostname, cb) => { return process.nextTick(cb, err); }; -dns.promises.resolve = async(hostname, type) => { - if (type === 'SRV' || type === 'TXT') - throw err; +dns.promises.resolve = async (hostname, type) => { + if (type === "SRV" || type === "TXT") throw err; await origPromiseResolve; }; dns.promises.resolveSrv = () => Promise.reject(err); diff --git a/testing/eventually.ts b/testing/eventually.ts index c3e1f05a9..e2ab3bdfc 100644 --- a/testing/eventually.ts +++ b/testing/eventually.ts @@ -1,9 +1,16 @@ -export async function eventually(fn: Function, opts: { initialInterval?: number; timeout?: number, backoffFactor?: number } = {}): Promise { +export async function eventually( + fn: Function, + opts: { + initialInterval?: number; + timeout?: number; + backoffFactor?: number; + } = {} +): Promise { const options = { initialInterval: 100, timeout: 10000, backoffFactor: 1, // no backoff - ...opts + ...opts, }; let attempts = calculateAttempts(options); @@ -21,7 +28,7 @@ export async function eventually(fn: Function, opts: { initialInterval?: number; err = e; } - await new Promise(resolve => setTimeout(resolve, currentInterval)); + await new Promise((resolve) => setTimeout(resolve, currentInterval)); currentInterval *= options.backoffFactor; } @@ -29,12 +36,16 @@ export async function eventually(fn: Function, opts: { initialInterval?: number; Object.assign(err, { timedOut: true, timeout: options.timeout, - message: `[Timed out ${options.timeout}ms] ${err.message}` + message: `[Timed out ${options.timeout}ms] ${err.message}`, }); throw err; } -function calculateAttempts(options: { initialInterval: number; timeout: number; backoffFactor: number; }): number { +function calculateAttempts(options: { + initialInterval: number; + timeout: number; + backoffFactor: number; +}): number { let totalInterval = 0; let attempts = 0; let interval = options.initialInterval; @@ -46,4 +57,3 @@ function calculateAttempts(options: { initialInterval: number; timeout: number; } return attempts; } - diff --git a/testing/fake-kms.ts b/testing/fake-kms.ts index 8dfa3edee..ebe5aded6 100644 --- a/testing/fake-kms.ts +++ b/testing/fake-kms.ts @@ -66,7 +66,6 @@ export const fakeAWSHandlers: HandlerList = [ function awsHandler({ body }: RequestData): any { const request = JSON.parse(body); - let response; if (request.KeyId && request.Plaintext) { // Famously "unbreakable" base64 encryption ;) We use this to forward // both KeyId and Plaintext so that they are available for generating diff --git a/testing/tsconfig-lint.json b/testing/tsconfig-lint.json deleted file mode 100644 index 981423b3e..000000000 --- a/testing/tsconfig-lint.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*"], - "exclude": ["node_modules", "dist", "lib"] -} diff --git a/testing/tsconfig.json b/testing/tsconfig.json index bd920dc26..dfe2dd3a1 100644 --- a/testing/tsconfig.json +++ b/testing/tsconfig.json @@ -1,4 +1,5 @@ { "extends": "@mongodb-js/tsconfig-mongosh/tsconfig.common.json", - "include": ["**/*", "../packages/e2e-tests/test/integration-testing-hooks.ts"] + "include": ["**/*"], + "exclude": ["certificates"] } From 2f7792cab28f3c13382f237008401f245d68a35a Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 28 Oct 2025 19:33:11 +0100 Subject: [PATCH 7/9] chore: run reformat properly --- testing/.eslintrc.js | 6 +-- testing/.prettierrc.json | 1 + testing/certificates/README.md | 51 +++++++++++--------- testing/disable-dns-srv.js | 14 +++--- testing/fake-kms.ts | 52 ++++++++++----------- testing/integration-testing-hooks.ts | 70 ++++++++++++++-------------- testing/package.json | 6 +++ testing/tsconfig.json | 4 ++ 8 files changed, 112 insertions(+), 92 deletions(-) create mode 100644 testing/.prettierrc.json create mode 100644 testing/package.json diff --git a/testing/.eslintrc.js b/testing/.eslintrc.js index 771e143e7..0ce9c7915 100644 --- a/testing/.eslintrc.js +++ b/testing/.eslintrc.js @@ -1,10 +1,10 @@ -const { fixCygwinPath } = require("@mongodb-js/eslint-config-mongosh/utils"); +const { fixCygwinPath } = require('@mongodb-js/eslint-config-mongosh/utils'); module.exports = { root: true, - extends: ["@mongodb-js/eslint-config-mongosh"], + extends: ['@mongodb-js/eslint-config-mongosh'], parserOptions: { tsconfigRootDir: fixCygwinPath(__dirname), - project: ["./tsconfig.json"], + project: ['./tsconfig.json'], }, }; diff --git a/testing/.prettierrc.json b/testing/.prettierrc.json new file mode 100644 index 000000000..dfae21d04 --- /dev/null +++ b/testing/.prettierrc.json @@ -0,0 +1 @@ +"@mongodb-js/prettier-config-devtools" diff --git a/testing/certificates/README.md b/testing/certificates/README.md index 83ac3804e..dee8b982d 100644 --- a/testing/certificates/README.md +++ b/testing/certificates/README.md @@ -5,6 +5,7 @@ This directory contains all certificates and keys used in testing. To recreate the certificates follow the steps outlined below. ## Setup CA + 1. Create a new key to use for the CA: ``` openssl genrsa -out ca.key 4096 @@ -13,10 +14,11 @@ To recreate the certificates follow the steps outlined below. ``` openssl req -new -x509 -key ca.key -out ca.crt -days 99999 ``` - * Organization Name: `MongoDB` - * Organizational Unit Name: `DevTools` - * Common Name: `DevTools CA` + - Organization Name: `MongoDB` + - Organizational Unit Name: `DevTools` + - Common Name: `DevTools CA` 3. To sign and revoke certificates, an openssl config files is required. Create `ca.cnf` with the following content: + ``` [ca] default_ca=CA_default @@ -43,12 +45,14 @@ To recreate the certificates follow the steps outlined below. commonName=supplied emailAddress=optional ``` + 4. Ensure the `ca.db` file exists: ``` touch ca.db ``` ## Setup Server Certificate + 1. Create a new key to use for the server: ``` openssl genrsa -out server.key 4096 @@ -57,9 +61,9 @@ To recreate the certificates follow the steps outlined below. ``` openssl req -new -key server.key -out server.csr -days 99999 ``` - * Organization Name: `MongoDB` - * Organizational Unit Name: `DevTools` - * Common Name: `localhost` + - Organization Name: `MongoDB` + - Organizational Unit Name: `DevTools` + - Common Name: `localhost` 3. Sign the CSR to generate server certificate: ``` openssl ca -create_serial -config ca.cnf -in server.csr -out server.pem -days 99999 @@ -71,6 +75,7 @@ To recreate the certificates follow the steps outlined below. ``` ## Setup Server Certificate with invalid hostname + 1. Create a new key to use for the server: ``` openssl genrsa -out server-invalidhost.key 4096 @@ -79,9 +84,9 @@ To recreate the certificates follow the steps outlined below. ``` openssl req -new -key server-invalidhost.key -out server-invalidhost.csr -days 99999 ``` - * Organization Name: `MongoDB` - * Organizational Unit Name: `DevTools` - * Common Name: `invalidhost` + - Organization Name: `MongoDB` + - Organizational Unit Name: `DevTools` + - Common Name: `invalidhost` 3. Sign the CSR to generate server certificate: ``` openssl ca -create_serial -config ca.cnf -in server-invalidhost.csr -out server-invalidhost.pem -days 99999 @@ -93,6 +98,7 @@ To recreate the certificates follow the steps outlined below. ``` ## Setup "Non-CA" for testing invalid CA cert + 1. Create a new key to use for the Non CA: ``` openssl genrsa -out non-ca.key 4096 @@ -101,11 +107,12 @@ To recreate the certificates follow the steps outlined below. ``` openssl req -new -x509 -key non-ca.key -out non-ca.crt -days 99999 ``` - * Organization Name: `MongoDB` - * Organizational Unit Name: `DevTools` - * Common Name: `NOT DevTools CA` + - Organization Name: `MongoDB` + - Organizational Unit Name: `DevTools` + - Common Name: `NOT DevTools CA` ## Revoke Server Certificate and generate CRL + 1. Revoke the server's certificate: ``` openssl ca -config ca.cnf -revoke server.pem @@ -116,6 +123,7 @@ To recreate the certificates follow the steps outlined below. ``` ## Create Client Certificate from CA + 1. Create a new key to use for the client: ``` openssl genrsa -out client.key 4096 @@ -124,10 +132,10 @@ To recreate the certificates follow the steps outlined below. ``` openssl req -new -key client.key -out client.csr -days 99999 ``` - * Organization Name: `MongoDB` - * Organizational Unit Name: `DevTools Testers` - * Common Name: `Wonderwoman` - * E-Mail: `tester@example.com` + - Organization Name: `MongoDB` + - Organizational Unit Name: `DevTools Testers` + - Common Name: `Wonderwoman` + - E-Mail: `tester@example.com` 3. Sign the CSR to generate server certificate: ``` openssl ca -create_serial -config ca.cnf -in client.csr -out client.pem -days 99999 @@ -146,9 +154,10 @@ To recreate the certificates follow the steps outlined below. ``` openssl pkcs12 -inkey client.bundle.pem -in client.bundle.pem -export -out client.bundle.pfx ``` - * Password: `passw0rd` + - Password: `passw0rd` ## Create Client Certificate not from CA + 1. Create a new key to use for the Non CA: ``` openssl genrsa -out invalid-client.key 4096 @@ -157,10 +166,10 @@ To recreate the certificates follow the steps outlined below. ``` openssl req -new -x509 -key invalid-client.key -out invalid-client.crt -days 99999 ``` - * Organization Name: `MongoDB` - * Organizational Unit Name: `DevTools Testers` - * Common Name: `Wonderwoman` - * E-Mail: `tester@example.com` + - Organization Name: `MongoDB` + - Organizational Unit Name: `DevTools Testers` + - Common Name: `Wonderwoman` + - E-Mail: `tester@example.com` 3. Create a bundle with client key and certificate to use for connecting: ``` cat invalid-client.crt invalid-client.key > invalid-client.bundle.pem diff --git a/testing/disable-dns-srv.js b/testing/disable-dns-srv.js index 122896d74..309d46606 100644 --- a/testing/disable-dns-srv.js +++ b/testing/disable-dns-srv.js @@ -1,18 +1,18 @@ -"use strict"; -const dns = require("dns"); +'use strict'; +const dns = require('dns'); // eslint-disable-next-line no-console console.log( - "!!! Disabling SRV and TXT DNS queries through the Node.js API !!!" + '!!! Disabling SRV and TXT DNS queries through the Node.js API !!!' ); const origResolve = dns.resolve; const origPromiseResolve = dns.promises.resolve; -const err = Object.assign(new Error("SRV and TXT not available"), { - code: "ENODATA", +const err = Object.assign(new Error('SRV and TXT not available'), { + code: 'ENODATA', }); dns.resolve = (hostname, type, cb) => { - if (type === "SRV" || type === "TXT") return process.nextTick(cb, err); + if (type === 'SRV' || type === 'TXT') return process.nextTick(cb, err); return origResolve(hostname, type, cb); }; dns.resolveSrv = (hostname, cb) => { @@ -22,7 +22,7 @@ dns.resolveTxt = (hostname, cb) => { return process.nextTick(cb, err); }; dns.promises.resolve = async (hostname, type) => { - if (type === "SRV" || type === "TXT") throw err; + if (type === 'SRV' || type === 'TXT') throw err; await origPromiseResolve; }; dns.promises.resolveSrv = () => Promise.reject(err); diff --git a/testing/fake-kms.ts b/testing/fake-kms.ts index ebe5aded6..ee0e44dee 100644 --- a/testing/fake-kms.ts +++ b/testing/fake-kms.ts @@ -1,8 +1,8 @@ -import DuplexPair from "duplexpair"; -import http from "http"; +import DuplexPair from 'duplexpair'; +import http from 'http'; // Exact values specified by RFC6749 ;) -const oauthToken = { access_token: "2YotnFZFEjr1zCsicMWpAA", expires_in: 3600 }; +const oauthToken = { access_token: '2YotnFZFEjr1zCsicMWpAA', expires_in: 3600 }; type RequestData = { url: string; body: string }; type HandlerFunction = (data: RequestData) => any; @@ -17,7 +17,7 @@ export function makeFakeHTTPConnection( ): Duplex & { requests: http.IncomingMessage[] } { const { socket1, socket2 } = new DuplexPair(); const server = makeFakeHTTPServer(handlerList); - server.emit("connection", socket2); + server.emit('connection', socket2); return Object.assign(socket1, { requests: server.requests }); } @@ -28,31 +28,31 @@ export function makeFakeHTTPServer(handlerList: HandlerList): FakeHTTPServer { const server = http.createServer((req, res) => { (server as FakeHTTPServer).requests.push(req); let foundHandler: HandlerFunction | undefined; - const host = req.headers["host"]; + const host = req.headers['host']; for (const potentialHandler of handlerList) { - if (potentialHandler.host.test(host ?? "")) { + if (potentialHandler.host.test(host ?? '')) { foundHandler = potentialHandler.handler; break; } } if (!foundHandler) { res.writeHead(404, { - "Content-Type": "text/plain", + 'Content-Type': 'text/plain', }); res.end(`Host ${host} not found`); return; } const handler = foundHandler; // Makes TS happy - let body = ""; - req.setEncoding("utf8").on("data", (chunk) => { + let body = ''; + req.setEncoding('utf8').on('data', (chunk) => { body += chunk; }); - req.on("end", () => { + req.on('end', () => { res.writeHead(200, { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }); - res.end(JSON.stringify(handler({ url: req.url ?? "", body }))); + res.end(JSON.stringify(handler({ url: req.url ?? '', body }))); }); }); return Object.assign(server, { requests: [] }); @@ -72,34 +72,34 @@ function awsHandler({ body }: RequestData): any { // the decryption response, which also provides the KeyId and Plaintext // based on the CiphertextBlob alone. const CiphertextBlob = Buffer.from( - request.KeyId + "\0" + request.Plaintext - ).toString("base64"); + request.KeyId + '\0' + request.Plaintext + ).toString('base64'); return { CiphertextBlob, - EncryptionAlgorithm: "SYMMETRIC_DEFAULT", + EncryptionAlgorithm: 'SYMMETRIC_DEFAULT', KeyId: request.KeyId, }; } else { - let [KeyId, Plaintext] = Buffer.from(request.CiphertextBlob, "base64") + let [KeyId, Plaintext] = Buffer.from(request.CiphertextBlob, 'base64') .toString() - .split("\0"); + .split('\0'); // Do not return invalid base64 https://jira.mongodb.org/browse/MONGOCRYPT-525 - if (Buffer.from(KeyId, "base64").toString("base64") !== KeyId) { - KeyId = "invalid0"; + if (Buffer.from(KeyId, 'base64').toString('base64') !== KeyId) { + KeyId = 'invalid0'; } - if (Buffer.from(Plaintext, "base64").toString("base64") !== Plaintext) { - Plaintext = "invalid1"; + if (Buffer.from(Plaintext, 'base64').toString('base64') !== Plaintext) { + Plaintext = 'invalid1'; } return { Plaintext, - EncryptionAlgorithm: "SYMMETRIC_DEFAULT", + EncryptionAlgorithm: 'SYMMETRIC_DEFAULT', KeyId, }; } } function azureHandler({ body, url }: RequestData): any { - if (url.endsWith("/token")) { + if (url.endsWith('/token')) { return oauthToken; } else if (url.match(/\/(un)?wrapkey/)) { // Just act as if this was encrypted. @@ -108,12 +108,12 @@ function azureHandler({ body, url }: RequestData): any { } function gcpHandler({ body, url }: RequestData): any { - if (url.endsWith("/token")) { + if (url.endsWith('/token')) { return oauthToken; - } else if (url.endsWith(":encrypt")) { + } else if (url.endsWith(':encrypt')) { // Here we also just perform noop encryption. return { ciphertext: JSON.parse(body).plaintext }; - } else if (url.endsWith(":decrypt")) { + } else if (url.endsWith(':decrypt')) { return { plaintext: JSON.parse(body).ciphertext }; } } diff --git a/testing/integration-testing-hooks.ts b/testing/integration-testing-hooks.ts index 15e3781bb..2c85cc004 100644 --- a/testing/integration-testing-hooks.ts +++ b/testing/integration-testing-hooks.ts @@ -1,20 +1,20 @@ /* eslint-disable mocha/no-exports */ -import child_process from "child_process"; -import { promises as fs } from "fs"; -import { MongoClient, type MongoClientOptions } from "mongodb"; -import path from "path"; -import semver from "semver"; -import { promisify } from "util"; -import which from "which"; -import { ConnectionString } from "mongodb-connection-string-url"; -import { MongoCluster, type MongoClusterOptions } from "mongodb-runner"; -import { downloadCryptLibrary } from "@mongosh/build"; +import child_process from 'child_process'; +import { promises as fs } from 'fs'; +import { MongoClient, type MongoClientOptions } from 'mongodb'; +import path from 'path'; +import semver from 'semver'; +import { promisify } from 'util'; +import which from 'which'; +import { ConnectionString } from 'mongodb-connection-string-url'; +import { MongoCluster, type MongoClusterOptions } from 'mongodb-runner'; +import { downloadCryptLibrary } from '@mongosh/build'; const execFile = promisify(child_process.execFile); // Return the path to the temporary directory and ensure that it exists. async function getTmpdir(): Promise { - const tmpdir = path.resolve(__dirname, "..", "tmp"); + const tmpdir = path.resolve(__dirname, '..', 'tmp'); await fs.mkdir(tmpdir, { recursive: true }); return tmpdir; } @@ -26,7 +26,7 @@ export class MongodSetup { _setConnectionString: (connectionString: string) => void; _serverVersion: string | null = null; _isCommunityServer: boolean | null = null; - _bindir = ""; + _bindir = ''; constructor(connectionString?: string) { this._setConnectionString = () => { @@ -42,11 +42,11 @@ export class MongodSetup { } async start(): Promise { - await Promise.reject(new Error("Server not managed")); + await Promise.reject(new Error('Server not managed')); } async stop(): Promise { - await Promise.reject(new Error("Server not managed")); + await Promise.reject(new Error('Server not managed')); } async connectionString( @@ -73,7 +73,7 @@ export class MongodSetup { } async port(): Promise { - return (await this.hostport()).split(":").reverse()[0]; + return (await this.hostport()).split(':').reverse()[0]; } async hostport(): Promise { @@ -86,7 +86,7 @@ export class MongodSetup { } const { version } = await this.withClient(async (client) => { - return await client.db("db1").admin().serverStatus(); + return await client.db('db1').admin().serverStatus(); }); this._serverVersion = version; return version; @@ -98,9 +98,9 @@ export class MongodSetup { } const { modules } = await this.withClient(async (client) => { - return await client.db("db1").admin().command({ buildInfo: 1 }); + return await client.db('db1').admin().command({ buildInfo: 1 }); }); - const isCommunityServer = !modules.includes("enterprise"); + const isCommunityServer = !modules.includes('enterprise'); this._isCommunityServer = isCommunityServer; return isCommunityServer; } @@ -133,8 +133,8 @@ export class MongoRunnerSetup extends MongodSetup { ) { const prefix = [id, version, topology] .filter(Boolean) - .join("-") - .replace(/[^a-zA-Z0-9_.-]/g, ""); + .join('-') + .replace(/[^a-zA-Z0-9_.-]/g, ''); this._usedDirPrefix[prefix] ??= 0; @@ -165,10 +165,10 @@ export class MongoRunnerSetup extends MongodSetup { ); this._cluster = await MongoCluster.start({ - topology: "standalone", - tmpDir: path.join(tmpDir, "mongodb-runner", "dbs", dirPath), - logDir: path.join(tmpDir, "mongodb-runner", "logs", dirPath), - downloadDir: path.join(tmpDir, "mongodb-runner"), + topology: 'standalone', + tmpDir: path.join(tmpDir, 'mongodb-runner', 'dbs', dirPath), + logDir: path.join(tmpDir, 'mongodb-runner', 'logs', dirPath), + downloadDir: path.join(tmpDir, 'mongodb-runner'), version: version, ...this._opts, }); @@ -183,22 +183,22 @@ export class MongoRunnerSetup extends MongodSetup { } async function getInstalledMongodVersion(): Promise { - await Promise.all([which("mongod"), which("mongos")]); - const { stdout } = await execFile("mongod", ["--version"]); + await Promise.all([which('mongod'), which('mongos')]); + const { stdout } = await execFile('mongod', ['--version']); const { version } = stdout.match(/^db version (?.+)$/m)! .groups as any; return version; } export async function downloadCurrentCryptSharedLibrary(): Promise { - if (process.platform === "linux") { + if (process.platform === 'linux') { return ( await downloadCryptLibrary( - `linux-${process.arch.replace("ppc64", "ppc64le")}` as any + `linux-${process.arch.replace('ppc64', 'ppc64le')}` as any ) ).cryptLibrary; } - return (await downloadCryptLibrary("host")).cryptLibrary; + return (await downloadCryptLibrary('host')).cryptLibrary; } /** @@ -243,7 +243,7 @@ export function startSharedTestServer(): MongodSetup { return new MongodSetup(process.env.MONGOSH_TEST_SERVER_URL); } - const server = sharedSetup ?? (sharedSetup = new MongoRunnerSetup("shared")); + const server = sharedSetup ?? (sharedSetup = new MongoRunnerSetup('shared')); before(async function () { this.timeout(120_000); // Include potential mongod download time. @@ -363,14 +363,14 @@ export function skipIfEnvServerVersion(semverCondition: string): void { testServerVersion = await getInstalledMongodVersion(); } catch (e: any) { // no explicitly specified version but also no local mongod installation - testServerVersion = "9999.9999.9999"; + testServerVersion = '9999.9999.9999'; } } else { testServerVersion = testServerVersion - .split("-")[0] - .split(".") - .map((num) => (/[0-9]+/.test(num) ? num : "0")) - .join("."); + .split('-')[0] + .split('.') + .map((num) => (/[0-9]+/.test(num) ? num : '0')) + .join('.'); } skipIfVersion(this, testServerVersion, semverCondition); }); diff --git a/testing/package.json b/testing/package.json new file mode 100644 index 000000000..bf20e826b --- /dev/null +++ b/testing/package.json @@ -0,0 +1,6 @@ +{ + "scripts": { + "lint": "prettier --check .", + "reformat": "prettier --write ." + } +} diff --git a/testing/tsconfig.json b/testing/tsconfig.json index dfe2dd3a1..c76dbd55a 100644 --- a/testing/tsconfig.json +++ b/testing/tsconfig.json @@ -1,5 +1,9 @@ { "extends": "@mongodb-js/tsconfig-mongosh/tsconfig.common.json", + "compilerOptions": { + "allowJs": true, + "outDir": "./dist" + }, "include": ["**/*"], "exclude": ["certificates"] } From b2d8618f67f56de7668807ec8363d58250e746c2 Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 29 Oct 2025 11:20:06 +0100 Subject: [PATCH 8/9] chore: resolve json module --- packages/build/src/download-center/constants.ts | 2 +- packages/build/tsconfig.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/build/src/download-center/constants.ts b/packages/build/src/download-center/constants.ts index cbe8cd328..fc4c2f64d 100644 --- a/packages/build/src/download-center/constants.ts +++ b/packages/build/src/download-center/constants.ts @@ -1,4 +1,4 @@ -const fallback = require('./fallback.json'); +import fallback from './fallback.json'; /** * The S3 bucket for download center configurations. diff --git a/packages/build/tsconfig.json b/packages/build/tsconfig.json index 22cb4c367..dc5d8a279 100644 --- a/packages/build/tsconfig.json +++ b/packages/build/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "outDir": "./lib", "allowJs": true, - "removeComments": false + "removeComments": false, + "resolveJsonModule": true }, "include": ["src/**/*"], "exclude": ["./src/**/*.spec.*"] From 037f6c86c5d9abba170b9fef64ff26c331ca364d Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 29 Oct 2025 11:27:14 +0100 Subject: [PATCH 9/9] chore: fix connection string type --- testing/integration-testing-hooks.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/testing/integration-testing-hooks.ts b/testing/integration-testing-hooks.ts index 2c85cc004..7f229cdf1 100644 --- a/testing/integration-testing-hooks.ts +++ b/testing/integration-testing-hooks.ts @@ -6,8 +6,8 @@ import path from 'path'; import semver from 'semver'; import { promisify } from 'util'; import which from 'which'; -import { ConnectionString } from 'mongodb-connection-string-url'; import { MongoCluster, type MongoClusterOptions } from 'mongodb-runner'; +import { ConnectionString } from 'mongodb-connection-string-url'; import { downloadCryptLibrary } from '@mongosh/build'; const execFile = promisify(child_process.execFile); @@ -51,7 +51,7 @@ export class MongodSetup { async connectionString( searchParams: Partial> = {}, - uriOptions: Partial = {} + uriOptions: Partial = {} ): Promise { if ( Object.keys(searchParams).length + Object.keys(uriOptions).length === @@ -190,15 +190,18 @@ async function getInstalledMongodVersion(): Promise { return version; } -export async function downloadCurrentCryptSharedLibrary(): Promise { +export async function downloadCurrentCryptSharedLibrary( + versionSpec?: string +): Promise { if (process.platform === 'linux') { return ( await downloadCryptLibrary( - `linux-${process.arch.replace('ppc64', 'ppc64le')}` as any + `linux-${process.arch.replace('ppc64', 'ppc64le')}` as any, + versionSpec ) ).cryptLibrary; } - return (await downloadCryptLibrary('host')).cryptLibrary; + return (await downloadCryptLibrary('host', versionSpec)).cryptLibrary; } /**