From 28bbb85a8ea056c617bcfc4f5b7afe6264d5794c Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Thu, 30 Jan 2025 20:53:35 +0000 Subject: [PATCH 1/8] test updating from the latest release to the newly packaged one --- .../tests/auto-update.test.ts | 113 +++++++------ .../compass-smoke-tests/src/build-info.ts | 16 +- packages/compass-smoke-tests/src/cli.ts | 156 +++++------------- packages/compass-smoke-tests/src/context.ts | 3 +- packages/compass-smoke-tests/src/releases.ts | 91 ++++++++++ .../src/tests/auto-update-from.ts | 57 +++++++ .../src/tests/auto-update-to.ts | 77 +++++++++ .../src/tests/time-to-first-query.ts | 30 ++++ .../compass-smoke-tests/src/tests/types.ts | 7 + .../src/tests/update-server.ts | 19 +++ 10 files changed, 397 insertions(+), 172 deletions(-) create mode 100644 packages/compass-smoke-tests/src/releases.ts create mode 100644 packages/compass-smoke-tests/src/tests/auto-update-from.ts create mode 100644 packages/compass-smoke-tests/src/tests/auto-update-to.ts create mode 100644 packages/compass-smoke-tests/src/tests/time-to-first-query.ts create mode 100644 packages/compass-smoke-tests/src/tests/types.ts create mode 100644 packages/compass-smoke-tests/src/tests/update-server.ts diff --git a/packages/compass-e2e-tests/tests/auto-update.test.ts b/packages/compass-e2e-tests/tests/auto-update.test.ts index aa399e5ed9e..869b3b01bee 100644 --- a/packages/compass-e2e-tests/tests/auto-update.test.ts +++ b/packages/compass-e2e-tests/tests/auto-update.test.ts @@ -13,66 +13,71 @@ function wait(ms: number) { } describe('Auto-update', function () { - it('auto-update from', async function () { - if (process.env.TEST_NAME !== 'auto-update-from') { - // we don't want this test to execute along with all the others under - // normal circumstances because it is destructive - it overwrites Compass - // itself - this.skip(); - } - - // run the app and wait for it to auto-update - console.log('starting compass the first time'); - const compass = await init('auto-update from', { firstRun: true }); - const { browser } = compass; - try { - await browser.$(Selectors.AutoUpdateToast).waitForDisplayed(); - - if (process.env.AUTO_UPDATE_UPDATABLE === 'true') { - const restartButton = browser.$(Selectors.AutoUpdateRestartButton); - await restartButton.waitForDisplayed(); - - // We could click the restart button to apply the update and restart the - // app, but restarting the app confuses webdriverio or at least our test - // helpers. So we're going to just restart the app manually. - await browser.pause(1000); - } else { - // When auto-update is not supported the toast contains a link to - // download - const linkElement = browser.$(Selectors.AutoUpdateDownloadLink); - await linkElement.waitForDisplayed(); - expect(await linkElement.getAttribute('href')).to.equal( - 'https://www.mongodb.com/try/download/compass?utm_source=compass&utm_medium=product' - ); + for (const testName of ['auto-update-from', 'auto-update-to']) { + it(testName, async function () { + if (process.env.TEST_NAME !== testName) { + // we don't want this test to execute along with all the others under + // normal circumstances because it is destructive - it overwrites Compass + // itself + this.skip(); } - } finally { - await browser.screenshot(screenshotPathName('auto-update-from')); - await cleanup(compass); - } - - if (process.env.AUTO_UPDATE_UPDATABLE === 'true') { - console.log( - 'pause to make sure the app properly exited before starting again' - ); - await wait(10_000); - console.log('starting compass a second time'); - // run the app again and check that the version changed - const compass = await init('auto-update from restart', { - firstRun: false, - }); + // run the app and wait for it to auto-update + console.log('starting compass the first time'); + const compass = await init(testName, { firstRun: true }); const { browser } = compass; try { await browser.$(Selectors.AutoUpdateToast).waitForDisplayed(); - await browser - .$(Selectors.AutoUpdateReleaseNotesLink) - .waitForDisplayed(); + + if (process.env.AUTO_UPDATE_UPDATABLE === 'true') { + const restartButton = browser.$(Selectors.AutoUpdateRestartButton); + await restartButton.waitForDisplayed(); + + // We could click the restart button to apply the update and restart the + // app, but restarting the app confuses webdriverio or at least our test + // helpers. So we're going to just restart the app manually. + await browser.pause(1000); + } else { + // When auto-update is not supported the toast contains a link to + // download + const linkElement = browser.$(Selectors.AutoUpdateDownloadLink); + await linkElement.waitForDisplayed(); + expect(await linkElement.getAttribute('href')).to.equal( + 'https://www.mongodb.com/try/download/compass?utm_source=compass&utm_medium=product' + ); + + // TODO: when updating to a known version we know the version, so + // check for the text + } } finally { - await browser.screenshot( - screenshotPathName('auto-update-from-restart') - ); + await browser.screenshot(screenshotPathName(testName)); await cleanup(compass); } - } - }); + + if (process.env.AUTO_UPDATE_UPDATABLE === 'true') { + console.log( + 'pause to make sure the app properly exited before starting again' + ); + await wait(10_000); + + console.log('starting compass a second time'); + // run the app again and check that the version changed + const compass = await init(`${testName} restart`, { + firstRun: false, + }); + const { browser } = compass; + try { + await browser.$(Selectors.AutoUpdateToast).waitForDisplayed(); + await browser + .$(Selectors.AutoUpdateReleaseNotesLink) + .waitForDisplayed(); + // TODO: when updating to a known version we know the version, so + // check for the text + } finally { + await browser.screenshot(screenshotPathName(`${testName}-restart`)); + await cleanup(compass); + } + } + }); + } }); diff --git a/packages/compass-smoke-tests/src/build-info.ts b/packages/compass-smoke-tests/src/build-info.ts index 61d4f9b9791..9e82becfda5 100644 --- a/packages/compass-smoke-tests/src/build-info.ts +++ b/packages/compass-smoke-tests/src/build-info.ts @@ -8,6 +8,10 @@ import { type PackageKind } from './packages'; import { type SmokeTestsContext } from './context'; import { pick } from 'lodash'; +const SUPPORTED_CHANNELS = ['dev', 'beta', 'stable'] as const; + +export type Channel = typeof SUPPORTED_CHANNELS[number]; + function assertObjectHasKeys( obj: unknown, name: string, @@ -25,13 +29,21 @@ function assertObjectHasKeys( // subsets of the hadron-build info result -export const commonKeys = ['productName'] as const; -export type CommonBuildInfo = Record; +export const commonKeys = ['productName', 'version', 'channel'] as const; +export type CommonBuildInfo = Record & { + channel: Channel; +}; export function assertCommonBuildInfo( buildInfo: unknown ): asserts buildInfo is CommonBuildInfo { assertObjectHasKeys(buildInfo, 'buildInfo', commonKeys); + assert( + SUPPORTED_CHANNELS.includes((buildInfo as { channel: Channel }).channel), + `Expected ${ + (buildInfo as { channel: Channel }).channel + } to be in ${SUPPORTED_CHANNELS.join(',')}` + ); } export const windowsFilenameKeys = [ diff --git a/packages/compass-smoke-tests/src/cli.ts b/packages/compass-smoke-tests/src/cli.ts index 1f3facf907b..8db8bcc31cf 100755 --- a/packages/compass-smoke-tests/src/cli.ts +++ b/packages/compass-smoke-tests/src/cli.ts @@ -2,12 +2,10 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; -import { once } from 'node:events'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { pick } from 'lodash'; -import { execute, executeAsync } from './execute'; import { type PackageDetails, readPackageDetails, @@ -16,6 +14,8 @@ import { import { createSandbox } from './directories'; import { downloadFile } from './downloads'; import { type PackageKind, SUPPORTED_PACKAGES } from './packages'; +import { getLatestRelease } from './releases'; +import { SUPPORTED_TESTS } from './tests/types'; import { type SmokeTestsContext } from './context'; import { installMacDMG } from './installers/mac-dmg'; @@ -23,11 +23,13 @@ import { installMacZIP } from './installers/mac-zip'; import { installWindowsZIP } from './installers/windows-zip'; import { installWindowsMSI } from './installers/windows-msi'; +import { testTimeToFirstQuery } from './tests/time-to-first-query'; +import { testAutoUpdateFrom } from './tests/auto-update-from'; +import { testAutoUpdateTo } from './tests/auto-update-to'; + const SUPPORTED_PLATFORMS = ['win32', 'darwin', 'linux'] as const; const SUPPORTED_ARCHS = ['x64', 'arm64'] as const; -const SUPPORTED_TESTS = ['time-to-first-query', 'auto-update-from'] as const; - function isSupportedPlatform( value: unknown ): value is typeof SUPPORTED_PLATFORMS[number] { @@ -198,9 +200,13 @@ async function run() { ]) ); - const { kind, appName, filepath, autoUpdatable } = await getTestSubject( - context - ); + const { + kind, + appName, + filepath, + buildInfo: { channel, version }, + autoUpdatable, + } = await getTestSubject(context); const install = getInstaller(kind); try { @@ -209,9 +215,19 @@ async function run() { } for (const testName of context.tests) { + const installerPath = + testName === 'auto-update-to' + ? await getLatestRelease( + channel, + context.arch, + kind, + context.forceDownload + ) + : filepath; + const { appPath, uninstall } = install({ appName, - filepath, + filepath: installerPath, destinationPath: context.sandboxPath, }); @@ -219,17 +235,31 @@ async function run() { if (testName === 'time-to-first-query') { // Auto-update does not work on mac in CI at the moment. So in that case // we just run the E2E tests to make sure the app at least starts up. - runTimeToFirstQuery({ + testTimeToFirstQuery({ appName, appPath, }); } if (testName === 'auto-update-from') { - await runUpdateTest({ + await testAutoUpdateFrom({ + appName, + appPath, + autoUpdatable, + }); + } + if (testName === 'auto-update-to') { + assert( + context.bucketKeyPrefix !== undefined, + 'Bucket key prefix is needed to download' + ); + + await testAutoUpdateTo({ appName, appPath, autoUpdatable, - testName, + channel, + bucketKeyPrefix: context.bucketKeyPrefix, + version, }); } } finally { @@ -246,110 +276,6 @@ async function run() { } } -async function importUpdateServer() { - try { - return (await import('compass-mongodb-com')).default; - } catch (err: unknown) { - console.log('Remember to npm link compass-mongodb-com'); - throw err; - } -} - -async function startAutoUpdateServer() { - console.log('Starting auto-update server'); - const { httpServer, updateChecker, start } = (await importUpdateServer())(); - start(); - await once(updateChecker, 'refreshed'); - - return httpServer; -} - -type RunE2ETestOptions = { - appName: string; - appPath: string; -}; - -function runTimeToFirstQuery({ appName, appPath }: RunE2ETestOptions) { - execute( - 'npm', - [ - 'run', - '--unsafe-perm', - 'test-packaged', - '--workspace', - 'compass-e2e-tests', - '--', - '--test-filter=time-to-first-query', - ], - { - // We need to use a shell to get environment variables setup correctly - shell: true, - env: { - ...process.env, - COMPASS_APP_NAME: appName, - COMPASS_APP_PATH: appPath, - }, - } - ); -} - -type RunUpdateTestOptions = { - appName: string; - appPath: string; - autoUpdatable?: boolean; - testName: string; -}; - -async function runUpdateTest({ - appName, - appPath, - autoUpdatable, - testName, -}: RunUpdateTestOptions) { - process.env.PORT = '0'; // dynamic port - process.env.UPDATE_CHECKER_ALLOW_DOWNGRADES = 'true'; - - const server = await startAutoUpdateServer(); - - const address = server.address(); - assert(typeof address === 'object' && address !== null); - const port = address.port; - const HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE = `http://localhost:${port}`; - console.log({ HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE }); - - try { - // must be async because the update server is running in the same process - await executeAsync( - 'npm', - [ - 'run', - '--unsafe-perm', - 'test-packaged', - '--workspace', - 'compass-e2e-tests', - '--', - '--test-filter=auto-update', - ], - { - // We need to use a shell to get environment variables setup correctly - shell: true, - env: { - ...process.env, - HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE, - AUTO_UPDATE_UPDATABLE: (!!autoUpdatable).toString(), - TEST_NAME: testName, - COMPASS_APP_NAME: appName, - COMPASS_APP_PATH: appPath, - }, - } - ); - } finally { - console.log('Stopping auto-update server'); - server.close(); - delete process.env.UPDATE_CHECKER_ALLOW_DOWNGRADES; - } -} - run() .then(function () { console.log('done'); diff --git a/packages/compass-smoke-tests/src/context.ts b/packages/compass-smoke-tests/src/context.ts index 268ff41b03c..5ebc1473100 100644 --- a/packages/compass-smoke-tests/src/context.ts +++ b/packages/compass-smoke-tests/src/context.ts @@ -1,4 +1,5 @@ import { type PackageKind } from './packages'; +import { type TestName } from './tests/types'; export type SmokeTestsContext = { bucketName?: string; @@ -9,6 +10,6 @@ export type SmokeTestsContext = { forceDownload?: boolean; localPackage?: boolean; sandboxPath: string; - tests: string[]; + tests: TestName[]; skipCleanup: boolean; }; diff --git a/packages/compass-smoke-tests/src/releases.ts b/packages/compass-smoke-tests/src/releases.ts new file mode 100644 index 00000000000..e2ce93c0054 --- /dev/null +++ b/packages/compass-smoke-tests/src/releases.ts @@ -0,0 +1,91 @@ +import { downloadFile } from './downloads'; +import { type PackageKind } from './packages'; + +type Arch = 'arm64' | 'x64'; + +type PlatformShortName = + | 'darwin-arm64' + | 'darwin-x64' + | 'windows' + | 'linux_deb' + | 'linux_rpm'; + +function getPlatformShortName( + arch: Arch, + kind: PackageKind +): PlatformShortName { + if (arch === 'arm64') { + if (kind === 'osx_dmg' || kind === 'osx_zip') { + return 'darwin-arm64'; + } + } + if (arch === 'x64') { + if (kind === 'osx_dmg' || kind === 'osx_zip') { + return 'darwin-x64'; + } + if ( + kind === 'windows_setup' || + kind === 'windows_msi' || + kind === 'windows_zip' + ) { + return 'windows'; + } + if (kind === 'linux_deb' || kind === 'linux_tar') { + return 'linux_deb'; + } + if (kind === 'linux_rpm' || kind === 'rhel_tar') { + return 'linux_rpm'; + } + } + + throw new Error(`Unsupported arch/kind combo: ${arch}/${kind}`); +} + +type PlatformExtension = 'dmg' | 'exe' | 'deb' | 'rpm'; + +function getPlatformExtension( + arch: Arch, + kind: PackageKind +): PlatformExtension { + if (arch === 'arm64') { + if (kind === 'osx_dmg' || kind === 'osx_zip') { + return 'dmg'; + } + } + if (arch === 'x64') { + if (kind === 'osx_dmg' || kind === 'osx_zip') { + return 'dmg'; + } + if ( + kind === 'windows_setup' || + kind === 'windows_msi' || + kind === 'windows_zip' + ) { + return 'exe'; + } + if (kind === 'linux_deb' || kind === 'linux_tar') { + return 'deb'; + } + if (kind === 'linux_rpm' || kind === 'rhel_tar') { + return 'rpm'; + } + } + + throw new Error(`Unsupported arch/kind combo: ${arch}/${kind}`); +} + +export async function getLatestRelease( + channel: 'dev' | 'beta' | 'stable', + arch: Arch, + kind: PackageKind, + forceDownload?: boolean +): Promise { + const shortName = getPlatformShortName(arch, kind); + const extension = getPlatformExtension(arch, kind); + + return await downloadFile({ + url: `http://compass.mongodb.com/api/v2/download/latest/compass/${channel}/${shortName}`, + targetFilename: `latest-${channel}.${extension}`, + clearCache: forceDownload, + }); +} diff --git a/packages/compass-smoke-tests/src/tests/auto-update-from.ts b/packages/compass-smoke-tests/src/tests/auto-update-from.ts new file mode 100644 index 00000000000..eaf483022a4 --- /dev/null +++ b/packages/compass-smoke-tests/src/tests/auto-update-from.ts @@ -0,0 +1,57 @@ +import assert from 'node:assert/strict'; +import { executeAsync } from '../execute'; +import { startAutoUpdateServer } from './update-server'; + +type RunUpdateFromTestOptions = { + appName: string; + appPath: string; + autoUpdatable?: boolean; +}; + +export async function testAutoUpdateFrom({ + appName, + appPath, + autoUpdatable, +}: RunUpdateFromTestOptions) { + process.env.PORT = '0'; // dynamic port + process.env.UPDATE_CHECKER_ALLOW_DOWNGRADES = 'true'; + + const server = await startAutoUpdateServer(); + + const address = server.address(); + assert(typeof address === 'object' && address !== null); + const port = address.port; + const HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE = `http://localhost:${port}`; + + try { + // must be async because the update server is running in the same process + await executeAsync( + 'npm', + [ + 'run', + '--unsafe-perm', + 'test-packaged', + '--workspace', + 'compass-e2e-tests', + '--', + '--test-filter=auto-update', + ], + { + // We need to use a shell to get environment variables setup correctly + shell: true, + env: { + ...process.env, + HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE, + AUTO_UPDATE_UPDATABLE: (!!autoUpdatable).toString(), + TEST_NAME: 'auto-update-from', + COMPASS_APP_NAME: appName, + COMPASS_APP_PATH: appPath, + }, + } + ); + } finally { + console.log('Stopping auto-update server'); + server.close(); + delete process.env.UPDATE_CHECKER_ALLOW_DOWNGRADES; + } +} diff --git a/packages/compass-smoke-tests/src/tests/auto-update-to.ts b/packages/compass-smoke-tests/src/tests/auto-update-to.ts new file mode 100644 index 00000000000..3ed21b61785 --- /dev/null +++ b/packages/compass-smoke-tests/src/tests/auto-update-to.ts @@ -0,0 +1,77 @@ +import assert from 'node:assert/strict'; +import { executeAsync } from '../execute'; +import { startAutoUpdateServer } from './update-server'; +import { type Channel } from '../build-info'; + +type RunUpdateToTestOptions = { + appName: string; + appPath: string; + autoUpdatable?: boolean; + bucketKeyPrefix: string; + version: string; + channel: Channel; +}; + +export async function testAutoUpdateTo({ + appName, + appPath, + autoUpdatable, + channel, + version, + bucketKeyPrefix, +}: RunUpdateToTestOptions) { + process.env.PORT = '0'; // dynamic port + + if (channel === 'dev') { + process.env.DEV_RELEASE = JSON.stringify({ + version: version, + bucket_key_prefix: bucketKeyPrefix, + }); + } else { + process.env.PUBLISHED_RELEASES = JSON.stringify({ + name: version, + body: version, + bucket_key_prefix: bucketKeyPrefix, + }); + } + + const server = await startAutoUpdateServer(); + + const address = server.address(); + assert(typeof address === 'object' && address !== null); + const port = address.port; + const HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE = `http://localhost:${port}`; + + try { + // must be async because the update server is running in the same process + await executeAsync( + 'npm', + [ + 'run', + '--unsafe-perm', + 'test-packaged', + '--workspace', + 'compass-e2e-tests', + '--', + '--test-filter=auto-update', + ], + { + // We need to use a shell to get environment variables setup correctly + shell: true, + env: { + ...process.env, + HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE, + AUTO_UPDATE_UPDATABLE: (!!autoUpdatable).toString(), + TEST_NAME: 'auto-update-to', + COMPASS_APP_NAME: appName, + COMPASS_APP_PATH: appPath, + }, + } + ); + } finally { + console.log('Stopping auto-update server'); + server.close(); + delete process.env.DEV_RELEASE; + delete process.env.PUBLISHED_RELEASES; + } +} diff --git a/packages/compass-smoke-tests/src/tests/time-to-first-query.ts b/packages/compass-smoke-tests/src/tests/time-to-first-query.ts new file mode 100644 index 00000000000..e19628df049 --- /dev/null +++ b/packages/compass-smoke-tests/src/tests/time-to-first-query.ts @@ -0,0 +1,30 @@ +import { execute } from '../execute'; + +type RunE2ETestOptions = { + appName: string; + appPath: string; +}; + +export function testTimeToFirstQuery({ appName, appPath }: RunE2ETestOptions) { + execute( + 'npm', + [ + 'run', + '--unsafe-perm', + 'test-packaged', + '--workspace', + 'compass-e2e-tests', + '--', + '--test-filter=time-to-first-query', + ], + { + // We need to use a shell to get environment variables setup correctly + shell: true, + env: { + ...process.env, + COMPASS_APP_NAME: appName, + COMPASS_APP_PATH: appPath, + }, + } + ); +} diff --git a/packages/compass-smoke-tests/src/tests/types.ts b/packages/compass-smoke-tests/src/tests/types.ts new file mode 100644 index 00000000000..df5695dc8f9 --- /dev/null +++ b/packages/compass-smoke-tests/src/tests/types.ts @@ -0,0 +1,7 @@ +export const SUPPORTED_TESTS = [ + 'time-to-first-query', + 'auto-update-from', + 'auto-update-to', +] as const; + +export type TestName = typeof SUPPORTED_TESTS[number]; diff --git a/packages/compass-smoke-tests/src/tests/update-server.ts b/packages/compass-smoke-tests/src/tests/update-server.ts new file mode 100644 index 00000000000..fd0c149213e --- /dev/null +++ b/packages/compass-smoke-tests/src/tests/update-server.ts @@ -0,0 +1,19 @@ +import { once } from 'node:events'; + +async function importUpdateServer() { + try { + return (await import('compass-mongodb-com')).default; + } catch (err: unknown) { + console.log('Remember to npm link compass-mongodb-com'); + throw err; + } +} + +export async function startAutoUpdateServer() { + console.log('Starting auto-update server'); + const { httpServer, updateChecker, start } = (await importUpdateServer())(); + start(); + await once(updateChecker, 'refreshed'); + + return httpServer; +} From 4074347e1d039841914f5d36ff7216d2d3b3afee Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Thu, 30 Jan 2025 20:59:03 +0000 Subject: [PATCH 2/8] also run this in CI --- .evergreen/functions.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.evergreen/functions.yml b/.evergreen/functions.yml index 5b075ade7e6..2aa822837b8 100644 --- a/.evergreen/functions.yml +++ b/.evergreen/functions.yml @@ -678,8 +678,8 @@ functions: if [[ "$IS_WINDOWS" == "true" ]]; then # TODO: windows_setup - npm run --unsafe-perm --workspace @mongodb-js/compass-smoke-tests start -- --package=windows_zip --tests=auto-update-from - npm run --unsafe-perm --workspace @mongodb-js/compass-smoke-tests start -- --package=windows_msi --tests=auto-update-from + npm run --unsafe-perm --workspace @mongodb-js/compass-smoke-tests start -- --package=windows_zip --tests auto-update-from auto-update-to + npm run --unsafe-perm --workspace @mongodb-js/compass-smoke-tests start -- --package=windows_msi --tests auto-update-from auto-update-to fi if [[ "$IS_OSX" == "true" ]]; then From 3c7162dd1130945fde737e3183b3f0d13ffe36ce Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 31 Jan 2025 09:38:46 +0000 Subject: [PATCH 3/8] better type assertions --- .../compass-smoke-tests/src/build-info.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/compass-smoke-tests/src/build-info.ts b/packages/compass-smoke-tests/src/build-info.ts index 9e82becfda5..ef6a6a1db90 100644 --- a/packages/compass-smoke-tests/src/build-info.ts +++ b/packages/compass-smoke-tests/src/build-info.ts @@ -12,14 +12,13 @@ const SUPPORTED_CHANNELS = ['dev', 'beta', 'stable'] as const; export type Channel = typeof SUPPORTED_CHANNELS[number]; -function assertObjectHasKeys( - obj: unknown, - name: string, - keys: readonly string[] -) { +function assertObjectHasKeys< + Keys extends readonly string[], + Obj extends Record +>(obj: unknown, name: string, keys: Keys): asserts obj is Obj { assert( typeof obj === 'object' && obj !== null, - 'Expected buildInfo to be an object' + `Expected ${name} to be an object` ); for (const key of keys) { @@ -39,10 +38,10 @@ export function assertCommonBuildInfo( ): asserts buildInfo is CommonBuildInfo { assertObjectHasKeys(buildInfo, 'buildInfo', commonKeys); assert( - SUPPORTED_CHANNELS.includes((buildInfo as { channel: Channel }).channel), - `Expected ${ - (buildInfo as { channel: Channel }).channel - } to be in ${SUPPORTED_CHANNELS.join(',')}` + SUPPORTED_CHANNELS.includes(buildInfo.channel as Channel), + `Expected ${JSON.stringify( + buildInfo.channel + )} to be in ${SUPPORTED_CHANNELS.join(',')}` ); } From 1ec24651db11cc1d1dacbc0311813dee2de43852 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 31 Jan 2025 12:21:19 +0000 Subject: [PATCH 4/8] auto-updating TO doesn't support windows yet because we don't have .exe installer support --- .evergreen/functions.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.evergreen/functions.yml b/.evergreen/functions.yml index 2aa822837b8..0fd64ce3bcd 100644 --- a/.evergreen/functions.yml +++ b/.evergreen/functions.yml @@ -678,8 +678,8 @@ functions: if [[ "$IS_WINDOWS" == "true" ]]; then # TODO: windows_setup - npm run --unsafe-perm --workspace @mongodb-js/compass-smoke-tests start -- --package=windows_zip --tests auto-update-from auto-update-to - npm run --unsafe-perm --workspace @mongodb-js/compass-smoke-tests start -- --package=windows_msi --tests auto-update-from auto-update-to + npm run --unsafe-perm --workspace @mongodb-js/compass-smoke-tests start -- --package=windows_zip --tests auto-update-from + npm run --unsafe-perm --workspace @mongodb-js/compass-smoke-tests start -- --package=windows_msi --tests auto-update-from fi if [[ "$IS_OSX" == "true" ]]; then From a897d930da90266ff9b2e5ca3b55d0cf4663af89 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 31 Jan 2025 14:00:18 +0000 Subject: [PATCH 5/8] the installer to use for the latest release is not necessarily the same as the package --- packages/compass-smoke-tests/src/cli.ts | 7 ++++-- packages/compass-smoke-tests/src/releases.ts | 24 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/compass-smoke-tests/src/cli.ts b/packages/compass-smoke-tests/src/cli.ts index 8db8bcc31cf..5687e6c2689 100755 --- a/packages/compass-smoke-tests/src/cli.ts +++ b/packages/compass-smoke-tests/src/cli.ts @@ -14,7 +14,7 @@ import { import { createSandbox } from './directories'; import { downloadFile } from './downloads'; import { type PackageKind, SUPPORTED_PACKAGES } from './packages'; -import { getLatestRelease } from './releases'; +import { getLatestRelease, getLatestReleaseKindByKind } from './releases'; import { SUPPORTED_TESTS } from './tests/types'; import { type SmokeTestsContext } from './context'; @@ -207,7 +207,6 @@ async function run() { buildInfo: { channel, version }, autoUpdatable, } = await getTestSubject(context); - const install = getInstaller(kind); try { if (context.tests.length === 0) { @@ -225,6 +224,10 @@ async function run() { ) : filepath; + const install = getInstaller( + testName === 'auto-update-to' ? getLatestReleaseKindByKind(kind) : kind + ); + const { appPath, uninstall } = install({ appName, filepath: installerPath, diff --git a/packages/compass-smoke-tests/src/releases.ts b/packages/compass-smoke-tests/src/releases.ts index e2ce93c0054..adfe762ba5b 100644 --- a/packages/compass-smoke-tests/src/releases.ts +++ b/packages/compass-smoke-tests/src/releases.ts @@ -89,3 +89,27 @@ export async function getLatestRelease( clearCache: forceDownload, }); } + +export function getLatestReleaseKindByKind(kind: PackageKind): PackageKind { + if (kind === 'osx_dmg' || kind === 'osx_zip') { + return 'osx_dmg'; + } + + if ( + kind === 'windows_setup' || + kind === 'windows_msi' || + kind === 'windows_zip' + ) { + return 'windows_setup'; + } + + if (kind === 'linux_deb' || kind === 'linux_tar') { + return 'linux_deb'; + } + + if (kind === 'linux_rpm' || kind === 'rhel_tar') { + return 'linux_rpm'; + } + + throw new Error(`Unsupported kind: ${kind as string}`); +} From 1c90eba580b12a11da4145d3cf779d5ec6e075e8 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Fri, 31 Jan 2025 16:56:48 +0000 Subject: [PATCH 6/8] check the text in the toasts --- .../tests/auto-update.test.ts | 37 +++++++++++++++---- .../src/tests/auto-update-to.ts | 1 + 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/compass-e2e-tests/tests/auto-update.test.ts b/packages/compass-e2e-tests/tests/auto-update.test.ts index 869b3b01bee..f2da620e2ec 100644 --- a/packages/compass-e2e-tests/tests/auto-update.test.ts +++ b/packages/compass-e2e-tests/tests/auto-update.test.ts @@ -33,6 +33,14 @@ describe('Auto-update', function () { const restartButton = browser.$(Selectors.AutoUpdateRestartButton); await restartButton.waitForDisplayed(); + if (process.env.EXPECTED_UPDATE_VERSION) { + expect( + await browser.$(Selectors.AutoUpdateToast).getText() + ).to.contain( + `Compass is ready to update to ${process.env.EXPECTED_UPDATE_VERSION}!` + ); + } + // We could click the restart button to apply the update and restart the // app, but restarting the app confuses webdriverio or at least our test // helpers. So we're going to just restart the app manually. @@ -46,8 +54,13 @@ describe('Auto-update', function () { 'https://www.mongodb.com/try/download/compass?utm_source=compass&utm_medium=product' ); - // TODO: when updating to a known version we know the version, so - // check for the text + if (process.env.EXPECTED_UPDATE_VERSION) { + expect( + await browser.$(Selectors.AutoUpdateToast).getText() + ).to.contain( + `Compass ${process.env.EXPECTED_UPDATE_VERSION} is available` + ); + } } } finally { await browser.screenshot(screenshotPathName(testName)); @@ -68,11 +81,21 @@ describe('Auto-update', function () { const { browser } = compass; try { await browser.$(Selectors.AutoUpdateToast).waitForDisplayed(); - await browser - .$(Selectors.AutoUpdateReleaseNotesLink) - .waitForDisplayed(); - // TODO: when updating to a known version we know the version, so - // check for the text + const releaseNotesLink = browser.$( + Selectors.AutoUpdateReleaseNotesLink + ); + await releaseNotesLink.waitForDisplayed(); + // for now we only know the new version in the auto-update-to case + if (process.env.EXPECTED_UPDATE_VERSION) { + expect( + await browser.$(Selectors.AutoUpdateToast).getText() + ).to.contain( + `Compass ${process.env.EXPECTED_UPDATE_VERSION} installed successfully` + ); + expect(await releaseNotesLink.getAttribute('href')).to.equal( + `https://github.com/mongodb-js/compass/releases/tag/v${process.env.EXPECTED_UPDATE_VERSION}` + ); + } } finally { await browser.screenshot(screenshotPathName(`${testName}-restart`)); await cleanup(compass); diff --git a/packages/compass-smoke-tests/src/tests/auto-update-to.ts b/packages/compass-smoke-tests/src/tests/auto-update-to.ts index 3ed21b61785..240adf17732 100644 --- a/packages/compass-smoke-tests/src/tests/auto-update-to.ts +++ b/packages/compass-smoke-tests/src/tests/auto-update-to.ts @@ -63,6 +63,7 @@ export async function testAutoUpdateTo({ HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE, AUTO_UPDATE_UPDATABLE: (!!autoUpdatable).toString(), TEST_NAME: 'auto-update-to', + EXPECTED_UPDATE_VERSION: version, COMPASS_APP_NAME: appName, COMPASS_APP_PATH: appPath, }, From 56d0879b0bba72f838239fe75f3b7fd35898775e Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Thu, 6 Feb 2025 12:42:13 +0000 Subject: [PATCH 7/8] refactor --- .../compass-smoke-tests/src/build-info.ts | 4 +- packages/compass-smoke-tests/src/cli.ts | 170 ++---------------- packages/compass-smoke-tests/src/context.ts | 5 +- .../src/installers/index.ts | 19 ++ .../compass-smoke-tests/src/test-subject.ts | 81 +++++++++ .../src/tests/auto-update-from.ts | 112 +++++++----- .../src/tests/auto-update-to.ts | 157 +++++++++------- .../src/tests/time-to-first-query.ts | 77 +++++--- 8 files changed, 329 insertions(+), 296 deletions(-) create mode 100644 packages/compass-smoke-tests/src/installers/index.ts create mode 100644 packages/compass-smoke-tests/src/test-subject.ts diff --git a/packages/compass-smoke-tests/src/build-info.ts b/packages/compass-smoke-tests/src/build-info.ts index ef6a6a1db90..860bf0a8f41 100644 --- a/packages/compass-smoke-tests/src/build-info.ts +++ b/packages/compass-smoke-tests/src/build-info.ts @@ -5,7 +5,7 @@ import path from 'node:path'; import { handler as writeBuildInfo } from 'hadron-build/commands/info'; import { type PackageKind } from './packages'; -import { type SmokeTestsContext } from './context'; +import { type SmokeTestsContextWithSandbox } from './context'; import { pick } from 'lodash'; const SUPPORTED_CHANNELS = ['dev', 'beta', 'stable'] as const; @@ -232,7 +232,7 @@ export function readPackageDetails( } export function writeAndReadPackageDetails( - context: SmokeTestsContext + context: SmokeTestsContextWithSandbox ): PackageDetails { const compassDir = path.resolve(__dirname, '../../compass'); const infoArgs = { diff --git a/packages/compass-smoke-tests/src/cli.ts b/packages/compass-smoke-tests/src/cli.ts index 5687e6c2689..05d9b83e1c2 100755 --- a/packages/compass-smoke-tests/src/cli.ts +++ b/packages/compass-smoke-tests/src/cli.ts @@ -1,28 +1,10 @@ #!/usr/bin/env npx ts-node -import assert from 'node:assert/strict'; -import fs from 'node:fs'; -import path from 'node:path'; - import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { pick } from 'lodash'; -import { - type PackageDetails, - readPackageDetails, - writeAndReadPackageDetails, -} from './build-info'; -import { createSandbox } from './directories'; -import { downloadFile } from './downloads'; -import { type PackageKind, SUPPORTED_PACKAGES } from './packages'; -import { getLatestRelease, getLatestReleaseKindByKind } from './releases'; import { SUPPORTED_TESTS } from './tests/types'; import { type SmokeTestsContext } from './context'; - -import { installMacDMG } from './installers/mac-dmg'; -import { installMacZIP } from './installers/mac-zip'; -import { installWindowsZIP } from './installers/windows-zip'; -import { installWindowsMSI } from './installers/windows-msi'; - +import { SUPPORTED_PACKAGES } from './packages'; import { testTimeToFirstQuery } from './tests/time-to-first-query'; import { testAutoUpdateFrom } from './tests/auto-update-from'; import { testAutoUpdateTo } from './tests/auto-update-to'; @@ -118,74 +100,10 @@ const argv = yargs(hideBin(process.argv)) }) .default('tests', SUPPORTED_TESTS.slice()); -type TestSubject = PackageDetails & { - filepath: string; - /** - * Is the package unsigned? - * In which case we'll expect auto-updating to fail. - */ - unsigned?: boolean; -}; - -/** - * Either finds the local package or downloads the package - */ -async function getTestSubject( - context: SmokeTestsContext -): Promise { - if (context.localPackage) { - const compassDistPath = path.resolve( - __dirname, - '../../packages/compass/dist' - ); - const buildInfoPath = path.resolve(compassDistPath, 'target.json'); - assert( - fs.existsSync(buildInfoPath), - `Expected '${buildInfoPath}' to exist` - ); - const details = readPackageDetails(context.package, buildInfoPath); - return { - ...details, - filepath: path.resolve(compassDistPath, details.filename), - unsigned: true, - }; - } else { - assert( - context.bucketName !== undefined && context.bucketKeyPrefix !== undefined, - 'Bucket name and key prefix are needed to download' - ); - const details = writeAndReadPackageDetails(context); - const filepath = await downloadFile({ - url: `https://${context.bucketName}.s3.amazonaws.com/${context.bucketKeyPrefix}/${details.filename}`, - targetFilename: details.filename, - clearCache: context.forceDownload, - }); - - return { ...details, filepath }; - } -} - -function getInstaller(kind: PackageKind) { - if (kind === 'osx_dmg') { - return installMacDMG; - } else if (kind === 'osx_zip') { - return installMacZIP; - } else if (kind === 'windows_zip') { - return installWindowsZIP; - } else if (kind === 'windows_msi') { - return installWindowsMSI; - } else { - throw new Error(`Installer for '${kind}' is not yet implemented`); - } -} - async function run() { - const context: SmokeTestsContext = { - ...argv.parseSync(), - sandboxPath: createSandbox(), - }; + const context: SmokeTestsContext = argv.parseSync(); - console.log(`Running tests in ${context.sandboxPath}`); + console.log(`Running tests`); console.log( 'context', @@ -200,81 +118,15 @@ async function run() { ]) ); - const { - kind, - appName, - filepath, - buildInfo: { channel, version }, - autoUpdatable, - } = await getTestSubject(context); - - try { - if (context.tests.length === 0) { - console.log('Warning: not performing any tests. Pass --tests.'); - } - - for (const testName of context.tests) { - const installerPath = - testName === 'auto-update-to' - ? await getLatestRelease( - channel, - context.arch, - kind, - context.forceDownload - ) - : filepath; + for (const testName of context.tests) { + console.log(testName); - const install = getInstaller( - testName === 'auto-update-to' ? getLatestReleaseKindByKind(kind) : kind - ); - - const { appPath, uninstall } = install({ - appName, - filepath: installerPath, - destinationPath: context.sandboxPath, - }); - - try { - if (testName === 'time-to-first-query') { - // Auto-update does not work on mac in CI at the moment. So in that case - // we just run the E2E tests to make sure the app at least starts up. - testTimeToFirstQuery({ - appName, - appPath, - }); - } - if (testName === 'auto-update-from') { - await testAutoUpdateFrom({ - appName, - appPath, - autoUpdatable, - }); - } - if (testName === 'auto-update-to') { - assert( - context.bucketKeyPrefix !== undefined, - 'Bucket key prefix is needed to download' - ); - - await testAutoUpdateTo({ - appName, - appPath, - autoUpdatable, - channel, - bucketKeyPrefix: context.bucketKeyPrefix, - version, - }); - } - } finally { - await uninstall(); - } - } - } finally { - if (context.skipCleanup) { - console.log(`Skipped cleaning up sandbox: ${context.sandboxPath}`); - } else { - console.log(`Cleaning up sandbox: ${context.sandboxPath}`); - fs.rmSync(context.sandboxPath, { recursive: true }); + if (testName === 'time-to-first-query') { + await testTimeToFirstQuery(context); + } else if (testName === 'auto-update-from') { + await testAutoUpdateFrom(context); + } else if (testName === 'auto-update-to') { + await testAutoUpdateTo(context); } } } diff --git a/packages/compass-smoke-tests/src/context.ts b/packages/compass-smoke-tests/src/context.ts index 5ebc1473100..0f46d634583 100644 --- a/packages/compass-smoke-tests/src/context.ts +++ b/packages/compass-smoke-tests/src/context.ts @@ -9,7 +9,10 @@ export type SmokeTestsContext = { package: PackageKind; forceDownload?: boolean; localPackage?: boolean; - sandboxPath: string; tests: TestName[]; skipCleanup: boolean; }; + +export type SmokeTestsContextWithSandbox = SmokeTestsContext & { + sandboxPath: string; +}; diff --git a/packages/compass-smoke-tests/src/installers/index.ts b/packages/compass-smoke-tests/src/installers/index.ts new file mode 100644 index 00000000000..a3ac52ff4d2 --- /dev/null +++ b/packages/compass-smoke-tests/src/installers/index.ts @@ -0,0 +1,19 @@ +import { type PackageKind } from '../packages'; +import { installMacDMG } from './mac-dmg'; +import { installMacZIP } from './mac-zip'; +import { installWindowsZIP } from './windows-zip'; +import { installWindowsMSI } from './windows-msi'; + +export function getInstaller(kind: PackageKind) { + if (kind === 'osx_dmg') { + return installMacDMG; + } else if (kind === 'osx_zip') { + return installMacZIP; + } else if (kind === 'windows_zip') { + return installWindowsZIP; + } else if (kind === 'windows_msi') { + return installWindowsMSI; + } else { + throw new Error(`Installer for '${kind}' is not yet implemented`); + } +} diff --git a/packages/compass-smoke-tests/src/test-subject.ts b/packages/compass-smoke-tests/src/test-subject.ts new file mode 100644 index 00000000000..97399ea91d2 --- /dev/null +++ b/packages/compass-smoke-tests/src/test-subject.ts @@ -0,0 +1,81 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import path from 'node:path'; + +import { type SmokeTestsContextWithSandbox } from './context'; + +import { + type PackageDetails, + readPackageDetails, + writeAndReadPackageDetails, +} from './build-info'; +import { downloadFile } from './downloads'; + +type TestSubjectDetails = PackageDetails & { + /** + * Is the package unsigned? + * In which case we'll expect auto-updating to fail. + */ + unsigned?: boolean; +}; + +type TestSubject = TestSubjectDetails & { + filepath: string; +}; +/** + * Either uses the local package details or calculates it + */ +export function getTestSubjectDetails( + context: SmokeTestsContextWithSandbox +): TestSubjectDetails { + if (context.localPackage) { + const compassDistPath = path.resolve( + __dirname, + '../../packages/compass/dist' + ); + const buildInfoPath = path.resolve(compassDistPath, 'target.json'); + assert( + fs.existsSync(buildInfoPath), + `Expected '${buildInfoPath}' to exist` + ); + const details = readPackageDetails(context.package, buildInfoPath); + return { + ...details, + unsigned: true, + }; + } else { + return writeAndReadPackageDetails(context); + } +} + +/** + * Either finds the local package or downloads the package + */ +export async function getTestSubject( + context: SmokeTestsContextWithSandbox +): Promise { + const subject = getTestSubjectDetails(context); + if (context.localPackage) { + const compassDistPath = path.resolve( + __dirname, + '../../packages/compass/dist' + ); + return { + ...subject, + filepath: path.resolve(compassDistPath, subject.filename), + }; + } else { + assert( + context.bucketName !== undefined && context.bucketKeyPrefix !== undefined, + 'Bucket name and key prefix are needed to download' + ); + + const filepath = await downloadFile({ + url: `https://${context.bucketName}.s3.amazonaws.com/${context.bucketKeyPrefix}/${subject.filename}`, + targetFilename: subject.filename, + clearCache: context.forceDownload, + }); + + return { ...subject, filepath }; + } +} diff --git a/packages/compass-smoke-tests/src/tests/auto-update-from.ts b/packages/compass-smoke-tests/src/tests/auto-update-from.ts index eaf483022a4..dd6f5df0c04 100644 --- a/packages/compass-smoke-tests/src/tests/auto-update-from.ts +++ b/packages/compass-smoke-tests/src/tests/auto-update-from.ts @@ -1,57 +1,79 @@ import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import { type SmokeTestsContext } from '../context'; +import { getInstaller } from '../installers'; +import { createSandbox } from '../directories'; +import { getTestSubject } from '../test-subject'; import { executeAsync } from '../execute'; import { startAutoUpdateServer } from './update-server'; -type RunUpdateFromTestOptions = { - appName: string; - appPath: string; - autoUpdatable?: boolean; -}; +export async function testAutoUpdateFrom(context: SmokeTestsContext) { + const sandboxPath = createSandbox(); + const { kind, appName, filepath, autoUpdatable } = await getTestSubject({ + ...context, + sandboxPath, + }); -export async function testAutoUpdateFrom({ - appName, - appPath, - autoUpdatable, -}: RunUpdateFromTestOptions) { - process.env.PORT = '0'; // dynamic port - process.env.UPDATE_CHECKER_ALLOW_DOWNGRADES = 'true'; + try { + const install = getInstaller(kind); - const server = await startAutoUpdateServer(); + const { appPath, uninstall } = install({ + appName, + filepath, + destinationPath: sandboxPath, + }); - const address = server.address(); - assert(typeof address === 'object' && address !== null); - const port = address.port; - const HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE = `http://localhost:${port}`; + try { + process.env.PORT = '0'; // dynamic port + process.env.UPDATE_CHECKER_ALLOW_DOWNGRADES = 'true'; - try { - // must be async because the update server is running in the same process - await executeAsync( - 'npm', - [ - 'run', - '--unsafe-perm', - 'test-packaged', - '--workspace', - 'compass-e2e-tests', - '--', - '--test-filter=auto-update', - ], - { - // We need to use a shell to get environment variables setup correctly - shell: true, - env: { - ...process.env, - HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE, - AUTO_UPDATE_UPDATABLE: (!!autoUpdatable).toString(), - TEST_NAME: 'auto-update-from', - COMPASS_APP_NAME: appName, - COMPASS_APP_PATH: appPath, - }, + const server = await startAutoUpdateServer(); + + const address = server.address(); + assert(typeof address === 'object' && address !== null); + const port = address.port; + const HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE = `http://localhost:${port}`; + + try { + // must be async because the update server is running in the same process + await executeAsync( + 'npm', + [ + 'run', + '--unsafe-perm', + 'test-packaged', + '--workspace', + 'compass-e2e-tests', + '--', + '--test-filter=auto-update', + ], + { + // We need to use a shell to get environment variables setup correctly + shell: true, + env: { + ...process.env, + HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE, + AUTO_UPDATE_UPDATABLE: (!!autoUpdatable).toString(), + TEST_NAME: 'auto-update-from', + COMPASS_APP_NAME: appName, + COMPASS_APP_PATH: appPath, + }, + } + ); + } finally { + console.log('Stopping auto-update server'); + server.close(); + delete process.env.UPDATE_CHECKER_ALLOW_DOWNGRADES; } - ); + } finally { + await uninstall(); + } } finally { - console.log('Stopping auto-update server'); - server.close(); - delete process.env.UPDATE_CHECKER_ALLOW_DOWNGRADES; + if (context.skipCleanup) { + console.log(`Skipped cleaning up sandbox: ${sandboxPath}`); + } else { + console.log(`Cleaning up sandbox: ${sandboxPath}`); + fs.rmSync(sandboxPath, { recursive: true }); + } } } diff --git a/packages/compass-smoke-tests/src/tests/auto-update-to.ts b/packages/compass-smoke-tests/src/tests/auto-update-to.ts index 240adf17732..b3f8e3bba0a 100644 --- a/packages/compass-smoke-tests/src/tests/auto-update-to.ts +++ b/packages/compass-smoke-tests/src/tests/auto-update-to.ts @@ -1,78 +1,107 @@ import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import { type SmokeTestsContext } from '../context'; +import { getInstaller } from '../installers'; +import { createSandbox } from '../directories'; +import { getTestSubjectDetails } from '../test-subject'; import { executeAsync } from '../execute'; +import { getLatestRelease, getLatestReleaseKindByKind } from '../releases'; import { startAutoUpdateServer } from './update-server'; -import { type Channel } from '../build-info'; -type RunUpdateToTestOptions = { - appName: string; - appPath: string; - autoUpdatable?: boolean; - bucketKeyPrefix: string; - version: string; - channel: Channel; -}; +export async function testAutoUpdateTo(context: SmokeTestsContext) { + assert( + context.bucketKeyPrefix !== undefined, + 'Bucket key prefix is needed to download' + ); -export async function testAutoUpdateTo({ - appName, - appPath, - autoUpdatable, - channel, - version, - bucketKeyPrefix, -}: RunUpdateToTestOptions) { - process.env.PORT = '0'; // dynamic port + const sandboxPath = createSandbox(); + const { + kind, + appName, + autoUpdatable, + buildInfo: { channel, version }, + } = getTestSubjectDetails({ ...context, sandboxPath }); - if (channel === 'dev') { - process.env.DEV_RELEASE = JSON.stringify({ - version: version, - bucket_key_prefix: bucketKeyPrefix, - }); - } else { - process.env.PUBLISHED_RELEASES = JSON.stringify({ - name: version, - body: version, - bucket_key_prefix: bucketKeyPrefix, + try { + const install = getInstaller(getLatestReleaseKindByKind(kind)); + const filepath = await getLatestRelease( + channel, + context.arch, + kind, + context.forceDownload + ); + + const { appPath, uninstall } = install({ + appName, + filepath, + destinationPath: sandboxPath, }); - } - const server = await startAutoUpdateServer(); + try { + process.env.PORT = '0'; // dynamic port - const address = server.address(); - assert(typeof address === 'object' && address !== null); - const port = address.port; - const HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE = `http://localhost:${port}`; + if (channel === 'dev') { + process.env.DEV_RELEASE = JSON.stringify({ + version: version, + bucket_key_prefix: context.bucketKeyPrefix, + }); + } else { + process.env.PUBLISHED_RELEASES = JSON.stringify({ + name: version, + body: version, + bucket_key_prefix: context.bucketKeyPrefix, + }); + } - try { - // must be async because the update server is running in the same process - await executeAsync( - 'npm', - [ - 'run', - '--unsafe-perm', - 'test-packaged', - '--workspace', - 'compass-e2e-tests', - '--', - '--test-filter=auto-update', - ], - { - // We need to use a shell to get environment variables setup correctly - shell: true, - env: { - ...process.env, - HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE, - AUTO_UPDATE_UPDATABLE: (!!autoUpdatable).toString(), - TEST_NAME: 'auto-update-to', - EXPECTED_UPDATE_VERSION: version, - COMPASS_APP_NAME: appName, - COMPASS_APP_PATH: appPath, - }, + const server = await startAutoUpdateServer(); + + const address = server.address(); + assert(typeof address === 'object' && address !== null); + const port = address.port; + const HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE = `http://localhost:${port}`; + + try { + // must be async because the update server is running in the same process + await executeAsync( + 'npm', + [ + 'run', + '--unsafe-perm', + 'test-packaged', + '--workspace', + 'compass-e2e-tests', + '--', + '--test-filter=auto-update', + ], + { + // We need to use a shell to get environment variables setup correctly + shell: true, + env: { + ...process.env, + HADRON_AUTO_UPDATE_ENDPOINT_OVERRIDE, + AUTO_UPDATE_UPDATABLE: (!!autoUpdatable).toString(), + TEST_NAME: 'auto-update-to', + EXPECTED_UPDATE_VERSION: version, + COMPASS_APP_NAME: appName, + COMPASS_APP_PATH: appPath, + }, + } + ); + } finally { + console.log('Stopping auto-update server'); + server.close(); + delete process.env.DEV_RELEASE; + delete process.env.PUBLISHED_RELEASES; } - ); + } finally { + await uninstall(); + } } finally { - console.log('Stopping auto-update server'); - server.close(); - delete process.env.DEV_RELEASE; - delete process.env.PUBLISHED_RELEASES; + if (context.skipCleanup) { + console.log(`Skipped cleaning up sandbox: ${sandboxPath}`); + } else { + console.log(`Cleaning up sandbox: ${sandboxPath}`); + fs.rmSync(sandboxPath, { recursive: true }); + } } } diff --git a/packages/compass-smoke-tests/src/tests/time-to-first-query.ts b/packages/compass-smoke-tests/src/tests/time-to-first-query.ts index e19628df049..9566cb92ffc 100644 --- a/packages/compass-smoke-tests/src/tests/time-to-first-query.ts +++ b/packages/compass-smoke-tests/src/tests/time-to-first-query.ts @@ -1,30 +1,57 @@ +import fs from 'node:fs'; +import { type SmokeTestsContext } from '../context'; import { execute } from '../execute'; +import { getInstaller } from '../installers'; +import { createSandbox } from '../directories'; +import { getTestSubject } from '../test-subject'; -type RunE2ETestOptions = { - appName: string; - appPath: string; -}; +export async function testTimeToFirstQuery(context: SmokeTestsContext) { + const sandboxPath = createSandbox(); + const { kind, appName, filepath } = await getTestSubject({ + ...context, + sandboxPath, + }); -export function testTimeToFirstQuery({ appName, appPath }: RunE2ETestOptions) { - execute( - 'npm', - [ - 'run', - '--unsafe-perm', - 'test-packaged', - '--workspace', - 'compass-e2e-tests', - '--', - '--test-filter=time-to-first-query', - ], - { - // We need to use a shell to get environment variables setup correctly - shell: true, - env: { - ...process.env, - COMPASS_APP_NAME: appName, - COMPASS_APP_PATH: appPath, - }, + try { + const install = getInstaller(kind); + + const { appPath, uninstall } = install({ + appName, + filepath, + destinationPath: sandboxPath, + }); + + try { + execute( + 'npm', + [ + 'run', + '--unsafe-perm', + 'test-packaged', + '--workspace', + 'compass-e2e-tests', + '--', + '--test-filter=time-to-first-query', + ], + { + // We need to use a shell to get environment variables setup correctly + shell: true, + env: { + ...process.env, + COMPASS_APP_NAME: appName, + COMPASS_APP_PATH: appPath, + }, + } + ); + } finally { + await uninstall(); + } + } finally { + if (context.skipCleanup) { + console.log(`Skipped cleaning up sandbox: ${sandboxPath}`); + } else { + console.log(`Cleaning up sandbox: ${sandboxPath}`); + fs.rmSync(sandboxPath, { recursive: true }); } - ); + } } From 9bd052429621dff9aed95fc812bfe45e04c171ae Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Thu, 6 Feb 2025 13:10:19 +0000 Subject: [PATCH 8/8] Update packages/compass-smoke-tests/src/releases.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kræn Hansen --- packages/compass-smoke-tests/src/releases.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/compass-smoke-tests/src/releases.ts b/packages/compass-smoke-tests/src/releases.ts index adfe762ba5b..0d8cc807391 100644 --- a/packages/compass-smoke-tests/src/releases.ts +++ b/packages/compass-smoke-tests/src/releases.ts @@ -10,6 +10,10 @@ type PlatformShortName = | 'linux_deb' | 'linux_rpm'; +/** + * Determines a short name (package type) passable to the update server. + * See https://github.com/10gen/compass-mongodb-com/blob/a1fb99908815d3c3ab0efb5960430cc3faf99a15/src/routes/update.js#L353-L377 for the possible values. + */ function getPlatformShortName( arch: Arch, kind: PackageKind