From b7e786f2694968e384025f796095bb372b1ad4e0 Mon Sep 17 00:00:00 2001 From: Sergey Petushkov Date: Tue, 11 Nov 2025 17:29:21 +0100 Subject: [PATCH 1/5] chore(atlas-service): resolve config in runtime to make sure that env overrides are applied --- packages/atlas-service/src/atlas-service.spec.ts | 5 +++-- packages/atlas-service/src/atlas-service.ts | 11 +++++++---- packages/atlas-service/src/util.ts | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/atlas-service/src/atlas-service.spec.ts b/packages/atlas-service/src/atlas-service.spec.ts index 209737f1baf..4676114596a 100644 --- a/packages/atlas-service/src/atlas-service.spec.ts +++ b/packages/atlas-service/src/atlas-service.spec.ts @@ -29,9 +29,10 @@ function getAtlasService( const atlasService = new AtlasService( authService, preferences, - createNoopLogger() + createNoopLogger(), + undefined, + ATLAS_CONFIG ); - atlasService['config'] = ATLAS_CONFIG; return atlasService; } diff --git a/packages/atlas-service/src/atlas-service.ts b/packages/atlas-service/src/atlas-service.ts index d2a659327b8..8fc68714813 100644 --- a/packages/atlas-service/src/atlas-service.ts +++ b/packages/atlas-service/src/atlas-service.ts @@ -52,14 +52,17 @@ function getAutomationAgentClusterId( } export class AtlasService { - private config: AtlasServiceConfig; constructor( private readonly authService: AtlasAuthService, private readonly preferences: PreferencesAccess, private readonly logger: Logger, - private readonly options?: AtlasServiceOptions - ) { - this.config = getAtlasConfig(preferences); + private readonly options?: AtlasServiceOptions, + private readonly defaultConfigOverride?: AtlasServiceConfig + ) {} + // Config value is dynamic to make sure that process.env overrides are taken + // into account in runtime + get config(): AtlasServiceConfig { + return this.defaultConfigOverride ?? getAtlasConfig(this.preferences); } adminApiEndpoint(path?: string): string { return `${this.config.atlasApiBaseUrl}${normalizePath(path)}`; diff --git a/packages/atlas-service/src/util.ts b/packages/atlas-service/src/util.ts index 0fa67be649c..080be1f5915 100644 --- a/packages/atlas-service/src/util.ts +++ b/packages/atlas-service/src/util.ts @@ -255,6 +255,7 @@ export function getAtlasConfig( const { atlasServiceBackendPreset } = preferences.getPreferences(); const envConfig = { atlasApiBaseUrl: process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE, + cloudBaseUrl: process.env.COMPASS_CLOUD_BASE_URL_OVERRIDE, atlasLogin: { clientId: process.env.COMPASS_CLIENT_ID_OVERRIDE, issuer: process.env.COMPASS_OIDC_ISSUER_OVERRIDE, From e6dbaf7f2866226054c7e640c8fed6c8da66d27e Mon Sep 17 00:00:00 2001 From: Sergey Petushkov Date: Tue, 11 Nov 2025 17:34:44 +0100 Subject: [PATCH 2/5] chore(generative-ai): use atlasMetadata as a way to detect which url preset to use --- .../src/atlas-ai-service.spec.ts | 76 +++++++++---------- .../src/atlas-ai-service.ts | 31 ++++---- 2 files changed, 48 insertions(+), 59 deletions(-) diff --git a/packages/compass-generative-ai/src/atlas-ai-service.spec.ts b/packages/compass-generative-ai/src/atlas-ai-service.spec.ts index b2748eda075..1767fef6dd5 100644 --- a/packages/compass-generative-ai/src/atlas-ai-service.spec.ts +++ b/packages/compass-generative-ai/src/atlas-ai-service.spec.ts @@ -21,28 +21,30 @@ const ATLAS_USER = { const BASE_URL = 'http://example.com'; -const mockConnectionInfo: ConnectionInfo = { - id: 'TEST', - connectionOptions: { - connectionString: 'mongodb://localhost:27020', - }, - atlasMetadata: { - orgId: 'testOrg', - projectId: 'testProject', - clusterName: 'pineapple', - regionalBaseUrl: null, - metricsId: 'metricsId', - metricsType: 'replicaSet', - instanceSize: 'M10', - clusterType: 'REPLICASET', - clusterUniqueId: 'clusterUniqueId', - clusterState: 'IDLE', - supports: { - globalWrites: false, - rollingIndexes: false, +const getMockConnectionInfo = (): ConnectionInfo => { + return { + id: 'TEST', + connectionOptions: { + connectionString: 'mongodb://localhost:27020', + }, + atlasMetadata: { + orgId: 'testOrg', + projectId: 'testProject', + clusterName: 'pineapple', + regionalBaseUrl: null, + metricsId: 'metricsId', + metricsType: 'replicaSet', + instanceSize: 'M10', + clusterType: 'REPLICASET', + clusterUniqueId: 'clusterUniqueId', + clusterState: 'IDLE', + supports: { + globalWrites: false, + rollingIndexes: false, + }, + userConnectionString: 'mongodb+srv://localhost:27020', }, - userConnectionString: 'mongodb+srv://localhost:27020', - }, + }; }; class MockAtlasService { @@ -104,9 +106,19 @@ describe('AtlasAiService', function () { ] as const; for (const { apiURLPreset, expectedEndpoints } of endpointBasepathTests) { - describe(`api URL Preset "${apiURLPreset}"`, function () { + const describeName = + apiURLPreset === 'admin-api' + ? 'connection WITHOUT atlas metadata' + : 'connection WITH atlas metadata'; + describe(describeName, function () { let atlasAiService: AtlasAiService; + const mockConnectionInfo = getMockConnectionInfo(); + + if (apiURLPreset === 'admin-api') { + delete mockConnectionInfo.atlasMetadata; + } + beforeEach(function () { const mockAtlasService = new MockAtlasService(); atlasAiService = new AtlasAiService({ @@ -574,26 +586,6 @@ describe('AtlasAiService', function () { expect(requestBody).to.have.property('schema'); }); - it('throws AtlasAiServiceInvalidInputError when connection info lacks atlas metadata', async function () { - const connectionInfoWithoutAtlas = { - ...mockConnectionInfo, - atlasMetadata: undefined, - }; - - try { - await atlasAiService.getMockDataSchema( - mockSchemaInput, - connectionInfoWithoutAtlas as any - ); - expect.fail('Expected getMockDataSchema to throw'); - } catch (err) { - expect(err).to.be.instanceOf(AtlasAiServiceInvalidInputError); - expect((err as Error).message).to.match( - /atlasMetadata is not available/i - ); - } - }); - it('throws AtlasAiServiceApiResponseParseError when API response has invalid format', async function () { const invalidMockResponse = { invalidField: 'invalid data', diff --git a/packages/compass-generative-ai/src/atlas-ai-service.ts b/packages/compass-generative-ai/src/atlas-ai-service.ts index c2df97354bd..04f75a90161 100644 --- a/packages/compass-generative-ai/src/atlas-ai-service.ts +++ b/packages/compass-generative-ai/src/atlas-ai-service.ts @@ -208,10 +208,15 @@ const aiURLConfig = { query: 'unauth/ai/api/v1/mql-query', }, cloud: { - aggregation: (groupId: string) => `ai/v1/groups/${groupId}/mql-aggregation`, - query: (groupId: string) => `ai/v1/groups/${groupId}/mql-query`, - 'mock-data-schema': (groupId: string) => - `ai/v1/groups/${groupId}/mock-data-schema`, + aggregation: (projectId: string) => { + return `ai/v1/groups/${projectId}/mql-aggregation`; + }, + query: (projectId: string) => { + return `ai/v1/groups/${projectId}/mql-query`; + }, + 'mock-data-schema': (projectId: string) => { + return `ai/v1/groups/${projectId}/mock-data-schema`; + }, }, } as const; @@ -290,18 +295,11 @@ export class AtlasAiService { */ private getUrlForEndpoint( resourceType: AIResourceType, - connectionInfo?: ConnectionInfo + { atlasMetadata }: ConnectionInfo ) { - if (this.apiURLPreset === 'cloud') { - const atlasMetadata = connectionInfo?.atlasMetadata; - if (!atlasMetadata) { - throw new AtlasAiServiceInvalidInputError( - "Can't perform generative ai request: atlasMetadata is not available" - ); - } - + if (atlasMetadata) { return this.atlasService.cloudEndpoint( - aiURLConfig[this.apiURLPreset][resourceType](atlasMetadata.projectId) + aiURLConfig.cloud[resourceType](atlasMetadata.projectId) ); } @@ -311,7 +309,7 @@ export class AtlasAiService { ); } - const urlPath = aiURLConfig[this.apiURLPreset][resourceType]; + const urlPath = aiURLConfig['admin-api'][resourceType]; return this.atlasService.adminApiEndpoint(urlPath); } @@ -353,8 +351,7 @@ export class AtlasAiService { }: { urlId: 'query' | 'aggregation'; input: GenerativeAiInput; - - connectionInfo?: ConnectionInfo; + connectionInfo: ConnectionInfo; }, validationFn: (res: any) => asserts res is T ): Promise => { From afb09438a8f7d333c2d5e2a3281d51359fd1676a Mon Sep 17 00:00:00 2001 From: Sergey Petushkov Date: Tue, 11 Nov 2025 17:50:59 +0100 Subject: [PATCH 3/5] chore(assistant): dynamically resolve service url --- .../src/compass-assistant-provider.spec.tsx | 63 +++++++++---------- .../src/compass-assistant-provider.tsx | 16 ++++- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/packages/compass-assistant/src/compass-assistant-provider.spec.tsx b/packages/compass-assistant/src/compass-assistant-provider.spec.tsx index 58351688d07..c5368006fb6 100644 --- a/packages/compass-assistant/src/compass-assistant-provider.spec.tsx +++ b/packages/compass-assistant/src/compass-assistant-provider.spec.tsx @@ -28,7 +28,7 @@ import { CompassAssistantDrawer } from './compass-assistant-drawer'; import { createBrokenTransport, createMockChat } from '../test/utils'; import type { AtlasAiService } from '@mongodb-js/compass-generative-ai/provider'; import type { TrackFunction } from '@mongodb-js/compass-telemetry'; -import { createLogger } from '@mongodb-js/compass-logging'; +import { createNoopLogger } from '@mongodb-js/compass-logging/provider'; function createMockProvider({ mockAtlasService, @@ -480,7 +480,7 @@ describe('CompassAssistantProvider', function () { .stub() .returns('https://localhost:3000'), } as unknown as AtlasService, - logger: createLogger('COMPASS-ASSISTANT-TEST'), + logger: createNoopLogger(), track: track as unknown as TrackFunction, }); await renderOpenAssistantDrawer({ @@ -654,47 +654,42 @@ describe('CompassAssistantProvider', function () { }); }); + let sandbox: sinon.SinonSandbox; + + beforeEach(function () { + sandbox = sinon.createSandbox(); + }); + + afterEach(function () { + if (sandbox) { + sandbox.reset(); + } + }); + it('uses the Atlas Service assistantApiEndpoint', async function () { const mockAtlasService = { - assistantApiEndpoint: sinon + assistantApiEndpoint: sandbox .stub() .returns('https://example.com/assistant/api/v1'), }; - const mockAtlasAiService = { - ensureAiFeatureAccess: sinon.stub().callsFake(() => { - return Promise.resolve(); - }), - }; - - const mockAtlasAuthService = {}; - - const MockedProvider = CompassAssistantProvider.withMockServices({ + const chat = createDefaultChat({ + originForPrompt: 'foo', + appNameForPrompt: 'bar', atlasService: mockAtlasService as unknown as AtlasService, - atlasAiService: mockAtlasAiService as unknown as AtlasAiService, - atlasAuthService: mockAtlasAuthService as unknown as AtlasAuthService, + logger: createNoopLogger(), + track: () => undefined, }); - render( - - - - , - { - preferences: { - enableAIAssistant: true, - enableGenAIFeatures: true, - enableGenAIFeaturesAtlasOrg: true, - cloudFeatureRolloutAccess: { GEN_AI_COMPASS: true }, - }, - } - ); + const fetchStub = sandbox + .stub(globalThis, 'fetch') + .resolves({ ok: true, headers: [] } as any); - await waitFor(() => { - expect(mockAtlasService.assistantApiEndpoint.calledOnce).to.be.true; - }); + await chat.sendMessage({ text: 'hello' }); + + expect(mockAtlasService.assistantApiEndpoint.calledOnce).to.be.true; + expect(fetchStub.lastCall.args[0]).to.eq( + 'https://example.com/assistant/api/v1/responses' + ); }); }); diff --git a/packages/compass-assistant/src/compass-assistant-provider.tsx b/packages/compass-assistant/src/compass-assistant-provider.tsx index e9ced74b509..db7633f65c6 100644 --- a/packages/compass-assistant/src/compass-assistant-provider.tsx +++ b/packages/compass-assistant/src/compass-assistant-provider.tsx @@ -334,6 +334,7 @@ export function createDefaultChat({ transport: Chat['transport']; }; }): Chat { + const initialBaseUrl = 'http://PLACEHOLDER_BASE_URL_TO_BE_REPLACED.invalid'; return new Chat({ transport: options?.transport ?? @@ -343,8 +344,21 @@ export function createDefaultChat({ target: appNameForPrompt, }), model: createOpenAI({ - baseURL: atlasService.assistantApiEndpoint(), + baseURL: initialBaseUrl, apiKey: '', + fetch(url, init) { + return globalThis.fetch( + // The `baseUrl` can be dynamically changed, but `createOpenAI` + // constructor doesn't allow us to change it after initial call. + // Instead we're going to update it every time the fetch call + // happens + String(url).replace( + initialBaseUrl, + atlasService.assistantApiEndpoint() + ), + init + ); + }, }).responses('mongodb-chat-latest'), }), onError: (err: Error) => { From b142d7236e20a9d69532ae164e2e37f5e53f1302 Mon Sep 17 00:00:00 2001 From: Sergey Petushkov Date: Tue, 11 Nov 2025 17:57:27 +0100 Subject: [PATCH 4/5] chore(e2e, web): remove override inlining from the build; make atlas service mock handle CORS properly; re-enable disabled genai tests --- .../helpers/assistant-service.ts | 3 +- .../helpers/compass-web-sandbox.ts | 4 - ...atlas-service.ts => mock-atlas-service.ts} | 71 ++++++++++---- .../compass-e2e-tests/tests/assistant.test.ts | 98 +++++-------------- .../atlas-cloud/collection-ai-query.test.ts | 2 +- .../tests/collection-ai-query.test.ts | 30 +++--- packages/compass-web/sandbox/index.tsx | 19 +--- .../sandbox/sandbox-atlas-sign-in.tsx | 13 +-- packages/compass-web/webpack.config.js | 18 +--- 9 files changed, 101 insertions(+), 157 deletions(-) rename packages/compass-e2e-tests/helpers/{atlas-service.ts => mock-atlas-service.ts} (56%) diff --git a/packages/compass-e2e-tests/helpers/assistant-service.ts b/packages/compass-e2e-tests/helpers/assistant-service.ts index 4504b78f1c1..c36a3d9ae3d 100644 --- a/packages/compass-e2e-tests/helpers/assistant-service.ts +++ b/packages/compass-e2e-tests/helpers/assistant-service.ts @@ -140,7 +140,6 @@ function sendStreamingResponse(res: http.ServerResponse, content: string) { sendChunk(); } -export const MOCK_ASSISTANT_SERVER_PORT = 27097; export async function startMockAssistantServer( { response: _response, @@ -222,7 +221,7 @@ export async function startMockAssistantServer( return sendStreamingResponse(res, response.body); }); }) - .listen(MOCK_ASSISTANT_SERVER_PORT); + .listen(0); await once(server, 'listening'); // address() returns either a string or AddressInfo. diff --git a/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts b/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts index be0b83805dd..caccb9f9608 100644 --- a/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts +++ b/packages/compass-e2e-tests/helpers/compass-web-sandbox.ts @@ -11,7 +11,6 @@ import { } from './test-runner-paths'; import type { ConnectionInfo } from '@mongodb-js/connection-info'; import ConnectionString from 'mongodb-connection-string-url'; -import { MOCK_ASSISTANT_SERVER_PORT } from './assistant-service'; const debug = Debug('compass-e2e-tests:compass-web-sandbox'); @@ -22,9 +21,6 @@ const debug = Debug('compass-e2e-tests:compass-web-sandbox'); process.env.OPEN_BROWSER = 'false'; // tell webpack dev server not to open the default browser process.env.DISABLE_DEVSERVER_OVERLAY = 'true'; process.env.APP_ENV = 'webdriverio'; -// Set the assistant base URL override for tests so we can mock the assistant server -process.env.COMPASS_ASSISTANT_BASE_URL_OVERRIDE = `http://localhost:${MOCK_ASSISTANT_SERVER_PORT}`; -process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES = 'true'; const wait = (ms: number) => { return new Promise((resolve) => { diff --git a/packages/compass-e2e-tests/helpers/atlas-service.ts b/packages/compass-e2e-tests/helpers/mock-atlas-service.ts similarity index 56% rename from packages/compass-e2e-tests/helpers/atlas-service.ts rename to packages/compass-e2e-tests/helpers/mock-atlas-service.ts index cad0687736c..47d7068fefb 100644 --- a/packages/compass-e2e-tests/helpers/atlas-service.ts +++ b/packages/compass-e2e-tests/helpers/mock-atlas-service.ts @@ -61,33 +61,62 @@ export async function startMockAtlasServiceServer( let response = _response; const server = http .createServer((req, res) => { - if (req.method === 'GET') { - requests.push({ - req, - content: null, - }); - return aiFeatureEnableResponse(req, res); + res.setHeader('Access-Control-Allow-Origin', req.headers.origin ?? '*'); + res.setHeader('Access-Control-Allow-Credentials', 'true'); + res.setHeader( + 'Access-Control-Allow-Headers', + req.headers['access-control-request-headers'] ?? '*' + ); + + if (req.method === 'OPTIONS') { + res.statusCode = 200; + res.end(); + return; + } + + if (req.url?.startsWith('/settings/optInDataExplorerGenAIFeatures')) { + res.statusCode = 200; + res.end(); + return; } - let body = ''; - req - .setEncoding('utf8') - .on('data', (chunk) => { - body += chunk; - }) - .on('end', () => { - const jsonObject = JSON.parse(body); + if (req.url?.startsWith('/unauth/ai/api/v1/mql-query')) { + if (req.method === 'GET') { requests.push({ req, - content: jsonObject, + content: null, }); + return aiFeatureEnableResponse(req, res); + } + + let body = ''; + + req + .setEncoding('utf8') + .on('data', (chunk) => { + body += chunk; + }) + .on('end', () => { + const jsonObject = JSON.parse(body); + requests.push({ + req, + content: jsonObject, + }); + + res.setHeader('Content-Type', 'application/json'); + if (response.status !== 200) { + res.writeHead(response.status); + } + return res.end(JSON.stringify(response.body)); + }); + + return; + } - res.setHeader('Content-Type', 'application/json'); - if (response.status !== 200) { - res.writeHead(response.status); - } - return res.end(JSON.stringify(response.body)); - }); + res.statusCode = 404; + res.statusMessage = + 'Route not found in the mock Atlas backend. Did you forget to add a mock route support?'; + res.end(); }) .listen(0); await once(server, 'listening'); diff --git a/packages/compass-e2e-tests/tests/assistant.test.ts b/packages/compass-e2e-tests/tests/assistant.test.ts index f3289f8301c..318df1239cd 100644 --- a/packages/compass-e2e-tests/tests/assistant.test.ts +++ b/packages/compass-e2e-tests/tests/assistant.test.ts @@ -9,15 +9,13 @@ import { cleanup, screenshotIfFailed, DEFAULT_CONNECTION_NAME_1, - skipForWeb, screenshotPathName, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; -import { startMockAtlasServiceServer } from '../helpers/atlas-service'; +import { startMockAtlasServiceServer } from '../helpers/mock-atlas-service'; import { startMockAssistantServer } from '../helpers/assistant-service'; import type { MockAssistantResponse } from '../helpers/assistant-service'; -import { isTestingWeb } from '../helpers/test-runner-context'; import { context } from '../helpers/test-runner-context'; @@ -53,12 +51,22 @@ describe('MongoDB Assistant', function () { mockAtlasServer = await startMockAtlasServiceServer(); mockAssistantServer = await startMockAssistantServer(); - process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE = - mockAtlasServer.endpoint; - telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); + await compass.browser.setEnv( + 'COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE', + mockAtlasServer.endpoint + ); + await compass.browser.setEnv( + 'COMPASS_CLOUD_BASE_URL_OVERRIDE', + mockAtlasServer.endpoint + ); + await compass.browser.setEnv( + 'COMPASS_ASSISTANT_BASE_URL_OVERRIDE', + mockAssistantServer.endpoint + ); + sendMessage = async ( text: string, { @@ -126,13 +134,8 @@ describe('MongoDB Assistant', function () { }; setAIFeatures = async (newValue: boolean) => { - if (isTestingWeb()) { - await browser.setEnv( - 'COMPASS_OVERRIDE_ENABLE_AI_FEATURES', - newValue ? 'true' : 'false' - ); - } await browser.setFeature('enableGenAIFeatures', newValue); + await browser.setFeature('enableGenAISampleDocumentPassing', newValue); if (newValue) { await browser.$(Selectors.AssistantDrawerButton).waitForDisplayed(); @@ -144,26 +147,6 @@ describe('MongoDB Assistant', function () { }; setAIOptIn = async (newValue: boolean) => { - if ( - isTestingWeb() || - ((await browser.getFeature('optInGenAIFeatures')) === true && - newValue === false) - ) { - await cleanup(compass); - // Reseting the opt-in to false can be tricky so it's best to start over in this case. - compass = await init(this.test?.fullTitle(), { firstRun: true }); - await setup(); - - if (isTestingWeb()) { - await setAIFeatures(true); - } - await browser.setFeature( - 'optInGenAIFeatures', - newValue ? 'true' : 'false' - ); - return; - } - await browser.setFeature('optInGenAIFeatures', newValue); }; @@ -177,25 +160,15 @@ describe('MongoDB Assistant', function () { after(async function () { await mockAtlasServer.stop(); await mockAssistantServer.stop(); - - delete process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE; - await cleanup(compass); await telemetry.stop(); }); afterEach(async function () { - await screenshotIfFailed(compass, this.currentTest); + mockAssistantServer.clearRequests(); + await clearChat(browser); - try { - mockAssistantServer.clearRequests(); - await clearChat(browser); - } catch (err) { - await browser.screenshot( - screenshotPathName('afterEach-MongoDB-Assistant') - ); - throw err; - } + await screenshotIfFailed(compass, this.currentTest); }); describe('drawer visibility', function () { @@ -208,12 +181,7 @@ describe('MongoDB Assistant', function () { }); it('does not show the assistant drawer button when AI features are disabled', async function () { - // we cannot opt back out on web because it is stored server-side - skipForWeb( - this, - 'E2E testing for assistant drawer visibility on compass-web is not yet implemented' - ); - + await setAIOptIn(false); await setAIFeatures(false); const drawerButton = browser.$(Selectors.AssistantDrawerButton); @@ -238,12 +206,7 @@ describe('MongoDB Assistant', function () { }); describe('before opt-in', function () { - // we cannot opt back out on web because it is stored server-side before(async function () { - skipForWeb( - this, - 'E2E testing for opt-in on compass-web is not yet implemented' - ); await setAIOptIn(false); }); @@ -304,18 +267,8 @@ describe('MongoDB Assistant', function () { describe('opting in', function () { before(async function () { - // we cannot opt back out on web because it is stored server-side - skipForWeb( - this, - 'E2E testing for opt-in on compass-web is not yet implemented' - ); - try { - await setAIOptIn(false); - await openAssistantDrawer(browser); - } catch (err) { - await browser.screenshot(screenshotPathName('before-opting-in')); - throw err; - } + await setAIOptIn(false); + await openAssistantDrawer(browser); }); it('sends the message if the user opts in', async function () { @@ -341,13 +294,8 @@ describe('MongoDB Assistant', function () { describe('after opt-in', function () { before(async function () { - try { - await setAIOptIn(true); - await openAssistantDrawer(browser); - } catch (err) { - await browser.screenshot(screenshotPathName('before-after-opting-in')); - throw err; - } + await setAIOptIn(true); + await openAssistantDrawer(browser); }); describe('clear chat button', function () { diff --git a/packages/compass-e2e-tests/tests/atlas-cloud/collection-ai-query.test.ts b/packages/compass-e2e-tests/tests/atlas-cloud/collection-ai-query.test.ts index 657c3a435bb..68232676205 100644 --- a/packages/compass-e2e-tests/tests/atlas-cloud/collection-ai-query.test.ts +++ b/packages/compass-e2e-tests/tests/atlas-cloud/collection-ai-query.test.ts @@ -13,7 +13,7 @@ import { createNumbersCollection } from '../../helpers/insert-data'; import { isTestingAtlasCloudSandbox } from '../../helpers/test-runner-context'; import { switchPipelineMode } from '../../helpers/commands/switch-pipeline-mode'; -describe('Collection ai query', function () { +describe('Collection ai query (with real Cloud backend)', function () { let compass: Compass; let browser: CompassBrowser; diff --git a/packages/compass-e2e-tests/tests/collection-ai-query.test.ts b/packages/compass-e2e-tests/tests/collection-ai-query.test.ts index d1454932598..919aa54490b 100644 --- a/packages/compass-e2e-tests/tests/collection-ai-query.test.ts +++ b/packages/compass-e2e-tests/tests/collection-ai-query.test.ts @@ -7,17 +7,15 @@ import { init, cleanup, screenshotIfFailed, - skipForWeb, - TEST_COMPASS_WEB, DEFAULT_CONNECTION_NAME_1, } from '../helpers/compass'; import type { Compass } from '../helpers/compass'; import * as Selectors from '../helpers/selectors'; import { createNumbersCollection } from '../helpers/insert-data'; -import { startMockAtlasServiceServer } from '../helpers/atlas-service'; -import type { MockAtlasServerResponse } from '../helpers/atlas-service'; +import { startMockAtlasServiceServer } from '../helpers/mock-atlas-service'; +import type { MockAtlasServerResponse } from '../helpers/mock-atlas-service'; -describe('Collection ai query', function () { +describe('Collection ai query (with mocked backend)', function () { let compass: Compass; let browser: CompassBrowser; let telemetry: Telemetry; @@ -27,10 +25,6 @@ describe('Collection ai query', function () { let clearRequests: () => void; before(async function () { - skipForWeb(this, 'ai queries not yet available in compass-web'); - - process.env.COMPASS_E2E_SKIP_AI_OPT_IN = 'true'; - // Start a mock server to pass an ai response. const { endpoint, @@ -45,11 +39,19 @@ describe('Collection ai query', function () { clearRequests = _clearRequests; setMockAtlasServerResponse = _setMockAtlasServerResponse; - process.env.COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE = endpoint; - telemetry = await startTelemetryServer(); compass = await init(this.test?.fullTitle()); browser = compass.browser; + + await browser.setEnv( + 'COMPASS_ATLAS_SERVICE_UNAUTH_BASE_URL_OVERRIDE', + endpoint + ); + + await browser.setFeature('enableGenAIFeatures', true); + await browser.setFeature('enableGenAISampleDocumentPassing', true); + await browser.setFeature('optInGenAIFeatures', true); + await browser.setupDefaultConnections(); }); @@ -66,14 +68,8 @@ describe('Collection ai query', function () { }); after(async function () { - if (TEST_COMPASS_WEB) { - return; - } - await stopMockAtlasServer(); - delete process.env.COMPASS_E2E_SKIP_AI_OPT_IN; - await cleanup(compass); await telemetry.stop(); }); diff --git a/packages/compass-web/sandbox/index.tsx b/packages/compass-web/sandbox/index.tsx index 0515d7164d2..7b64a3e4fa3 100644 --- a/packages/compass-web/sandbox/index.tsx +++ b/packages/compass-web/sandbox/index.tsx @@ -91,9 +91,6 @@ const App = () => { return { readOnly: true }; })(); - const overrideGenAIFeatures = - process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES === 'true'; - return ( { enableCreatingNewConnections: !isAtlas, enableGlobalWrites: isAtlas, enableRollingIndexes: isAtlas, - showDisabledConnections: true, + enableGenAIFeaturesAtlasOrg: + !isAtlas || !!enableGenAIFeaturesAtlasOrg, enableGenAIFeaturesAtlasProject: - overrideGenAIFeatures || - (isAtlas && !!enableGenAIFeaturesAtlasProject), + !isAtlas || !!enableGenAIFeaturesAtlasProject, enableGenAISampleDocumentPassing: - overrideGenAIFeatures || - (isAtlas && !!enableGenAISampleDocumentPassing), - enableGenAIFeaturesAtlasOrg: - overrideGenAIFeatures || - (isAtlas && !!enableGenAIFeaturesAtlasOrg), - optInGenAIFeatures: - overrideGenAIFeatures || (isAtlas && !!optInGenAIFeatures), - enableDataModeling: true, + !!enableGenAISampleDocumentPassing, + optInGenAIFeatures: isAtlas ? !!optInGenAIFeatures : false, enableDataModelingCollapse: true, enableMyQueries: isAtlas, ...groupRolePreferences, diff --git a/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx b/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx index b7d7b1f72e9..51c5c964bc0 100644 --- a/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx +++ b/packages/compass-web/sandbox/sandbox-atlas-sign-in.tsx @@ -130,25 +130,20 @@ export function useAtlasProxySignIn(): AtlasLoginReturnValue { userRoles, currentOrganization, } = params; - const overrideGenAIFeatures = - process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES === 'true'; setProjectParams({ orgId: currentOrganization.id, projectId, csrfToken, csrfTime, optInGenAIFeatures: isOptedIntoDataExplorerGenAIFeatures, - enableGenAIFeaturesAtlasOrg: - overrideGenAIFeatures || genAIFeaturesEnabled, + enableGenAIFeaturesAtlasOrg: genAIFeaturesEnabled, enableGenAISampleDocumentPassing: !groupEnabledFeatureFlags.includes( 'DISABLE_DATA_EXPLORER_GEN_AI_SAMPLE_DOCUMENT_PASSING' ), - enableGenAIFeaturesAtlasProject: - overrideGenAIFeatures || - groupEnabledFeatureFlags.includes( - 'ENABLE_DATA_EXPLORER_GEN_AI_FEATURES' - ), + enableGenAIFeaturesAtlasProject: groupEnabledFeatureFlags.includes( + 'ENABLE_DATA_EXPLORER_GEN_AI_FEATURES' + ), userRoles, }); setStatus('signed-in'); diff --git a/packages/compass-web/webpack.config.js b/packages/compass-web/webpack.config.js index ef12b44bdfd..bff3ec5ad3b 100644 --- a/packages/compass-web/webpack.config.js +++ b/packages/compass-web/webpack.config.js @@ -151,20 +151,10 @@ module.exports = (env, args) => { // Can be either `web` or `webdriverio`, helpful if we need special // behavior for tests in sandbox 'process.env.APP_ENV': JSON.stringify(process.env.APP_ENV ?? 'web'), - ...(process.env.COMPASS_ASSISTANT_BASE_URL_OVERRIDE - ? { - 'process.env.COMPASS_ASSISTANT_BASE_URL_OVERRIDE': JSON.stringify( - process.env.COMPASS_ASSISTANT_BASE_URL_OVERRIDE - ), - } - : {}), - ...(process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES - ? { - 'process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES': JSON.stringify( - process.env.COMPASS_OVERRIDE_ENABLE_AI_FEATURES - ), - } - : {}), + // NB: DefinePlugin completely replaces matched string with a provided + // value, in most cases WE DO NOT WANT THAT and process variables in the + // code are added to be able to change them in runtime. Do not add new + // records unless you're super sure it's needed }), new webpack.ProvidePlugin({ From a20468537236519910702888facf7601c9cc923d Mon Sep 17 00:00:00 2001 From: Sergey Petushkov Date: Wed, 12 Nov 2025 09:50:53 +0100 Subject: [PATCH 5/5] chore(e2e): restore lost screenshots --- .../compass-e2e-tests/tests/assistant.test.ts | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/compass-e2e-tests/tests/assistant.test.ts b/packages/compass-e2e-tests/tests/assistant.test.ts index 318df1239cd..49c31c791ee 100644 --- a/packages/compass-e2e-tests/tests/assistant.test.ts +++ b/packages/compass-e2e-tests/tests/assistant.test.ts @@ -165,10 +165,16 @@ describe('MongoDB Assistant', function () { }); afterEach(async function () { - mockAssistantServer.clearRequests(); - await clearChat(browser); - await screenshotIfFailed(compass, this.currentTest); + try { + mockAssistantServer.clearRequests(); + await clearChat(browser); + } catch (err) { + await browser.screenshot( + screenshotPathName('afterEach-MongoDB-Assistant') + ); + throw err; + } }); describe('drawer visibility', function () { @@ -267,8 +273,13 @@ describe('MongoDB Assistant', function () { describe('opting in', function () { before(async function () { - await setAIOptIn(false); - await openAssistantDrawer(browser); + try { + await setAIOptIn(false); + await openAssistantDrawer(browser); + } catch (err) { + await browser.screenshot(screenshotPathName('before-opting-in')); + throw err; + } }); it('sends the message if the user opts in', async function () { @@ -294,8 +305,13 @@ describe('MongoDB Assistant', function () { describe('after opt-in', function () { before(async function () { - await setAIOptIn(true); - await openAssistantDrawer(browser); + try { + await setAIOptIn(true); + await openAssistantDrawer(browser); + } catch (err) { + await browser.screenshot(screenshotPathName('before-after-opting-in')); + throw err; + } }); describe('clear chat button', function () {