Files
openchamber/packages/electron/relay-dev-tunnel.test.mjs

97 lines
4.3 KiB
JavaScript

import { afterEach, describe, expect, test } from 'bun:test';
import net from 'node:net';
import { MessageChannel } from 'node:worker_threads';
import { createRelayDevTunnelBridge } from './relay-dev-tunnel.mjs';
const bridges = [];
afterEach(() => {
while (bridges.length) bridges.pop().closeAll();
});
describe('relay dev tunnel bridge', () => {
test('pipes a local browser connection through a renderer-owned message port', async () => {
let nextPort;
const webContents = {
id: 7,
isDestroyed: () => false,
once: () => {},
postMessage: (_channel, payload, ports) => {
nextPort = ports[0];
nextPort.on('message', (message) => {
if (message.type !== 'data') return;
expect(Buffer.from(message.data).toString()).toContain('GET /docs HTTP/1.1');
nextPort.postMessage({ type: 'data', data: Buffer.from('HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok') });
nextPort.postMessage({ type: 'close' });
});
nextPort.start();
expect(payload.remotePort).toBe(4322);
nextPort.postMessage({ type: 'ready' });
},
};
const bridge = createRelayDevTunnelBridge({ createMessageChannel: () => new MessageChannel(), logger: { warn: () => {} } });
bridges.push(bridge);
const { localPort } = await bridge.open({ targetKey: 'host:exe', remotePort: 4322, webContents });
const response = await new Promise((resolve, reject) => {
const socket = net.connect({ host: '127.0.0.1', port: localPort }, () => socket.write('GET /docs HTTP/1.1\r\nHost: localhost\r\n\r\n'));
let data = '';
socket.on('data', (chunk) => { data += chunk; });
socket.on('close', () => resolve(data));
socket.on('error', reject);
});
expect(response).toContain('\r\n\r\nok');
});
test('reuses one local listener for the same window, runtime, and port', async () => {
const webContents = { id: 9, isDestroyed: () => false, once: () => {}, postMessage: () => {} };
const bridge = createRelayDevTunnelBridge({ createMessageChannel: () => new MessageChannel() });
bridges.push(bridge);
const first = await bridge.open({ targetKey: 'host:exe', remotePort: 4322, webContents });
const second = await bridge.open({ targetKey: 'host:exe', remotePort: 4322, webContents });
expect(second).toEqual({ localPort: first.localPort, reused: true });
});
test('tells the renderer when the local browser connection closes', async () => {
const rendererClosed = new Promise((resolve) => {
const webContents = {
id: 11,
isDestroyed: () => false,
once: () => {},
postMessage: (_channel, _payload, ports) => {
const rendererPort = ports[0];
rendererPort.on('message', (message) => {
if (message.type === 'close') resolve();
});
rendererPort.start();
rendererPort.postMessage({ type: 'ready' });
},
};
const bridge = createRelayDevTunnelBridge({ createMessageChannel: () => new MessageChannel() });
bridges.push(bridge);
void bridge.open({ targetKey: 'host:exe', remotePort: 4322, webContents }).then(({ localPort }) => {
const socket = net.connect({ host: '127.0.0.1', port: localPort }, () => socket.destroy());
});
});
await rendererClosed;
});
test('closes only listeners owned by the requested desktop window', async () => {
const bridge = createRelayDevTunnelBridge({ createMessageChannel: () => new MessageChannel() });
bridges.push(bridge);
const windowOne = { id: 21, isDestroyed: () => false, once: () => {}, postMessage: () => {} };
const windowTwo = { id: 22, isDestroyed: () => false, once: () => {}, postMessage: () => {} };
const first = await bridge.open({ targetKey: 'host:one', remotePort: 4322, webContents: windowOne });
const second = await bridge.open({ targetKey: 'host:two', remotePort: 4322, webContents: windowTwo });
expect(bridge.closeForWebContents(windowOne.id)).toBe(1);
await expect(new Promise((resolve, reject) => {
const socket = net.connect({ host: '127.0.0.1', port: first.localPort }, resolve);
socket.on('error', reject);
})).rejects.toThrow();
const remaining = await bridge.open({ targetKey: 'host:two', remotePort: 4322, webContents: windowTwo });
expect(remaining).toEqual({ localPort: second.localPort, reused: true });
});
});