src/__tests__/process-helper-spec.js
// jest.dontMock('../lib/process-helper');
describe('process-helper', () => {
describe('start', () => {
it('spawns a child, calls the callback and returns the child', () => {
const processHelper = require('../lib/process-helper.js');
const child = {};
const mockedSpawn = jest.spyOn(processHelper, "spawn");
mockedSpawn.mockReturnValue(child);
const options = {};
const callback = jest.fn();
const ret = processHelper.start(options, callback);
expect(ret).toBe(child);
expect(callback.mock.calls.length).toBe(1);
expect(callback.mock.calls[0][0]).toBeFalsy();
mockedSpawn.mockRestore();
});
it('waits for message if waitForMessage is provided and delegates the callback as is', () => {
const processHelper = require('../lib/process-helper.js');
const cp = require('child_process');
const child = {};
cp.spawn = jest.fn().mockReturnValue(child)
const mockedSpawn = jest.spyOn(processHelper, "spawn");
mockedSpawn.mockImplementation(() => {});
const mockedWaitForMessage = jest.spyOn(processHelper, "waitForMessage");
mockedWaitForMessage.mockImplementation(function (options, child, callback) {
callback.apply(this, [1, 2, 3, 4]);
});
const options = {waitForMessage: 'not null'};
const callback = jest.fn();
const ret = processHelper.start(options, callback);
expect(processHelper.waitForMessage.mock.calls.length).toBe(1);
expect(callback.mock.calls.length).toBe(1);
expect(callback.mock.calls[0]).toEqual([1, 2, 3, 4]);
mockedSpawn.mockRestore();
mockedWaitForMessage.mockRestore();
});
});
describe('spawn', () => {
it('calls spawn with the binary and args and returns the child process', () => {
const cp = require('child_process');
const processHelper = require('../lib/process-helper.js');
processHelper.logOutputs = jest.fn();
const child = {};
cp.spawn = jest.fn().mockReturnValue(child)
const options = {
bin: '/someBinary',
args: ['bunch', 'of', 'args'],
};
const ret = processHelper.spawn(options);
expect(cp.spawn.mock.calls.length).toBe(1);
expect(cp.spawn.mock.calls[0][0]).toBe(options.bin);
expect(cp.spawn.mock.calls[0][1]).toBe(options.args);
expect(ret).toBe(child);
});
it('logs the outputs of the child process', () => {
let cp = require('child_process'),
processHelper = require('../lib/process-helper.js');
processHelper.logOutputs = jest.fn();
const child = {};
spyOn(cp, 'spawn').and.returnValue(child);
const options = {
prefix: 'hey bear',
};
const ret = processHelper.spawn(options);
expect(processHelper.logOutputs.mock.calls.length).toBe(1);
expect(processHelper.logOutputs.mock.calls[0][0]).toBe(options.prefix);
expect(processHelper.logOutputs.mock.calls[0][1]).toBe(child);
});
});
describe('logOutputs', () => {
it('logs the output of the child process stderr events', () => {
let log = require('../lib/log.js'),
processHelper = require('../lib/process-helper.js');
log.debug = jest.fn();
const child = {
stdout: {
on: jest.fn().mockImplementation((event, eventTrigger) => {
eventTrigger('blah');
expect(event).toBe('data');
expect(log.debug.mock.calls.length).toBe(1);
expect(log.debug.mock.calls[0][0]).toBe('[chimp][prefix.stdout]');
expect(log.debug.mock.calls[0][1]).toBe('blah');
}),
},
stderr: {
on: jest.fn().mockImplementation((event, eventTrigger) => {
eventTrigger('blah blah');
expect(event).toBe('data');
expect(log.debug.mock.calls.length).toBe(2);
expect(log.debug.mock.calls[1][0]).toBe('[chimp][prefix.stderr]');
expect(log.debug.mock.calls[1][1]).toBe('blah blah');
}),
},
};
processHelper.logOutputs('prefix', child);
});
});
describe('waitForMessage', () => {
it('removes the listener if the success message is seen and calls the callback', () => {
const processHelper = require('../lib/process-helper.js');
const callback = jest.fn();
const options = {
prefix: '[apollo]',
waitForMessage: 'we have lift off',
};
let eventToBeRemovedStdOut = false;
let eventToBeRemovedStdErr = false;
const child = {
stdout: {
on: jest.fn().mockImplementation((event, eventTrigger) => {
eventToBeRemovedStdOut = eventTrigger;
eventTrigger('Huston, we have lift off!');
}),
removeListener: jest.fn(),
},
stderr: {
on: jest.fn().mockImplementation((event, eventTrigger) => {
eventToBeRemovedStdErr = eventTrigger;
eventTrigger('Huston, we have lift off!');
}),
removeListener: jest.fn(),
},
};
processHelper.waitForMessage(options, child, callback);
expect(child.stdout.removeListener.mock.calls.length).toBe(1);
expect(child.stdout.removeListener.mock.calls[0][0]).toBe('data');
expect(child.stdout.removeListener.mock.calls[0][1]).toBe(eventToBeRemovedStdOut);
expect(child.stderr.removeListener.mock.calls.length).toBe(1);
expect(child.stderr.removeListener.mock.calls[0][0]).toBe('data');
expect(child.stderr.removeListener.mock.calls[0][1]).toBe(eventToBeRemovedStdErr);
expect(callback.mock.calls.length).toBe(2);
expect(callback.mock.calls[0][0]).toBeFalsy();
expect(callback.mock.calls[1][0]).toBeFalsy();
});
it('calls back with an error if the error message is seen', () => {
const processHelper = require('../lib/process-helper.js');
const callback = jest.fn();
const options = {
prefix: '[apollo]',
waitForMessage: 'not empty',
errorMessage: 'engine failure',
};
const eventToBeRemoved = false;
const child = {
stdout: {
on: jest.fn().mockImplementation((event, eventTrigger) => {
eventTrigger('Huston, we have a problem - engine failure!');
}),
},
stderr: {
on: jest.fn().mockImplementation((event, eventTrigger) => {
eventTrigger('Huston, we have a problem - engine failure!');
}),
},
};
processHelper.waitForMessage(options, child, callback);
expect(callback.mock.calls.length).toBe(2);
expect(callback.mock.calls[0][0]).toBe('Huston, we have a problem - engine failure!');
expect(callback.mock.calls[1][0]).toBe('Huston, we have a problem - engine failure!');
});
});
describe('kill', () => {
it('kills the provided process, sets it to null and calls the callback when the process is dead', () => {
const processHelper = require('../lib/process-helper.js');
process.kill = jest.fn().mockImplementation(() => {
// the first call checks if the process exists
// the second call is the actual kill
// subsequent calls are checking if the process exists
// it takes 3 calls to go through all the execution paths for this SUT
if (process.kill.mock.calls.length === 4) {
throw ({code: 'ESRCH'});
}
});
const options = {
child: {
pid: 1234,
},
};
const callback = jest.fn();
jest.useFakeTimers();
processHelper.kill(options, callback);
jest.runAllTimers();
expect(process.kill.mock.calls.length).toBe(4);
expect(process.kill.mock.calls[0][0]).toEqual(1234);
expect(process.kill.mock.calls[0][1]).toBe(0);
expect(process.kill.mock.calls[1][1]).toBe('SIGTERM');
expect(process.kill.mock.calls[2][0]).toEqual(1234);
expect(process.kill.mock.calls[2][1]).toEqual(0);
expect(process.kill.mock.calls[3][0]).toEqual(1234);
expect(process.kill.mock.calls[3][1]).toEqual(0);
expect(options.child).toBe(null);
expect(callback.mock.calls.length).toBe(1);
expect(callback.mock.calls[0][0]).toBeFalsy();
});
});
});