Skip to content

Commit

Permalink
fixup: add e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
addaleax committed Aug 26, 2024
1 parent d1bf511 commit 0d53e6c
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 5 deletions.
3 changes: 2 additions & 1 deletion packages/compass-e2e-tests/.depcheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ ignores:
- '@wdio/types'
- 'mongodb-compass'
- 'ps-list'
- 'mongodb-runner'
- 'mongodb-runner'
- '@electron/remote'
6 changes: 6 additions & 0 deletions packages/compass-e2e-tests/helpers/commands/connect-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,12 @@ export async function setConnectFormState(
state.oidcUsername
);
}
if (state.oidcUseApplicationProxy === false) {
await browser.expandAccordion(Selectors.ConnectionFormOIDCAdvancedToggle);
await browser.clickParent(
Selectors.ConnectionFormOIDCUseApplicationProxyCheckbox
);
}
}

// FLE2
Expand Down
3 changes: 2 additions & 1 deletion packages/compass-e2e-tests/helpers/connect-form-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface ConnectFormState {

// - OIDC
oidcUsername?: string; // (Principal).
oidcUseApplicationProxy?: boolean;

// - AWS IAM
awsAccessKeyId?: string;
Expand All @@ -57,7 +58,7 @@ export interface ConnectFormState {
tlsAllowInvalidCertificates?: boolean;

// Proxy/SSH
proxyMethod?: 'none' | 'password' | 'identity' | 'socks';
proxyMethod?: 'none' | 'password' | 'identity' | 'socks' | 'app-proxy';

// FLE2
fleKeyVaultNamespace?: string;
Expand Down
71 changes: 71 additions & 0 deletions packages/compass-e2e-tests/helpers/proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type {
Server as HTTPServer,
IncomingMessage,
ServerResponse,
} from 'http';
import { request } from 'http';
import type { Socket } from 'net';
import { connect } from 'net';

export interface ProxyHandlersResult {
connectRequests: IncomingMessage[];
httpForwardRequests: IncomingMessage[];
connections: Socket[];
}

export function setupProxyServer(server: HTTPServer): ProxyHandlersResult {
const connectRequests: IncomingMessage[] = [];
const httpForwardRequests: IncomingMessage[] = [];
const connections: Socket[] = [];

server.on('connect', onconnect);
server.on('request', onrequest);
function onconnect(
this: HTTPServer,
req: IncomingMessage,
socket: Socket,
head: Buffer
): void {
(req as any).server = this;
let host: string;
let port: string;
if (req.url?.includes(']:')) {
[host, port] = req.url.slice(1).split(']:');
} else {
[host, port] = (req.url ?? '').split(':');
}
if (host === 'compass.mongodb.com' || host === 'downloads.mongodb.com') {
// The snippet loader and update notifier can reach out to thes endpoints,
// but we usually do not actually wait for this to happen or not in CI,
// so we're just ignoring these requests here to avoid flaky behavior.
socket.end();
return;
}
connectRequests.push(req);
socket.unshift(head);
socket.write('HTTP/1.0 200 OK\r\n\r\n');
const outbound = connect(+port, host);
socket.pipe(outbound).pipe(socket);
// socket.on('data', chk => console.log('[from client] ' + chk.toString()));
// outbound.on('data', chk => console.log('[from server] ' + chk.toString()));
const cleanup = () => {
outbound.destroy();
socket.destroy();
};
outbound.on('error', cleanup);
socket.on('error', cleanup);
connections.push(socket, outbound);
}
function onrequest(req: IncomingMessage, res: ServerResponse) {
httpForwardRequests.push(req);
const proxyReq = request(
req.url!,
{ method: req.method, headers: req.headers },
(proxyRes) => proxyRes.pipe(res)
);
if (req.method === 'GET') proxyReq.end();
else req.pipe(proxyReq);
}

return { connections, connectRequests, httpForwardRequests };
}
10 changes: 10 additions & 0 deletions packages/compass-e2e-tests/helpers/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ export const ConnectionFormInputPlainPassword =
'[data-testid="connection-plain-password-input"]';
export const ConnectionFormInputOIDCUsername =
'[data-testid="connection-oidc-username-input"]';
export const ConnectionFormOIDCAdvancedToggle =
'[data-testid="oidc-advanced-options"]';
export const ConnectionFormOIDCUseApplicationProxyCheckbox =
'[data-testid="oidc-use-application-level-proxy"]';
export const ConnectionFormInputAWSAccessKeyId =
'[data-testid="connection-form-aws-access-key-id-input"]';
export const ConnectionFormInputAWSSecretAccessKey =
Expand Down Expand Up @@ -1387,5 +1391,11 @@ export const AtlasLoginStatus = '[data-testid="atlas-login-status"]';
export const AtlasLoginErrorToast = '#atlas-sign-in-error';
export const AgreeAndContinueButton = 'button=Agree and continue';

// Proxy settings
export const ProxyUrl =
'[data-testid="proxy-settings"] [data-testid="proxy-url"]';
export const ProxyCustomButton =
'[data-testid="proxy-settings"] [data-testid="custom-radio"]';

// Close tab confirmation
export const ConfirmTabCloseModal = '[data-testid="confirm-tab-close"]';
81 changes: 81 additions & 0 deletions packages/compass-e2e-tests/tests/oidc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
connectionNameFromString,
TEST_MULTIPLE_CONNECTIONS,
} from '../helpers/compass';
import { setupProxyServer } from '../helpers/proxy';
import * as Selectors from '../helpers/selectors';
import type { Compass } from '../helpers/compass';
import type { OIDCMockProviderConfig } from '@mongodb-js/oidc-mock-provider';
Expand All @@ -18,6 +19,9 @@ import path from 'path';
import os from 'os';
import { promises as fs } from 'fs';
import { once, EventEmitter } from 'events';
import type { Server as HTTPServer, IncomingMessage } from 'http';
import { createServer as createHTTPServer } from 'http';
import type { Socket, AddressInfo } from 'net';
import { expect } from 'chai';
import type { MongoCluster } from '@mongodb-js/compass-test-server';
import { startTestServer } from '@mongodb-js/compass-test-server';
Expand Down Expand Up @@ -487,4 +491,81 @@ describe('OIDC integration', function () {

expect(oidcMockProviderEndpointAccesses['/authorize']).to.equal(1);
});

context('when using a proxy', function () {
let httpServer: HTTPServer;
let connectRequests: IncomingMessage[];
let httpForwardRequests: IncomingMessage[];
let connections: Socket[];

beforeEach(async function () {
await browser.execute(function () {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { safeStorage } = require('@electron/remote');
if (!safeStorage.isEncryptionAvailable())
safeStorage.setUsePlainTextEncryption(true);
if (!safeStorage.isEncryptionAvailable())
throw new Error('encryption not available on this platform');
});
await browser.setFeature('proxy', '');
await browser.setFeature('enableProxySupport', true);
httpServer = createHTTPServer();
({ connectRequests, httpForwardRequests, connections } =
setupProxyServer(httpServer));
httpServer.listen(0);
await once(httpServer, 'listening');
});

afterEach(async function () {
await browser.setFeature('proxy', '');
httpServer?.close?.();
for (const conn of connections) {
if (!conn.destroyed) conn.destroy();
}
});

it('can proxy both HTTP and MongoDB traffic through a proxy', async function () {
await browser.openSettingsModal('proxy');
await browser.clickParent(Selectors.ProxyCustomButton);
await browser.setValueVisible(
Selectors.ProxyUrl,
`http://localhost:${(httpServer.address() as AddressInfo).port}`
);
await browser.clickVisible(Selectors.SaveSettingsButton);

await browser.connectWithConnectionForm({
hosts: [hostport],
authMethod: 'MONGODB-OIDC',
connectionName,
oidcUseApplicationProxy: true,
proxyMethod: 'app-proxy',
});

expect(connectRequests.map((c) => c.url)).to.include(hostport);
expect(httpForwardRequests.map((c) => c.url)).to.include(
`${oidcMockProvider.issuer}/.well-known/openid-configuration`
);
});

it('can choose not to forward OIDC HTTP traffic', async function () {
await browser.openSettingsModal('proxy');
await browser.clickParent(Selectors.ProxyCustomButton);
await browser.setValueVisible(
Selectors.ProxyUrl,
`http://localhost:${(httpServer.address() as AddressInfo).port}`
);
await browser.clickVisible(Selectors.SaveSettingsButton);

await browser.connectWithConnectionForm({
hosts: [hostport],
authMethod: 'MONGODB-OIDC',
connectionName,
oidcUseApplicationProxy: false,
proxyMethod: 'app-proxy',
});

expect(connectRequests.map((c) => c.url)).to.include(hostport);
expect(httpForwardRequests.map((c) => c.url)).to.be.empty;
});
});
});
56 changes: 54 additions & 2 deletions packages/compass-e2e-tests/tests/proxy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import {
import { expect } from 'chai';
import type { Compass } from '../helpers/compass';
import type { CompassBrowser } from '../helpers/compass-browser';
import type { Server as HTTPServer } from 'http';
import type { Server as HTTPServer, IncomingMessage } from 'http';
import { createServer as createHTTPServer } from 'http';
import type { AddressInfo, Server } from 'net';
import type { AddressInfo, Server, Socket } from 'net';
import { once } from 'events';
import { setupProxyServer } from '../helpers/proxy';
import * as Selectors from '../helpers/selectors';

async function listen(srv: Server): Promise<void> {
srv.listen(0);
Expand Down Expand Up @@ -83,4 +85,54 @@ describe('Proxy support', function () {
});
expect(result).to.equal('hello, http://compass.mongodb.com/ (proxy2)');
});

