From 10d17497b8882d6d68400410d6bd7fd0ae8deb97 Mon Sep 17 00:00:00 2001 From: hasezoey Date: Wed, 18 Jan 2023 00:32:09 +0100 Subject: [PATCH] fix(MongoBinaryDownload): use coerced versions for semver fixes #734 --- .../src/util/MongoBinaryDownloadUrl.ts | 45 +++-- .../__tests__/MongoBinaryDownloadUrl.test.ts | 162 ++++++++++++++++++ .../MongoBinaryDownloadUrl.test.ts.snap | 6 + 3 files changed, 199 insertions(+), 14 deletions(-) diff --git a/packages/mongodb-memory-server-core/src/util/MongoBinaryDownloadUrl.ts b/packages/mongodb-memory-server-core/src/util/MongoBinaryDownloadUrl.ts index 1ea7e7da1..bc9e545b7 100644 --- a/packages/mongodb-memory-server-core/src/util/MongoBinaryDownloadUrl.ts +++ b/packages/mongodb-memory-server-core/src/util/MongoBinaryDownloadUrl.ts @@ -8,6 +8,7 @@ import { KnownVersionIncompatibilityError, UnknownArchitectureError, UnknownPlatformError, + UnknownVersionError, } from './errors'; import { deprecate } from 'util'; @@ -101,11 +102,12 @@ export class MongoBinaryDownloadUrl implements MongoBinaryDownloadUrlOpts { */ getArchiveNameWin(): string { let name = `mongodb-${this.platform}-${this.arch}`; + const coercedVersion = semver.coerce(this.version); - if (!isNullOrUndefined(semver.coerce(this.version))) { - if (semver.satisfies(this.version, '4.2.x')) { + if (!isNullOrUndefined(coercedVersion)) { + if (semver.satisfies(coercedVersion, '4.2.x')) { name += '-2012plus'; - } else if (semver.lt(this.version, '4.1.0')) { + } else if (semver.lt(coercedVersion, '4.1.0')) { name += '-2008plus-ssl'; } } @@ -121,19 +123,19 @@ export class MongoBinaryDownloadUrl implements MongoBinaryDownloadUrlOpts { */ getArchiveNameOsx(): string { let name = `mongodb-osx`; - const version = semver.coerce(this.version); + const coercedVersion = semver.coerce(this.version); - if (!isNullOrUndefined(version) && semver.gte(version, '3.2.0')) { + if (!isNullOrUndefined(coercedVersion) && semver.gte(coercedVersion, '3.2.0')) { name += '-ssl'; } - if (isNullOrUndefined(version) || semver.gte(version, '4.2.0')) { + if (isNullOrUndefined(coercedVersion) || semver.gte(coercedVersion, '4.2.0')) { name = `mongodb-macos`; // somehow these files are not listed in https://www.mongodb.org/dl/osx } // mongodb has native arm64 if (this.arch === 'aarch64') { // force usage of "x86_64" binary for all versions below than 6.0.0 - if (!isNullOrUndefined(version) && semver.lt(version, '6.0.0')) { + if (!isNullOrUndefined(coercedVersion) && semver.lt(coercedVersion, '6.0.0')) { log('getArchiveNameOsx: Arch is "aarch64" and version is below 6.0.0, using x64 binary'); this.arch = 'x86_64'; } else { @@ -243,11 +245,16 @@ export class MongoBinaryDownloadUrl implements MongoBinaryDownloadUrlOpts { getDebianVersionString(os: LinuxOS): string { let name = 'debian'; const release: number = parseFloat(os.release); + const coercedVersion = semver.coerce(this.version); + + if (isNullOrUndefined(coercedVersion)) { + throw new UnknownVersionError(this.version); + } if (release >= 11 || ['unstable', 'testing'].includes(os.release)) { // Debian 11 is compatible with the binaries for debian 10 // but does not have binaries for before 5.0.8 - if (semver.lt(this.version, '5.0.8')) { + if (semver.lt(coercedVersion, '5.0.8')) { log('debian11 detected, but version below 5.0.8 requested, using debian10'); name += '10'; } else { @@ -264,7 +271,7 @@ export class MongoBinaryDownloadUrl implements MongoBinaryDownloadUrlOpts { } if (release >= 10) { - if (semver.lt(this.version, '4.2.1')) { + if (semver.lt(coercedVersion, '4.2.1')) { throw new KnownVersionIncompatibilityError( `Debian ${release}`, this.version, @@ -311,6 +318,11 @@ export class MongoBinaryDownloadUrl implements MongoBinaryDownloadUrlOpts { let name = 'rhel'; const { release } = os; const releaseAsSemver = semver.coerce(release); // coerce "8" to "8.0.0" and "8.2" to "8.2.0", makes comparing easier than "parseInt" or "parseFloat" + const coercedVersion = semver.coerce(this.version); + + if (isNullOrUndefined(coercedVersion)) { + throw new UnknownVersionError(this.version); + } if (releaseAsSemver) { if (this.arch === 'aarch64') { @@ -325,7 +337,7 @@ export class MongoBinaryDownloadUrl implements MongoBinaryDownloadUrlOpts { } // there are no versions for aarch64 before mongodb 4.4.2 // Note: version 4.4.2 and 4.4.3 are NOT listed at the list, but are existing; list: https://www.mongodb.com/download-center/community/releases/archive - if (semver.lt(this.version, '4.4.2')) { + if (semver.lt(coercedVersion, '4.4.2')) { throw new KnownVersionIncompatibilityError(`Rhel ${release}`, this.version, '>=4.4.2'); } @@ -399,6 +411,11 @@ export class MongoBinaryDownloadUrl implements MongoBinaryDownloadUrlOpts { */ getUbuntuVersionString(os: LinuxOS): string { let ubuntuOS: LinuxOS | undefined = undefined; + const coercedVersion = semver.coerce(this.version); + + if (isNullOrUndefined(coercedVersion)) { + throw new UnknownVersionError(this.version); + } // "id_like" processing (version conversion) [this is an block to be collapsible] { @@ -460,13 +477,13 @@ export class MongoBinaryDownloadUrl implements MongoBinaryDownloadUrlOpts { if (this.arch === 'aarch64') { // this is because, before version 4.1.10, everything for "arm64" / "aarch64" were just "arm64" and for "ubuntu1604" - if (semver.satisfies(this.version, '<4.1.10')) { + if (semver.satisfies(coercedVersion, '<4.1.10')) { this.arch = 'arm64'; return 'ubuntu1604'; } // this is because versions below "4.4.0" did not provide an binary for anything above 1804 - if (semver.satisfies(this.version, '>=4.1.10 <4.4.0')) { + if (semver.satisfies(coercedVersion, '>=4.1.10 <4.4.0')) { return 'ubuntu1804'; } } @@ -477,7 +494,7 @@ export class MongoBinaryDownloadUrl implements MongoBinaryDownloadUrlOpts { // there are no MongoDB 3.x binary distributions for ubuntu >= 18 // https://www.mongodb.org/dl/linux/x86_64-ubuntu1604 - if (ubuntuYear >= 18 && semver.satisfies(this.version, '3.x.x')) { + if (ubuntuYear >= 18 && semver.satisfies(coercedVersion, '3.x.x')) { log( `getUbuntuVersionString: ubuntuYear is "${ubuntuYear}", which dosnt have an 3.x.x version, defaulting to "1604"` ); @@ -487,7 +504,7 @@ export class MongoBinaryDownloadUrl implements MongoBinaryDownloadUrlOpts { // there are no MongoDB <=4.3.x binary distributions for ubuntu > 18 // https://www.mongodb.org/dl/linux/x86_64-ubuntu1804 - if (ubuntuYear > 18 && semver.satisfies(this.version, '<=4.3.x')) { + if (ubuntuYear > 18 && semver.satisfies(coercedVersion, '<=4.3.x')) { log( `getUbuntuVersionString: ubuntuYear is "${ubuntuYear}", which dosnt have an "<=4.3.x" version, defaulting to "1804"` ); diff --git a/packages/mongodb-memory-server-core/src/util/__tests__/MongoBinaryDownloadUrl.test.ts b/packages/mongodb-memory-server-core/src/util/__tests__/MongoBinaryDownloadUrl.test.ts index e156de50d..93da2d7a0 100644 --- a/packages/mongodb-memory-server-core/src/util/__tests__/MongoBinaryDownloadUrl.test.ts +++ b/packages/mongodb-memory-server-core/src/util/__tests__/MongoBinaryDownloadUrl.test.ts @@ -3,16 +3,42 @@ import { UnknownPlatformError, UnknownArchitectureError, KnownVersionIncompatibilityError, + UnknownVersionError, } from '../errors'; import { LinuxOS } from '../getos'; import MongoBinaryDownloadUrl from '../MongoBinaryDownloadUrl'; import { envName, ResolveConfigVariables } from '../resolveConfig'; +import * as semver from 'semver'; +import { assertion } from '../utils'; afterEach(() => { jest.restoreAllMocks(); }); describe('MongoBinaryDownloadUrl', () => { + // the following is to make sure that semver works in expected ways and as examples of how we use it + describe('semver correctly coerces mongodb versions', () => { + it('should convert a normal semver version', () => { + const normal5 = semver.coerce('5.0.0'); + assertion(normal5); + expect(normal5.version).toStrictEqual('5.0.0'); + + const normalAll = semver.coerce('4.4.2'); + assertion(normalAll); + expect(normalAll.version).toStrictEqual('4.4.2'); + }); + + it('should convert "-latest" version correctly', () => { + const latest5 = semver.coerce('v5.0-latest'); + assertion(latest5); + expect(latest5.version).toStrictEqual('5.0.0'); + + const latest44 = semver.coerce('v4.4-latest'); + assertion(latest44); + expect(latest44.version).toStrictEqual('4.4.0'); + }); + }); + describe('getDownloadUrl()', () => { describe('for mac', () => { it('latest', async () => { @@ -92,6 +118,28 @@ describe('MongoBinaryDownloadUrl', () => { 'https://fastdl.mongodb.org/osx/mongodb-macos-arm64-6.0.0.tgz' ); }); + + it('should allow v5.0-latest x64', async () => { + const du = new MongoBinaryDownloadUrl({ + platform: 'darwin', + arch: 'x64', + version: 'v5.0-latest', + }); + expect(await du.getDownloadUrl()).toBe( + 'https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-v5.0-latest.tgz' + ); + }); + + it('should allow v5.0-latest arm64', async () => { + const du = new MongoBinaryDownloadUrl({ + platform: 'darwin', + arch: 'arm64', + version: 'v5.0-latest', + }); + expect(await du.getDownloadUrl()).toBe( + 'https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-v5.0-latest.tgz' + ); + }); }); describe('for linux', () => { @@ -224,6 +272,44 @@ describe('MongoBinaryDownloadUrl', () => { ); }); + it('should allow v5.0-latest', async () => { + const du = new MongoBinaryDownloadUrl({ + platform: 'linux', + arch: 'x64', + version: 'v5.0-latest', + os: { + os: 'linux', + dist: 'ubuntu', + release: '20.04', + }, + }); + expect(await du.getDownloadUrl()).toBe( + 'https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu2004-v5.0-latest.tgz' + ); + }); + + it('should throw a Error when the provided version could not be coerced [UnknownVersionError]', async () => { + const du = new MongoBinaryDownloadUrl({ + platform: 'linux', + arch: 'x64', + version: 'vvv', + os: { + os: 'linux', + dist: 'ubuntu', + release: '20.04', + }, + }); + + try { + await du.getDownloadUrl(); + fail('Expected to throw a UnknownVersionError'); + } catch (err) { + assertIsError(err); + expect(err).toBeInstanceOf(UnknownVersionError); + expect(err.message).toMatchSnapshot(); + } + }); + describe('arm64', () => { it('for ubuntu arm64 4.0.25 (below 4.1.10)', async () => { const du = new MongoBinaryDownloadUrl({ @@ -420,6 +506,44 @@ describe('MongoBinaryDownloadUrl', () => { ); }); + it('should allow v5.0-latest', async () => { + const du = new MongoBinaryDownloadUrl({ + platform: 'linux', + arch: 'x64', + version: 'v5.0-latest', + os: { + os: 'linux', + dist: 'debian', + release: '10', + }, + }); + expect(await du.getDownloadUrl()).toBe( + 'https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian10-v5.0-latest.tgz' + ); + }); + + it('should throw a Error when the provided version could not be coerced [UnknownVersionError]', async () => { + const du = new MongoBinaryDownloadUrl({ + platform: 'linux', + arch: 'x64', + version: 'vvv', + os: { + os: 'linux', + dist: 'debian', + release: '10', + }, + }); + + try { + await du.getDownloadUrl(); + fail('Expected to throw a UnknownVersionError'); + } catch (err) { + assertIsError(err); + expect(err).toBeInstanceOf(UnknownVersionError); + expect(err.message).toMatchSnapshot(); + } + }); + it('should throw a Error when requesting a version below 4.2.1 for debian 10+ [#554] [KnownVersionIncompatibilityError]', async () => { const du = new MongoBinaryDownloadUrl({ platform: 'linux', @@ -903,6 +1027,22 @@ describe('MongoBinaryDownloadUrl', () => { ); }); + it('should allow v5.0-latest', async () => { + const du = new MongoBinaryDownloadUrl({ + platform: 'linux', + arch: 'x64', + version: 'v5.0-latest', + os: { + os: 'linux', + dist: 'rhel', + release: '9', + }, + }); + expect(await du.getDownloadUrl()).toBe( + 'https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-v5.0-latest.tgz' + ); + }); + it('should Error when ARM64 and rhel below 8 [KnownVersionIncompatibilityError]', async () => { const du = new MongoBinaryDownloadUrl({ platform: 'linux', @@ -988,6 +1128,28 @@ describe('MongoBinaryDownloadUrl', () => { expect(consoleWarnSpy).toHaveBeenCalledTimes(1); expect(consoleWarnSpy).toHaveBeenCalledWith('Couldnt coerce RHEL version "a"'); }); + + it('should throw a Error when the provided version could not be coerced [UnknownVersionError]', async () => { + const du = new MongoBinaryDownloadUrl({ + platform: 'linux', + arch: 'x64', + version: 'vvv', + os: { + os: 'linux', + dist: 'rhel', + release: '9', + }, + }); + + try { + await du.getDownloadUrl(); + fail('Expected to throw a UnknownVersionError'); + } catch (err) { + assertIsError(err); + expect(err).toBeInstanceOf(UnknownVersionError); + expect(err.message).toMatchSnapshot(); + } + }); }); }); diff --git a/packages/mongodb-memory-server-core/src/util/__tests__/__snapshots__/MongoBinaryDownloadUrl.test.ts.snap b/packages/mongodb-memory-server-core/src/util/__tests__/__snapshots__/MongoBinaryDownloadUrl.test.ts.snap index 5bc955f62..9b1758660 100644 --- a/packages/mongodb-memory-server-core/src/util/__tests__/__snapshots__/MongoBinaryDownloadUrl.test.ts.snap +++ b/packages/mongodb-memory-server-core/src/util/__tests__/__snapshots__/MongoBinaryDownloadUrl.test.ts.snap @@ -10,6 +10,8 @@ exports[`MongoBinaryDownloadUrl getDownloadUrl() for linux for debian should thr Mongodb does not provide binaries for versions before 4.2.1 for Debian 10+ and also cannot be mapped to a previous Debian release" `; +exports[`MongoBinaryDownloadUrl getDownloadUrl() for linux for debian should throw a Error when the provided version could not be coerced [UnknownVersionError] 1`] = `"Could not corece VERSION to a semver version (version: \\"vvv\\")"`; + exports[`MongoBinaryDownloadUrl getDownloadUrl() for linux for rhel should Error when ARM64 and rhel below 8 [KnownVersionIncompatibilityError] 1`] = ` "Requested Version \\"4.4.2\\" is not available for \\"Rhel 7\\"! Available Versions: \\">=4.4.2\\" ARM64(aarch64) support for rhel is only for rhel82 or higher" @@ -20,6 +22,10 @@ exports[`MongoBinaryDownloadUrl getDownloadUrl() for linux for rhel should Error ARM64(aarch64) support for rhel is only for rhel82 or higher" `; +exports[`MongoBinaryDownloadUrl getDownloadUrl() for linux for rhel should throw a Error when the provided version could not be coerced [UnknownVersionError] 1`] = `"Could not corece VERSION to a semver version (version: \\"vvv\\")"`; + +exports[`MongoBinaryDownloadUrl getDownloadUrl() for linux for ubuntu should throw a Error when the provided version could not be coerced [UnknownVersionError] 1`] = `"Could not corece VERSION to a semver version (version: \\"vvv\\")"`; + exports[`MongoBinaryDownloadUrl getDownloadUrl() should throw an error if platform is unknown (getArchiveName) 1`] = `"Unknown Platform: \\"unknown\\""`; exports[`MongoBinaryDownloadUrl getDownloadUrl() should throw an error if platform is unknown (translatePlatform) 1`] = `"Unknown Platform: \\"unknown\\""`;