150 lines
4.4 KiB
JavaScript
150 lines
4.4 KiB
JavaScript
const {
|
|
FastCGIHandler,
|
|
constants,
|
|
buildFastCGIPacket,
|
|
buildNameValuePair,
|
|
writeFastCGIPacket
|
|
} = require("../../src/utils/fastCgi.js");
|
|
const net = require("net");
|
|
const EventEmitter = require("events");
|
|
|
|
jest.mock("net");
|
|
|
|
describe("FastCGI module", () => {
|
|
let mockSocket;
|
|
|
|
beforeEach(() => {
|
|
mockSocket = {
|
|
write: jest.fn(),
|
|
on: jest.fn().mockImplementation((event, handler) => {
|
|
if (event === "data") {
|
|
mockSocket.dataHandler = handler;
|
|
}
|
|
return mockSocket; // Enable method chaining
|
|
}),
|
|
removeListener: jest.fn().mockImplementation((event, handler) => {
|
|
if (event === "data" && mockSocket.dataHandler == handler) {
|
|
mockSocket.dataHandler = () => {};
|
|
}
|
|
return mockSocket; // Enable method chaining
|
|
}),
|
|
dataHandler: () => {}
|
|
};
|
|
|
|
net.createConnection.mockReturnValue(mockSocket);
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
test("should create a socket and connect", () => {
|
|
const handler = new FastCGIHandler({ host: "localhost", port: 9000 });
|
|
|
|
expect(net.createConnection).toHaveBeenCalledWith(
|
|
{ host: "localhost", port: 9000 },
|
|
expect.any(Function)
|
|
);
|
|
|
|
expect(mockSocket.on).toHaveBeenCalledWith("error", expect.any(Function));
|
|
expect(mockSocket.on).toHaveBeenCalledWith("data", expect.any(Function));
|
|
expect(handler).toBeInstanceOf(EventEmitter);
|
|
});
|
|
|
|
test("should build a FastCGI packet", () => {
|
|
const content = Buffer.from("test");
|
|
|
|
const packet = buildFastCGIPacket(1, 1, content);
|
|
expect(packet.length).toBe(16); // Header + padded content
|
|
expect(packet.readUInt8(0)).toBe(1); // version
|
|
expect(packet.readUInt8(1)).toBe(1); // type
|
|
expect(packet.readUInt16BE(2)).toBe(1); // requestID
|
|
expect(packet.readUInt16BE(4)).toBe(4); // content length
|
|
expect(packet.readUInt8(6)).toBe(4); // padding length
|
|
});
|
|
|
|
test("should write FastCGI packets to the socket", () => {
|
|
const handler = new FastCGIHandler({});
|
|
const content = Buffer.from("test");
|
|
|
|
writeFastCGIPacket(handler.socket, 1, handler.requestID, content);
|
|
expect(mockSocket.write).toHaveBeenCalledTimes(1);
|
|
expect(mockSocket.write).toHaveBeenCalledWith(expect.any(Buffer));
|
|
});
|
|
|
|
// eslint-disable-next-line jest/no-done-callback
|
|
test("should handle socket data and emit events", (done) => {
|
|
const handler = new FastCGIHandler({});
|
|
handler.stdout.on("data", (chunk) => {
|
|
expect(chunk).toStrictEqual(Buffer.from("Hello World"));
|
|
done();
|
|
});
|
|
|
|
// Simulate a FastCGI STDOUT packet
|
|
const packet = buildFastCGIPacket(
|
|
constants.STDOUT,
|
|
handler.requestID,
|
|
Buffer.from("Hello World")
|
|
);
|
|
|
|
mockSocket.dataHandler(packet);
|
|
});
|
|
|
|
// eslint-disable-next-line jest/no-done-callback
|
|
test("should handle END_REQUEST and emit 'exit' event", (done) => {
|
|
const handler = new FastCGIHandler({});
|
|
|
|
handler.on("exit", (code, signal) => {
|
|
expect(code).toBe(0);
|
|
expect(signal).toBeNull();
|
|
done();
|
|
});
|
|
|
|
// Simulate an END_REQUEST packet
|
|
const content = Buffer.alloc(8);
|
|
content.writeUInt32BE(0, 0); // appStatus
|
|
content.writeUInt8(0, 4); // protocolStatus
|
|
const packet = buildFastCGIPacket(
|
|
constants.END_REQUEST,
|
|
handler.requestID,
|
|
content
|
|
);
|
|
|
|
mockSocket.dataHandler(packet);
|
|
});
|
|
|
|
test("should handle UNKNOWN_TYPE and discard it", () => {
|
|
const handler = new FastCGIHandler({});
|
|
const dataListener = jest.fn();
|
|
handler.stdout.on("data", dataListener);
|
|
|
|
// Simulate an UNKNOWN_TYPE packet
|
|
const packet = buildFastCGIPacket(
|
|
constants.UNKNOWN_TYPE,
|
|
handler.requestID,
|
|
Buffer.alloc(0)
|
|
);
|
|
|
|
mockSocket.dataHandler(packet);
|
|
expect(dataListener).not.toHaveBeenCalledWith();
|
|
});
|
|
|
|
test("should build name-value pairs correctly", () => {
|
|
const pair = buildNameValuePair("key", "value");
|
|
expect(pair.length).toBe(10); // 1 byte for key length, 1 for value length, and key-value content
|
|
expect(pair.toString("utf8", 2)).toBe("keyvalue");
|
|
});
|
|
|
|
test("should send environment variables on init", () => {
|
|
const env = { FOO: "BAR", BAZ: "QUX" };
|
|
const handler = new FastCGIHandler({ env });
|
|
|
|
handler.init();
|
|
|
|
expect(mockSocket.write).toHaveBeenCalled();
|
|
const calls = mockSocket.write.mock.calls;
|
|
|
|
const envPacket = calls.find(([arg]) => arg.includes(Buffer.from("FOO")));
|
|
expect(envPacket).toBeTruthy();
|
|
});
|
|
});
|