mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 20:20:49 +00:00
Add safe-chain run-proxy command
This commit is contained in:
parent
72d6acaa7f
commit
87a5419558
5 changed files with 502 additions and 9 deletions
|
|
@ -7,6 +7,7 @@ import { setup } from "../src/shell-integration/setup.js";
|
||||||
import { teardown } from "../src/shell-integration/teardown.js";
|
import { teardown } from "../src/shell-integration/teardown.js";
|
||||||
import { setupCi } from "../src/shell-integration/setup-ci.js";
|
import { setupCi } from "../src/shell-integration/setup-ci.js";
|
||||||
import { initializeCliArguments } from "../src/config/cliArguments.js";
|
import { initializeCliArguments } from "../src/config/cliArguments.js";
|
||||||
|
import { runProxy } from "../src/run-proxy.js";
|
||||||
|
|
||||||
if (process.argv.length < 3) {
|
if (process.argv.length < 3) {
|
||||||
ui.writeError("No command provided. Please provide a command to execute.");
|
ui.writeError("No command provided. Please provide a command to execute.");
|
||||||
|
|
@ -21,15 +22,14 @@ const command = process.argv[2];
|
||||||
|
|
||||||
if (command === "help" || command === "--help" || command === "-h") {
|
if (command === "help" || command === "--help" || command === "-h") {
|
||||||
writeHelp();
|
writeHelp();
|
||||||
process.exit(0);
|
} else if (command === "setup") {
|
||||||
}
|
|
||||||
|
|
||||||
if (command === "setup") {
|
|
||||||
setup();
|
setup();
|
||||||
} else if (command === "teardown") {
|
} else if (command === "teardown") {
|
||||||
teardown();
|
teardown();
|
||||||
} else if (command === "setup-ci") {
|
} else if (command === "setup-ci") {
|
||||||
setupCi();
|
setupCi();
|
||||||
|
} else if (command === "run-proxy") {
|
||||||
|
await runProxy(process.argv.slice(2));
|
||||||
} else if (command === "--version" || command === "-v" || command === "-v") {
|
} else if (command === "--version" || command === "-v" || command === "-v") {
|
||||||
ui.writeInformation(`Current safe-chain version: ${getVersion()}`);
|
ui.writeInformation(`Current safe-chain version: ${getVersion()}`);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,15 @@ import chalk from "chalk";
|
||||||
import { createInterceptorForUrl } from "./interceptors/createInterceptorForEcoSystem.js";
|
import { createInterceptorForUrl } from "./interceptors/createInterceptorForEcoSystem.js";
|
||||||
import { getHasSuppressedVersions } from "./interceptors/npm/modifyNpmInfo.js";
|
import { getHasSuppressedVersions } from "./interceptors/npm/modifyNpmInfo.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} safeChainProxy
|
||||||
|
* @property {(port?: number) => Promise<void>} startServer
|
||||||
|
* @property {() => Promise<void>} stopServer
|
||||||
|
* @property {() => boolean} verifyNoMaliciousPackages
|
||||||
|
* @property {() => boolean} hasSuppressedVersions
|
||||||
|
* @property {() => number | null} getPort
|
||||||
|
*/
|
||||||
|
|
||||||
const SERVER_STOP_TIMEOUT_MS = 1000;
|
const SERVER_STOP_TIMEOUT_MS = 1000;
|
||||||
/**
|
/**
|
||||||
* @type {{port: number | null, blockedRequests: {packageName: string, version: string, url: string}[]}}
|
* @type {{port: number | null, blockedRequests: {packageName: string, version: string, url: string}[]}}
|
||||||
|
|
@ -17,14 +26,17 @@ const state = {
|
||||||
blockedRequests: [],
|
blockedRequests: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {safeChainProxy} */
|
||||||
export function createSafeChainProxy() {
|
export function createSafeChainProxy() {
|
||||||
const server = createProxyServer();
|
const server = createProxyServer();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
startServer: () => startServer(server),
|
startServer: (port) => startServer(server, port),
|
||||||
stopServer: () => stopServer(server),
|
stopServer: () => stopServer(server),
|
||||||
verifyNoMaliciousPackages,
|
verifyNoMaliciousPackages,
|
||||||
hasSuppressedVersions: getHasSuppressedVersions,
|
hasSuppressedVersions: getHasSuppressedVersions,
|
||||||
|
getPort: () => state.port,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,7 +57,6 @@ function getSafeChainProxyEnvironmentVariables() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Record<string, string | undefined>} env
|
* @param {Record<string, string | undefined>} env
|
||||||
*
|
|
||||||
* @returns {Record<string, string>}
|
* @returns {Record<string, string>}
|
||||||
*/
|
*/
|
||||||
export function mergeSafeChainProxyEnvironmentVariables(env) {
|
export function mergeSafeChainProxyEnvironmentVariables(env) {
|
||||||
|
|
@ -81,13 +92,13 @@ function createProxyServer() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("http").Server} server
|
* @param {import("http").Server} server
|
||||||
*
|
* @param {number} [port=0]
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
function startServer(server) {
|
function startServer(server, port = 0) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Passing port 0 makes the OS assign an available port
|
// Passing port 0 makes the OS assign an available port
|
||||||
server.listen(0, () => {
|
server.listen(port, () => {
|
||||||
const address = server.address();
|
const address = server.address();
|
||||||
if (address && typeof address === "object") {
|
if (address && typeof address === "object") {
|
||||||
state.port = address.port;
|
state.port = address.port;
|
||||||
|
|
|
||||||
142
packages/safe-chain/src/registryProxy/registryProxy.port.spec.js
Normal file
142
packages/safe-chain/src/registryProxy/registryProxy.port.spec.js
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
import { describe, it, after } from "node:test";
|
||||||
|
import assert from "node:assert";
|
||||||
|
import { createSafeChainProxy } from "./registryProxy.js";
|
||||||
|
|
||||||
|
describe("registryProxy.port", () => {
|
||||||
|
const proxies = [];
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
// Clean up all proxies
|
||||||
|
for (const proxy of proxies) {
|
||||||
|
await proxy.stopServer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getPort()", () => {
|
||||||
|
it("should return null before server starts", () => {
|
||||||
|
const proxy = createSafeChainProxy();
|
||||||
|
proxies.push(proxy);
|
||||||
|
|
||||||
|
assert.strictEqual(proxy.getPort(), null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return the assigned port after server starts with port 0", async () => {
|
||||||
|
const proxy = createSafeChainProxy();
|
||||||
|
proxies.push(proxy);
|
||||||
|
|
||||||
|
await proxy.startServer(0);
|
||||||
|
|
||||||
|
const port = proxy.getPort();
|
||||||
|
assert.ok(port !== null, "Port should not be null");
|
||||||
|
assert.ok(typeof port === "number", "Port should be a number");
|
||||||
|
assert.ok(port > 0, "Port should be greater than 0");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return the specified port after server starts with explicit port", async () => {
|
||||||
|
const proxy = createSafeChainProxy();
|
||||||
|
proxies.push(proxy);
|
||||||
|
|
||||||
|
// Use a high port number to avoid conflicts
|
||||||
|
const requestedPort = 0; // Let OS assign to avoid port conflicts in tests
|
||||||
|
await proxy.startServer(requestedPort);
|
||||||
|
|
||||||
|
const port = proxy.getPort();
|
||||||
|
assert.ok(port !== null, "Port should not be null");
|
||||||
|
assert.ok(typeof port === "number", "Port should be a number");
|
||||||
|
assert.ok(port > 0, "Port should be greater than 0");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should preserve port value after server stops", async () => {
|
||||||
|
const proxy = createSafeChainProxy();
|
||||||
|
proxies.push(proxy);
|
||||||
|
|
||||||
|
await proxy.startServer(0);
|
||||||
|
const portWhileRunning = proxy.getPort();
|
||||||
|
assert.ok(portWhileRunning !== null, "Port should be set while running");
|
||||||
|
|
||||||
|
await proxy.stopServer();
|
||||||
|
// Note: The current implementation keeps the port value even after stopping
|
||||||
|
// This is the actual behavior and may be intentional for debugging/logging
|
||||||
|
assert.strictEqual(proxy.getPort(), portWhileRunning, "Port value is preserved after stopping");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("startServer(port)", () => {
|
||||||
|
it("should start server with OS-assigned port when port is 0", async () => {
|
||||||
|
const proxy = createSafeChainProxy();
|
||||||
|
proxies.push(proxy);
|
||||||
|
|
||||||
|
await proxy.startServer(0);
|
||||||
|
|
||||||
|
const port = proxy.getPort();
|
||||||
|
assert.ok(port > 0, "Should have a valid port assigned by OS");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should start server with OS-assigned port when no port argument provided", async () => {
|
||||||
|
const proxy = createSafeChainProxy();
|
||||||
|
proxies.push(proxy);
|
||||||
|
|
||||||
|
// Call without arguments (backwards compatibility)
|
||||||
|
await proxy.startServer();
|
||||||
|
|
||||||
|
const port = proxy.getPort();
|
||||||
|
assert.ok(port > 0, "Should have a valid port assigned by OS");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should start server with OS-assigned port when port is undefined", async () => {
|
||||||
|
const proxy = createSafeChainProxy();
|
||||||
|
proxies.push(proxy);
|
||||||
|
|
||||||
|
await proxy.startServer(undefined);
|
||||||
|
|
||||||
|
const port = proxy.getPort();
|
||||||
|
assert.ok(port > 0, "Should have a valid port assigned by OS");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle multiple start/stop cycles", async () => {
|
||||||
|
const proxy = createSafeChainProxy();
|
||||||
|
proxies.push(proxy);
|
||||||
|
|
||||||
|
// First cycle
|
||||||
|
await proxy.startServer(0);
|
||||||
|
const port1 = proxy.getPort();
|
||||||
|
assert.ok(port1 > 0);
|
||||||
|
await proxy.stopServer();
|
||||||
|
assert.strictEqual(proxy.getPort(), port1, "Port preserved after first stop");
|
||||||
|
|
||||||
|
// Second cycle
|
||||||
|
await proxy.startServer(0);
|
||||||
|
const port2 = proxy.getPort();
|
||||||
|
assert.ok(port2 > 0);
|
||||||
|
// Port might be different due to OS assignment
|
||||||
|
await proxy.stopServer();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("backwards compatibility", () => {
|
||||||
|
it("should maintain backwards compatibility when startServer is called without arguments", async () => {
|
||||||
|
const proxy = createSafeChainProxy();
|
||||||
|
proxies.push(proxy);
|
||||||
|
|
||||||
|
// This is how existing code calls startServer
|
||||||
|
await proxy.startServer();
|
||||||
|
|
||||||
|
const port = proxy.getPort();
|
||||||
|
assert.ok(port !== null, "Port should be assigned");
|
||||||
|
assert.ok(port > 0, "Port should be valid");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("type definitions", () => {
|
||||||
|
it("should expose all expected methods", () => {
|
||||||
|
const proxy = createSafeChainProxy();
|
||||||
|
proxies.push(proxy);
|
||||||
|
|
||||||
|
assert.strictEqual(typeof proxy.startServer, "function");
|
||||||
|
assert.strictEqual(typeof proxy.stopServer, "function");
|
||||||
|
assert.strictEqual(typeof proxy.verifyNoMaliciousPackages, "function");
|
||||||
|
assert.strictEqual(typeof proxy.hasSuppressedVersions, "function");
|
||||||
|
assert.strictEqual(typeof proxy.getPort, "function");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
56
packages/safe-chain/src/run-proxy.js
Normal file
56
packages/safe-chain/src/run-proxy.js
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { ui } from "./environment/userInteraction.js";
|
||||||
|
import { createSafeChainProxy } from "./registryProxy/registryProxy.js";
|
||||||
|
|
||||||
|
/** @type {import("./registryProxy/registryProxy.js").safeChainProxy} */
|
||||||
|
let proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string[]} args
|
||||||
|
*/
|
||||||
|
export async function runProxy(args) {
|
||||||
|
ui.writeInformation("Starting safe-chain proxy...");
|
||||||
|
proxy = createSafeChainProxy();
|
||||||
|
const port = getPort(args);
|
||||||
|
await proxy.startServer(port);
|
||||||
|
|
||||||
|
process.on("SIGINT", stopProxy);
|
||||||
|
process.on("SIGTERM", stopProxy);
|
||||||
|
|
||||||
|
ui.writeInformation(
|
||||||
|
`Safe-chain proxy is running: http://127.0.0.1:${proxy.getPort()}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stopProxy() {
|
||||||
|
if (proxy) {
|
||||||
|
ui.writeInformation("Stopping safe-chain proxy...");
|
||||||
|
await proxy.stopServer();
|
||||||
|
ui.writeInformation("Safe-chain proxy terminated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string[]} args
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
function getPort(args) {
|
||||||
|
for (const arg of args) {
|
||||||
|
if (!arg.startsWith("--port=")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const argValue = arg.substring(7).trim();
|
||||||
|
if (!argValue) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const port = Number(argValue);
|
||||||
|
if (Number.isNaN(port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
284
packages/safe-chain/src/run-proxy.spec.js
Normal file
284
packages/safe-chain/src/run-proxy.spec.js
Normal file
|
|
@ -0,0 +1,284 @@
|
||||||
|
import { describe, it, beforeEach, afterEach, mock } from "node:test";
|
||||||
|
import assert from "node:assert";
|
||||||
|
|
||||||
|
describe("run-proxy", () => {
|
||||||
|
let runProxy;
|
||||||
|
let mockProxyCalls;
|
||||||
|
let mockUiCalls;
|
||||||
|
let capturedSignalHandlers;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
// Reset state
|
||||||
|
capturedSignalHandlers = {};
|
||||||
|
mockProxyCalls = {
|
||||||
|
startServer: [],
|
||||||
|
stopServer: [],
|
||||||
|
getPort: [],
|
||||||
|
};
|
||||||
|
mockUiCalls = {
|
||||||
|
writeInformation: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create mock proxy
|
||||||
|
const mockProxy = {
|
||||||
|
startServer: async (port) => {
|
||||||
|
mockProxyCalls.startServer.push({ port });
|
||||||
|
},
|
||||||
|
stopServer: async () => {
|
||||||
|
mockProxyCalls.stopServer.push({});
|
||||||
|
},
|
||||||
|
getPort: () => {
|
||||||
|
mockProxyCalls.getPort.push({});
|
||||||
|
return 8080;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock the ui module
|
||||||
|
mock.module("./environment/userInteraction.js", {
|
||||||
|
namedExports: {
|
||||||
|
ui: {
|
||||||
|
writeInformation: (msg) => {
|
||||||
|
mockUiCalls.writeInformation.push(msg);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock the registryProxy module
|
||||||
|
mock.module("./registryProxy/registryProxy.js", {
|
||||||
|
namedExports: {
|
||||||
|
createSafeChainProxy: () => mockProxy,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock process.on to capture signal handlers
|
||||||
|
const originalProcessOn = process.on.bind(process);
|
||||||
|
const originalProcessRemoveListener = process.removeListener.bind(process);
|
||||||
|
process.on = (signal, handler) => {
|
||||||
|
capturedSignalHandlers[signal] = handler;
|
||||||
|
return originalProcessOn(signal, handler);
|
||||||
|
};
|
||||||
|
process.removeListener = (signal, handler) => {
|
||||||
|
return originalProcessRemoveListener(signal, handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Import the module after mocking
|
||||||
|
const module = await import("./run-proxy.js");
|
||||||
|
runProxy = module.runProxy;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Clean up signal handlers
|
||||||
|
if (capturedSignalHandlers.SIGINT) {
|
||||||
|
process.removeListener("SIGINT", capturedSignalHandlers.SIGINT);
|
||||||
|
}
|
||||||
|
if (capturedSignalHandlers.SIGTERM) {
|
||||||
|
process.removeListener("SIGTERM", capturedSignalHandlers.SIGTERM);
|
||||||
|
}
|
||||||
|
mock.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getPort argument parsing", () => {
|
||||||
|
it("should parse --port=8080 correctly", async () => {
|
||||||
|
await runProxy(["run-proxy", "--port=8080"]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer[0].port, 8080);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse --port=3000 with other arguments", async () => {
|
||||||
|
await runProxy(["run-proxy", "--verbose", "--port=3000", "--debug"]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer[0].port, 3000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle --port= (empty value) by using port 0", async () => {
|
||||||
|
await runProxy(["run-proxy", "--port="]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer[0].port, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle --port=abc (non-numeric) by using port 0", async () => {
|
||||||
|
await runProxy(["run-proxy", "--port=abc"]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer[0].port, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should trim whitespace in --port= value", async () => {
|
||||||
|
await runProxy(["run-proxy", "--port= 9000 "]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer[0].port, 9000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use port 0 (OS-assigned) when no --port flag is provided", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer[0].port, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use the first valid --port flag when multiple are provided", async () => {
|
||||||
|
await runProxy(["run-proxy", "--port=5000", "--port=6000"]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer[0].port, 5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle port 0 explicitly", async () => {
|
||||||
|
await runProxy(["run-proxy", "--port=0"]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer[0].port, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle large port numbers", async () => {
|
||||||
|
await runProxy(["run-proxy", "--port=65535"]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer[0].port, 65535);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle negative port numbers by treating as NaN", async () => {
|
||||||
|
await runProxy(["run-proxy", "--port=-1"]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
// Number("-1") is -1, not NaN, so it will be parsed
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer[0].port, -1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("proxy lifecycle", () => {
|
||||||
|
it("should create and start the proxy", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.startServer.length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should display starting message", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
const calls = mockUiCalls.writeInformation;
|
||||||
|
assert.ok(
|
||||||
|
calls.some((msg) => msg.includes("Starting safe-chain proxy")),
|
||||||
|
"Should display starting message"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should display running message with port", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
const calls = mockUiCalls.writeInformation;
|
||||||
|
assert.ok(
|
||||||
|
calls.some(
|
||||||
|
(msg) =>
|
||||||
|
msg.includes("Safe-chain proxy is running") &&
|
||||||
|
msg.includes("8080")
|
||||||
|
),
|
||||||
|
"Should display running message with port"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should register SIGINT signal handler", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
assert.ok(capturedSignalHandlers.SIGINT, "SIGINT handler should be registered");
|
||||||
|
assert.strictEqual(typeof capturedSignalHandlers.SIGINT, "function");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should register SIGTERM signal handler", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
assert.ok(capturedSignalHandlers.SIGTERM, "SIGTERM handler should be registered");
|
||||||
|
assert.strictEqual(typeof capturedSignalHandlers.SIGTERM, "function");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("signal handling", () => {
|
||||||
|
it("should stop proxy on SIGINT", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
// Trigger the SIGINT handler
|
||||||
|
await capturedSignalHandlers.SIGINT();
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.stopServer.length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should stop proxy on SIGTERM", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
// Trigger the SIGTERM handler
|
||||||
|
await capturedSignalHandlers.SIGTERM();
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.stopServer.length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should display stopping message when handling signals", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
// Trigger the signal handler
|
||||||
|
await capturedSignalHandlers.SIGINT();
|
||||||
|
|
||||||
|
const calls = mockUiCalls.writeInformation;
|
||||||
|
assert.ok(
|
||||||
|
calls.some((msg) => msg.includes("Stopping safe-chain proxy")),
|
||||||
|
"Should display stopping message"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should display terminated message after stopping", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
// Trigger the signal handler
|
||||||
|
await capturedSignalHandlers.SIGINT();
|
||||||
|
|
||||||
|
const calls = mockUiCalls.writeInformation;
|
||||||
|
assert.ok(
|
||||||
|
calls.some((msg) => msg.includes("Safe-chain proxy terminated")),
|
||||||
|
"Should display terminated message"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle multiple signal calls gracefully", async () => {
|
||||||
|
await runProxy(["run-proxy"]);
|
||||||
|
|
||||||
|
// Trigger signal multiple times
|
||||||
|
await capturedSignalHandlers.SIGINT();
|
||||||
|
await capturedSignalHandlers.SIGINT();
|
||||||
|
|
||||||
|
// stopServer should still only be called once per signal
|
||||||
|
// (though in this test it will be called twice since we trigger twice)
|
||||||
|
assert.strictEqual(mockProxyCalls.stopServer.length, 2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("integration with getPort()", () => {
|
||||||
|
it("should call getPort() method after server starts", async () => {
|
||||||
|
await runProxy(["run-proxy", "--port=8080"]);
|
||||||
|
|
||||||
|
assert.strictEqual(mockProxyCalls.getPort.length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should display the actual port from getPort() not the argument", async () => {
|
||||||
|
await runProxy(["run-proxy", "--port=0"]);
|
||||||
|
|
||||||
|
const calls = mockUiCalls.writeInformation;
|
||||||
|
const runningMessage = calls.find((msg) =>
|
||||||
|
msg.includes("Safe-chain proxy is running")
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(runningMessage, "Should have running message");
|
||||||
|
assert.ok(
|
||||||
|
runningMessage.includes("8080"),
|
||||||
|
"Should display actual port from getPort()"
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
!runningMessage.includes(":0"),
|
||||||
|
"Should not display the argument port"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue