UNPKG

n8n-nodes-pytenable

Version:

Un nodo de n8n para interactuar con la API de Tenable usando Pytenable en un sandbox de Docker.

125 lines (124 loc) 6.45 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const Pytenable_node_1 = require("./Pytenable.node"); const dockerode_1 = __importDefault(require("dockerode")); const stream_1 = require("stream"); // Mock the dockerode library jest.mock('dockerode'); // A helper to create the mock Docker stream header for multiplexed streams const createDockerStreamChunk = (channel, data) => { const header = Buffer.alloc(8); header.writeUInt8(channel, 0); // 1 for stdout, 2 for stderr header.writeUInt32BE(data.length, 4); return Buffer.concat([header, Buffer.from(data)]); }; describe('Pytenable Node with Docker', () => { let executeFunctions; beforeEach(() => { jest.clearAllMocks(); jest.setTimeout(30000); executeFunctions = { getNode: jest.fn().mockReturnValue({ getContext: jest.fn().mockReturnValue({ executionId: 'test-execution', node: { name: 'Pytenable', type: 'Pytenable', id: 'test-node-id' }, }), }), getNodeParameter: jest.fn(), getCredentials: jest.fn().mockResolvedValue({}), getInputData: jest.fn(), helpers: { returnJsonArray: jest.fn(data => data), constructExecutionMetaData: jest.fn((data, options) => data.map((item) => ({ json: item, pairedItem: options.pairedItem }))), }, }; }); it('should execute for all items at once successfully', async () => { // Arrange executeFunctions.getNodeParameter .mockReturnValueOnce('runOnceForAllItems') .mockReturnValueOnce('return [{"status": "ok"}]'); executeFunctions.getInputData.mockReturnValue([{ json: { id: 1 } }]); executeFunctions.helpers.constructExecutionMetaData.mockImplementation((data, _) => data.map((item) => ({ json: item }))); const mockStream = new stream_1.PassThrough(); // Prevent the node's call to stream.end() from closing our mock stream mockStream.end = jest.fn(); const mockContainer = { attach: jest.fn().mockResolvedValue(mockStream), start: jest.fn().mockImplementation(cb => cb(null)), }; const mockDocker = { createContainer: jest.fn().mockResolvedValue(mockContainer) }; dockerode_1.default.mockReturnValue(mockDocker); // Act const executionPromise = new Pytenable_node_1.Pytenable().execute.call(executeFunctions); // Simulate container output and end the stream manually for the node's listener await new Promise(resolve => setImmediate(resolve)); // Wait for listeners to attach const output = JSON.stringify([{ status: 'ok' }]); mockStream.write(createDockerStreamChunk(1, output)); mockStream.emit('end'); const result = await executionPromise; // Assert expect(result[0]).toEqual([expect.objectContaining({ json: { status: 'ok' } })]); expect(mockDocker.createContainer).toHaveBeenCalledTimes(1); }); it('should execute for each item individually and pair results', async () => { // Arrange executeFunctions.getNodeParameter .mockReturnValueOnce('runOnceForEachItem') .mockReturnValueOnce('return [{"id": _item["id"] * 2}]'); executeFunctions.getInputData.mockReturnValue([{ json: { id: 1 } }, { json: { id: 2 } }]); executeFunctions.helpers.returnJsonArray.mockImplementation(data => data); executeFunctions.helpers.constructExecutionMetaData.mockImplementation((data, _) => data.map((item) => ({ json: item }))); const mockStreams = [new stream_1.PassThrough(), new stream_1.PassThrough()]; mockStreams.forEach(s => { s.end = jest.fn(); }); // Disable end on both streams const mockContainer = { attach: jest.fn() .mockResolvedValueOnce(mockStreams[0]) .mockResolvedValueOnce(mockStreams[1]), start: jest.fn().mockImplementation(cb => cb(null)), }; const mockDocker = { createContainer: jest.fn().mockResolvedValue(mockContainer) }; dockerode_1.default.mockReturnValue(mockDocker); // Act const executionPromise = new Pytenable_node_1.Pytenable().execute.call(executeFunctions); // Simulate responses for each item await new Promise(resolve => setImmediate(resolve)); mockStreams[0].write(createDockerStreamChunk(1, JSON.stringify([{ id: 2 }]))); mockStreams[0].emit('end'); await new Promise(resolve => setImmediate(resolve)); mockStreams[1].write(createDockerStreamChunk(1, JSON.stringify([{ id: 4 }]))); mockStreams[1].emit('end'); const result = await executionPromise; // Assert const returnData = result[0]; expect(returnData.length).toBe(2); expect(returnData[0]).toEqual({ json: { id: 2 }, pairedItem: { item: 0 } }); expect(returnData[1]).toEqual({ json: { id: 4 }, pairedItem: { item: 1 } }); }); it('should handle Python script error from stderr', async () => { // Arrange executeFunctions.getNodeParameter .mockReturnValueOnce('runOnceForAllItems') .mockReturnValueOnce('raise Exception("Boom!")'); executeFunctions.getInputData.mockReturnValue([{}]); const mockStream = new stream_1.PassThrough(); mockStream.end = jest.fn(); const mockContainer = { attach: jest.fn().mockResolvedValue(mockStream), start: jest.fn().mockImplementation(cb => cb(null)), }; const mockDocker = { createContainer: jest.fn().mockResolvedValue(mockContainer) }; dockerode_1.default.mockReturnValue(mockDocker); // Act const executionPromise = new Pytenable_node_1.Pytenable().execute.call(executeFunctions); await new Promise(resolve => setImmediate(resolve)); const errorOutput = "Traceback... Exception: Boom!"; mockStream.write(createDockerStreamChunk(2, errorOutput)); mockStream.emit('end'); // Assert await expect(executionPromise).rejects.toThrow('Error en el contenedor de Python: Traceback... Exception: Boom!'); }); });