context('when connecting to a cluster', function () {
let connectRequests: IncomingMessage[];
let connections: Socket[];

beforeEach(async function () {
compass = await init(this.test?.fullTitle());
browser = compass.browser;

await browser.execute(function () {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { safeStorage } = require('@electron/remote');
if (!safeStorage.isEncryptionAvailable())
safeStorage.setUsePlainTextEncryption(true);
if (!safeStorage.isEncryptionAvailable())
throw new Error('encryption not available on this platform');
});
await browser.setFeature('proxy', '');
await browser.setFeature('enableProxySupport', true);
httpProxyServer1.removeAllListeners('request');
({ connectRequests, connections } = setupProxyServer(httpProxyServer1));
});

afterEach(async function () {
await browser?.setFeature('proxy', '');
for (const conn of connections) {
if (!conn.destroyed) conn.destroy();
}
});

it('can proxy MongoDB traffic through a proxy', async function () {
await browser.openSettingsModal('proxy');
await browser.clickParent(Selectors.ProxyCustomButton);
await browser.setValueVisible(
Selectors.ProxyUrl,
`http://localhost:${(httpProxyServer1.address() as AddressInfo).port}`
);
await browser.clickVisible(Selectors.SaveSettingsButton);

const hostport = '127.0.0.1:27091';
const connectionName = this.test?.fullTitle() ?? '';
await browser.connectWithConnectionForm({
hosts: [hostport],
connectionName,
proxyMethod: 'app-proxy',
});

expect(connectRequests.map((c) => c.url)).to.include(hostport);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function AuthenticationOIDC({
/>
</FormFieldContainer>
<FormFieldContainer>
<Accordion text="OIDC Options">
<Accordion text="OIDC Options" data-testid="oidc-advanced-options">
<FormFieldContainer>
<TextInput
data-testid="connection-oidc-auth-code-flow-redirect-uri-input"
Expand Down
4 changes: 4 additions & 0 deletions packages/data-service/src/connect-mongo-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ export async function connectMongoClientDataService({
'generate-credentials',
'mongodb://'
);
// TODO: Not urgent, but it might be helpful to properly implement redaction
// and then actually log this to the log file, it's been helpful for debugging
// e2e tests for sure
// console.log({tunnel, tunnelOptions: getTunnelOptions(connectionOptions), connectionOptions, oidcOptions})
if (tunnel && logger)
hookProxyLogger(tunnel.logger, logger, 'compass-tunnel');
const tunnelForwardingErrors: Error[] = [];
Expand Down

0 comments on commit 0d53e6c

Please sign in to comment.