@signalapp/mock-server
Version:
Mock Signal Server for writing tests
117 lines (116 loc) • 3.96 kB
JavaScript
;
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Service = void 0;
const assert_1 = __importDefault(require("assert"));
const long_1 = __importDefault(require("long"));
const debug_1 = __importDefault(require("debug"));
const compiled_1 = require("../../../protos/compiled");
const debug = (0, debug_1.default)('mock:ws:service');
const WSMessage = compiled_1.signalservice.WebSocketMessage;
class Service {
ws;
requests = new Map();
lastSentId = 0;
constructor(ws) {
this.ws = ws;
this.ws = ws;
this.ws.on('message', async (message) => {
try {
await this.onMessage(message);
}
catch (error) {
(0, assert_1.default)(error instanceof Error);
debug('onMessage error', error.stack);
}
});
this.ws.once('close', () => this.onClose());
}
async send(verb, path, options) {
const id = this.lastSentId++;
const packet = WSMessage.encode({
type: WSMessage.Type.REQUEST,
request: {
...options,
verb,
path,
id: long_1.default.fromNumber(id),
},
}).finish();
this.ws.send(packet);
return await new Promise((resolve) => this.requests.set(id, resolve));
}
async onMessage(raw) {
if (!(raw instanceof Uint8Array)) {
throw new Error('Unexpected input');
}
const message = WSMessage.decode(raw);
if (message.type === WSMessage.Type.RESPONSE) {
const response = message.response;
if (!response) {
throw new Error('Expected response in message');
}
if (!response.id) {
throw new Error('Expected response.id');
}
const id = parseInt(response.id.toString(), 10);
if (isNaN(id)) {
throw new Error(`Invalid response.id: ${response.id}`);
}
const resolve = this.requests.get(id);
if (!resolve) {
throw new Error(`Unexpected response: ${id}`);
}
resolve(response);
}
else if (message.type === WSMessage.Type.REQUEST) {
const request = message.request;
if (!request) {
throw new Error('Expected request in message');
}
if (!request.id) {
throw new Error('Expected request.id');
}
let response;
try {
response = await this.handleRequest(request);
}
catch (error) {
(0, assert_1.default)(error instanceof Error);
console.error('handleRequest error', error.stack);
response = {
status: 500,
body: Buffer.from(JSON.stringify({
error: error.stack,
})),
};
}
// Keepalive responses
const packet = WSMessage.encode({
type: WSMessage.Type.RESPONSE,
response: {
...response,
id: request.id,
},
}).finish();
this.ws.send(packet);
}
else {
debug('unsupported message', message);
}
}
onClose() {
for (const [id, resolve] of this.requests.entries()) {
resolve({
id: long_1.default.fromNumber(id),
status: 500,
message: 'WebSocket is gone',
});
}
}
}
exports.Service = Service;