diff --git a/packages/mongodb-memory-server-core/src/util/DryMongoBinary.ts b/packages/mongodb-memory-server-core/src/util/DryMongoBinary.ts index 1f9a56a08..8f5126017 100644 --- a/packages/mongodb-memory-server-core/src/util/DryMongoBinary.ts +++ b/packages/mongodb-memory-server-core/src/util/DryMongoBinary.ts @@ -5,7 +5,7 @@ import { isNullOrUndefined, pathExists } from './utils'; import * as path from 'path'; import { arch, homedir, platform } from 'os'; import findCacheDir from 'find-cache-dir'; -import getOS, { AnyOS } from './getos'; +import getOS, { AnyOS, isLinuxOS } from './getos'; const log = debug('MongoMS:DryMongoBinary'); @@ -18,7 +18,13 @@ export interface BaseDryMongoBinaryOptions { } export interface DryMongoBinaryOptions extends BaseDryMongoBinaryOptions { - version: string; + version: NonNullable; +} + +export interface DryMongoBinaryNameOptions { + version: NonNullable; + arch: NonNullable; + os: NonNullable; } export interface DryMongoBinaryPaths { @@ -47,16 +53,16 @@ export class DryMongoBinary { */ static async locateBinary(opts: DryMongoBinaryOptions): Promise { log(`locateBinary: Trying to locate Binary for version "${opts.version}"`); - const systemBinary = opts.systemBinary ?? (await this.generateOptions()).systemBinary; + const useOpts = await this.generateOptions(opts); - if (!isNullOrUndefined(systemBinary) && systemBinary.length > 0) { - log(`locateBinary: env "SYSTEM_BINARY" was provided with value: "${systemBinary}"`); + if (!isNullOrUndefined(useOpts.systemBinary) && useOpts.systemBinary.length > 0) { + log(`locateBinary: env "SYSTEM_BINARY" was provided with value: "${useOpts.systemBinary}"`); - const systemReturn = await this.getSystemPath(systemBinary); + const systemReturn = await this.getSystemPath(useOpts.systemBinary); if (isNullOrUndefined(systemReturn)) { throw new Error( - `Config option "SYSTEM_BINARY" was provided with value "${systemBinary}", but no binary could be found!` + `Config option "SYSTEM_BINARY" was provided with value "${useOpts.systemBinary}", but no binary could be found!` ); } @@ -71,7 +77,7 @@ export class DryMongoBinary { } log('locateBinary: running generateDownloadPath'); - const returnValue = await this.generateDownloadPath(opts); + const returnValue = await this.generateDownloadPath(useOpts); if (!returnValue[0]) { log('locateBinary: could not find an existing binary'); @@ -108,8 +114,7 @@ export class DryMongoBinary { systemBinary: resolveConfig(ResolveConfigVariables.SYSTEM_BINARY) || '', }; - // fix the path, because what gets returned is the full path to the binary (base/version/binary) - final.downloadDir = path.resolve((await this.generateDownloadPath(final))[1], '..', '..'); + final.downloadDir = path.dirname((await this.generateDownloadPath(final))[1]); return final; } @@ -118,10 +123,11 @@ export class DryMongoBinary { * Get the full path with filename * @return Absoulte Path with FileName */ - static getBinaryName(): string { + static getBinaryName(opts: DryMongoBinaryNameOptions): string { const addExe = platform() === 'win32' ? '.exe' : ''; + const dist = isLinuxOS(opts.os) ? opts.os.dist : opts.os.os; - return `mongod${addExe}`; + return `mongod-${opts.arch}-${dist}-${opts.version}${addExe}`; } /** @@ -133,7 +139,7 @@ export class DryMongoBinary { basePath: string, binaryName: string ): string { - return path.resolve(basePath, opts.version, binaryName); + return path.resolve(basePath, binaryName); } /** @@ -159,14 +165,16 @@ export class DryMongoBinary { * Generate an "MongoBinaryPaths" object * @return an finished "MongoBinaryPaths" object */ - static async generatePaths(opts: DryMongoBinaryOptions): Promise { + static async generatePaths( + opts: DryMongoBinaryOptions & DryMongoBinaryNameOptions + ): Promise { const final: DryMongoBinaryPaths = { legacyHomeCache: '', modulesCache: '', relative: '', resolveConfig: '', }; - const binaryName = this.getBinaryName(); + const binaryName = this.getBinaryName(opts); // Assign "node_modules/.cache" to modulesCache // if we're in postinstall script, npm will set the cwd too deep @@ -194,7 +202,7 @@ export class DryMongoBinary { // Resolve the config value "DOWNLOAD_DIR" if provided, otherwise remove from list const resolveConfigValue = - resolveConfig(ResolveConfigVariables.DOWNLOAD_DIR) || opts.downloadDir; + opts.downloadDir || resolveConfig(ResolveConfigVariables.DOWNLOAD_DIR); if (!isNullOrUndefined(resolveConfigValue) && resolveConfigValue.length > 0) { log(`generatePaths: resolveConfigValue is not empty`); @@ -215,7 +223,9 @@ export class DryMongoBinary { * Generate the Path where an Binary will be located * @return "boolean" indicating if the binary exists at the provided path, and "string" the path to use for the binary */ - static async generateDownloadPath(opts: DryMongoBinaryOptions): Promise<[boolean, string]> { + static async generateDownloadPath( + opts: DryMongoBinaryOptions & DryMongoBinaryNameOptions + ): Promise<[boolean, string]> { const preferGlobal = envToBool(resolveConfig(ResolveConfigVariables.PREFER_GLOBAL_PATH)); log(`generateDownloadPath: Generating Download Path, preferGlobal: "${preferGlobal}"`); const paths = await this.generatePaths(opts); diff --git a/packages/mongodb-memory-server-core/src/util/MongoBinaryDownload.ts b/packages/mongodb-memory-server-core/src/util/MongoBinaryDownload.ts index 85f8cd9bf..ca16a69a7 100644 --- a/packages/mongodb-memory-server-core/src/util/MongoBinaryDownload.ts +++ b/packages/mongodb-memory-server-core/src/util/MongoBinaryDownload.ts @@ -62,11 +62,13 @@ export class MongoBinaryDownload { * Get the full path with filename * @return Absoulte Path with FileName */ - protected getPath(): string { + protected async getPath(): Promise { + const opts = await DryMongoBinary.generateOptions({ version: this.version }); + return DryMongoBinary.combineBinaryName( { version: this.version }, this.downloadDir, - DryMongoBinary.getBinaryName() + DryMongoBinary.getBinaryName(opts) ); } @@ -76,7 +78,7 @@ export class MongoBinaryDownload { */ async getMongodPath(): Promise { log('getMongodPath'); - const mongodPath = this.getPath(); + const mongodPath = await this.getPath(); if (await pathExists(mongodPath)) { return mongodPath; @@ -224,7 +226,7 @@ export class MongoBinaryDownload { */ async extract(mongoDBArchive: string): Promise { log('extract'); - const mongodbFullPath = this.getPath(); + const mongodbFullPath = await this.getPath(); const mongodbDirPath = path.dirname(mongodbFullPath); log(`extract: archive: "${mongoDBArchive}" final: "${mongodbFullPath}"`); diff --git a/packages/mongodb-memory-server-core/src/util/__tests__/dryBinary.test.ts b/packages/mongodb-memory-server-core/src/util/__tests__/dryBinary.test.ts index 1e2aff3e1..73e806604 100644 --- a/packages/mongodb-memory-server-core/src/util/__tests__/dryBinary.test.ts +++ b/packages/mongodb-memory-server-core/src/util/__tests__/dryBinary.test.ts @@ -18,7 +18,7 @@ describe('DryBinary', () => { binary: 'mongod', }; expect(binary.DryMongoBinary.combineBinaryName(input.opts, input.base, input.binary)).toEqual( - path.resolve(input.base, input.opts.version, input.binary) + path.resolve(input.base, 'mongod') ); }); @@ -51,11 +51,12 @@ describe('DryBinary', () => { let tmpDir: tmp.DirResult; /** Used for non "find-cache-dir" */ let tmpDir2: tmp.DirResult; - let cwdBefore: string; + const cwdBefore = process.cwd(); const version = '4.0.20'; + let opts: binary.DryMongoBinaryOptions & binary.DryMongoBinaryNameOptions; + let binaryName: string; beforeAll(async () => { delete process.env[envName(ResolveConfigVariables.DOWNLOAD_DIR)]; // i dont know where this comes from, but without it, this property exists - cwdBefore = process.cwd(); tmpDir = tmp.dirSync({ prefix: 'mongo-mem-drybinGP-', unsafeCleanup: true }); tmpDir2 = tmp.dirSync({ prefix: 'mongo-mem-drybinGP-', unsafeCleanup: true }); jest @@ -80,57 +81,47 @@ describe('DryBinary', () => { process.chdir(cwdBefore); }); - beforeEach(() => { + beforeEach(async () => { // execute this before each test, to always have the correct cwd process.chdir(path.resolve(tmpDir.name)); + opts = await binary.DryMongoBinary.generateOptions({ version }); + delete opts.downloadDir; // delete, because these tests are about "generatePaths", which will prioritise "opts.downloadDir" over env + binaryName = binary.DryMongoBinary.getBinaryName(opts); }); afterEach(() => { delete process.env[envName(ResolveConfigVariables.DOWNLOAD_DIR)]; }); it('should have 3 complete paths while not being in postinstall or having a config', async () => { - const returnValue = await binary.DryMongoBinary.generatePaths({ version }); + const returnValue = await binary.DryMongoBinary.generatePaths(opts); expect(returnValue).toStrictEqual({ resolveConfig: '', // empty because not having an extra config value - relative: path.resolve(tmpDir.name, 'mongodb-binaries', version, 'mongod'), - legacyHomeCache: path.resolve( - tmpDir.name, - 'homedir/.cache/mongodb-binaries', - version, - 'mongod' - ), + relative: path.resolve(tmpDir.name, 'mongodb-binaries', binaryName), + legacyHomeCache: path.resolve(tmpDir.name, 'homedir/.cache/mongodb-binaries', binaryName), modulesCache: path.resolve( tmpDir.name, 'node_modules/.cache/mongodb-memory-server', - version, - 'mongod' + binaryName ), } as binary.DryMongoBinaryPaths); }); it('should have 3 complete paths while being in postinstall and not having a config', async () => { process.chdir(path.resolve(tmpDir.name, 'node_modules/mongodb-memory-server')); - const returnValue = await binary.DryMongoBinary.generatePaths({ version }); + const returnValue = await binary.DryMongoBinary.generatePaths(opts); expect(returnValue).toStrictEqual({ resolveConfig: '', // empty because not having an extra config value relative: path.resolve( tmpDir.name, 'node_modules/mongodb-memory-server', 'mongodb-binaries', - version, - 'mongod' - ), - legacyHomeCache: path.resolve( - tmpDir.name, - 'homedir/.cache/mongodb-binaries', - version, - 'mongod' + binaryName ), + legacyHomeCache: path.resolve(tmpDir.name, 'homedir/.cache/mongodb-binaries', binaryName), modulesCache: path.resolve( tmpDir.name, 'node_modules/.cache/mongodb-memory-server', - version, - 'mongod' + binaryName ), } as binary.DryMongoBinaryPaths); }); @@ -138,21 +129,15 @@ describe('DryBinary', () => { it('should have 4 complete paths while not being in postinstall but having a config', async () => { const customPath = '/some/custom/path'; process.env[envName(ResolveConfigVariables.DOWNLOAD_DIR)] = customPath; - const returnValue = await binary.DryMongoBinary.generatePaths({ version }); + const returnValue = await binary.DryMongoBinary.generatePaths(opts); expect(returnValue).toStrictEqual({ - resolveConfig: path.resolve('/some/custom/path', version, 'mongod'), - relative: path.resolve(tmpDir.name, 'mongodb-binaries', version, 'mongod'), - legacyHomeCache: path.resolve( - tmpDir.name, - 'homedir/.cache/mongodb-binaries', - version, - 'mongod' - ), + resolveConfig: path.resolve('/some/custom/path', binaryName), + relative: path.resolve(tmpDir.name, 'mongodb-binaries', binaryName), + legacyHomeCache: path.resolve(tmpDir.name, 'homedir/.cache/mongodb-binaries', binaryName), modulesCache: path.resolve( tmpDir.name, 'node_modules/.cache/mongodb-memory-server', - version, - 'mongod' + binaryName ), } as binary.DryMongoBinaryPaths); }); @@ -160,16 +145,11 @@ describe('DryBinary', () => { it('should have 2 complete paths while not being in postinstall and not having a config and not being in an project', async () => { const customPath = path.resolve(tmpDir2.name); process.chdir(customPath); - const returnValue = await binary.DryMongoBinary.generatePaths({ version }); + const returnValue = await binary.DryMongoBinary.generatePaths(opts); expect(returnValue).toStrictEqual({ resolveConfig: '', // empty because not having an extra config value - relative: path.resolve(tmpDir2.name, 'mongodb-binaries', version, 'mongod'), - legacyHomeCache: path.resolve( - tmpDir.name, - 'homedir/.cache/mongodb-binaries', - version, - 'mongod' - ), + relative: path.resolve(tmpDir2.name, 'mongodb-binaries', binaryName), + legacyHomeCache: path.resolve(tmpDir.name, 'homedir/.cache/mongodb-binaries', binaryName), modulesCache: '', // because not being in an project } as binary.DryMongoBinaryPaths); }); @@ -177,9 +157,10 @@ describe('DryBinary', () => { describe('generateDownloadPath', () => { const filesExist: Set = new Set(); - const version = '4.0.20'; let expectedPaths: binary.DryMongoBinaryPaths; - beforeAll(() => { + let opts: Required; + beforeAll(async () => { + opts = await binary.DryMongoBinary.generateOptions({ version: '4.0.20' }); delete process.env[envName(ResolveConfigVariables.PREFER_GLOBAL_PATH)]; jest.spyOn(utils, 'pathExists').mockImplementation((file) => { // this is to ensure it is returning an promise @@ -214,7 +195,7 @@ describe('DryBinary', () => { resolveConfig: expectedPath, }; filesExist.add(expectedPath); - const returnValue = await binary.DryMongoBinary.generateDownloadPath({ version }); + const returnValue = await binary.DryMongoBinary.generateDownloadPath(opts); expect(binary.DryMongoBinary.generatePaths).toHaveBeenCalledTimes(1); expect(returnValue).toStrictEqual([true, expectedPath]); }); @@ -228,7 +209,7 @@ describe('DryBinary', () => { resolveConfig: '/no', }; filesExist.add(expectedPath); - const returnValue = await binary.DryMongoBinary.generateDownloadPath({ version }); + const returnValue = await binary.DryMongoBinary.generateDownloadPath(opts); expect(binary.DryMongoBinary.generatePaths).toHaveBeenCalledTimes(1); expect(returnValue).toStrictEqual([true, expectedPath]); }); @@ -242,7 +223,7 @@ describe('DryBinary', () => { resolveConfig: '/no', }; filesExist.add(expectedPath); - const returnValue = await binary.DryMongoBinary.generateDownloadPath({ version }); + const returnValue = await binary.DryMongoBinary.generateDownloadPath(opts); expect(binary.DryMongoBinary.generatePaths).toHaveBeenCalledTimes(1); expect(returnValue).toStrictEqual([true, expectedPath]); }); @@ -256,7 +237,7 @@ describe('DryBinary', () => { resolveConfig: '/no', }; filesExist.add(expectedPath); - const returnValue = await binary.DryMongoBinary.generateDownloadPath({ version }); + const returnValue = await binary.DryMongoBinary.generateDownloadPath(opts); expect(binary.DryMongoBinary.generatePaths).toHaveBeenCalledTimes(1); expect(returnValue).toStrictEqual([true, expectedPath]); }); @@ -271,7 +252,7 @@ describe('DryBinary', () => { relative: '', resolveConfig: expectedPath, }; - const returnValue = await binary.DryMongoBinary.generateDownloadPath({ version }); + const returnValue = await binary.DryMongoBinary.generateDownloadPath(opts); expect(binary.DryMongoBinary.generatePaths).toHaveBeenCalledTimes(1); expect(returnValue).toStrictEqual([false, expectedPath]); }); @@ -285,7 +266,7 @@ describe('DryBinary', () => { relative: '', resolveConfig: '', }; - const returnValue = await binary.DryMongoBinary.generateDownloadPath({ version }); + const returnValue = await binary.DryMongoBinary.generateDownloadPath(opts); expect(binary.DryMongoBinary.generatePaths).toHaveBeenCalledTimes(1); expect(returnValue).toStrictEqual([false, expectedPath]); }); @@ -299,7 +280,7 @@ describe('DryBinary', () => { relative: '', resolveConfig: '', }; - const returnValue = await binary.DryMongoBinary.generateDownloadPath({ version }); + const returnValue = await binary.DryMongoBinary.generateDownloadPath(opts); expect(binary.DryMongoBinary.generatePaths).toHaveBeenCalledTimes(1); expect(returnValue).toStrictEqual([false, expectedPath]); }); @@ -313,7 +294,7 @@ describe('DryBinary', () => { relative: expectedPath, resolveConfig: '', }; - const returnValue = await binary.DryMongoBinary.generateDownloadPath({ version }); + const returnValue = await binary.DryMongoBinary.generateDownloadPath(opts); expect(binary.DryMongoBinary.generatePaths).toHaveBeenCalledTimes(1); expect(returnValue).toStrictEqual([false, expectedPath]); });