Skip to content

Commit

Permalink
[kbn-es] add basic integration tests for exit code/promise handling (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
spalger authored Apr 19, 2018
1 parent 0520696 commit 6a7c047
Show file tree
Hide file tree
Showing 3 changed files with 340 additions and 1 deletion.
2 changes: 1 addition & 1 deletion packages/kbn-es/src/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ exports.Cluster = class Cluster {
this._process.once('exit', code => {
// JVM exits with 143 on SIGTERM and 130 on SIGINT, dont' treat them as errors
if (code > 0 && !(code === 143 || code === 130)) {
reject(createCliError(`ES exitted with code ${code}`));
reject(createCliError(`ES exited with code ${code}`));
} else {
resolve();
}
Expand Down
9 changes: 9 additions & 0 deletions packages/kbn-es/src/integration_tests/__fixtures__/es_bin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env node

const { exitCode, start } = JSON.parse(process.argv[2]);

if (start) {
console.log('started');
}

process.exitCode = exitCode;
330 changes: 330 additions & 0 deletions packages/kbn-es/src/integration_tests/cluster.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
const { createToolingLog } = require('@kbn/dev-utils');
const execa = require('execa');
const { Cluster } = require('../cluster');
const {
installSource,
installSnapshot,
installArchive,
} = require('../install');

jest.mock('../install', () => ({
installSource: jest.fn(),
installSnapshot: jest.fn(),
installArchive: jest.fn(),
}));

jest.mock('execa', () => jest.fn());

const log = createToolingLog('verbose');
log.onData = jest.fn();
log.on('data', log.onData);

function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async function ensureNoResolve(promise) {
await Promise.race([
sleep(100),
promise.then(() => {
throw new Error('promise was not supposed to resolve');
}),
]);
}

async function ensureResolve(promise) {
return await Promise.race([
promise,
sleep(100).then(() => {
throw new Error(
'promise was supposed to resolve with installSource() resolution'
);
}),
]);
}

function mockEsBin({ exitCode, start }) {
execa.mockImplementationOnce((cmd, args, options) =>
require.requireActual('execa')(
process.execPath,
[
require.resolve('./__fixtures__/es_bin.js'),
JSON.stringify({
exitCode,
start,
}),
],
options
)
);
}

beforeEach(() => {
jest.resetAllMocks();
});

describe('#installSource()', () => {
it('awaits installSource() promise and returns { installPath }', async () => {
let resolveInstallSource;
installSource.mockImplementationOnce(
() =>
new Promise(resolve => {
resolveInstallSource = () => {
resolve({ installPath: 'foo' });
};
})
);

const cluster = new Cluster(log);
const promise = cluster.installSource();
await ensureNoResolve(promise);
resolveInstallSource();
await expect(ensureResolve(promise)).resolves.toEqual({
installPath: 'foo',
});
});

it('passes through all options+log to installSource()', async () => {
installSource.mockResolvedValue({});
const cluster = new Cluster(log);
await cluster.installSource({ foo: 'bar' });
expect(installSource).toHaveBeenCalledTimes(1);
expect(installSource).toHaveBeenCalledWith({
log,
foo: 'bar',
});
});

it('rejects if installSource() rejects', async () => {
installSource.mockRejectedValue(new Error('foo'));
const cluster = new Cluster(log);
await expect(cluster.installSource()).rejects.toThrowError('foo');
});
});

describe('#installSnapshot()', () => {
it('awaits installSnapshot() promise and returns { installPath }', async () => {
let resolveInstallSnapshot;
installSnapshot.mockImplementationOnce(
() =>
new Promise(resolve => {
resolveInstallSnapshot = () => {
resolve({ installPath: 'foo' });
};
})
);

const cluster = new Cluster(log);
const promise = cluster.installSnapshot();
await ensureNoResolve(promise);
resolveInstallSnapshot();
await expect(ensureResolve(promise)).resolves.toEqual({
installPath: 'foo',
});
});

it('passes through all options+log to installSnapshot()', async () => {
installSnapshot.mockResolvedValue({});
const cluster = new Cluster(log);
await cluster.installSnapshot({ foo: 'bar' });
expect(installSnapshot).toHaveBeenCalledTimes(1);
expect(installSnapshot).toHaveBeenCalledWith({
log,
foo: 'bar',
});
});

it('rejects if installSnapshot() rejects', async () => {
installSnapshot.mockRejectedValue(new Error('foo'));
const cluster = new Cluster(log);
await expect(cluster.installSnapshot()).rejects.toThrowError('foo');
});
});

describe('#installArchive(path)', () => {
it('awaits installArchive() promise and returns { installPath }', async () => {
let resolveInstallArchive;
installArchive.mockImplementationOnce(
() =>
new Promise(resolve => {
resolveInstallArchive = () => {
resolve({ installPath: 'foo' });
};
})
);

const cluster = new Cluster(log);
const promise = cluster.installArchive();
await ensureNoResolve(promise);
resolveInstallArchive();
await expect(ensureResolve(promise)).resolves.toEqual({
installPath: 'foo',
});
});

it('passes through path and all options+log to installArchive()', async () => {
installArchive.mockResolvedValue({});
const cluster = new Cluster(log);
await cluster.installArchive('path', { foo: 'bar' });
expect(installArchive).toHaveBeenCalledTimes(1);
expect(installArchive).toHaveBeenCalledWith('path', {
log,
foo: 'bar',
});
});

it('rejects if installArchive() rejects', async () => {
installArchive.mockRejectedValue(new Error('foo'));
const cluster = new Cluster(log);
await expect(cluster.installArchive()).rejects.toThrowError('foo');
});
});

describe('#start(installPath)', () => {
it('rejects when bin/elasticsearch exists with 0 before starting', async () => {
mockEsBin({ exitCode: 0, start: false });

await expect(new Cluster(log).start()).rejects.toThrowError(
'ES exited without starting'
);
});

it('rejects when bin/elasticsearch exists with 143 before starting', async () => {
mockEsBin({ exitCode: 143, start: false });

await expect(new Cluster(log).start()).rejects.toThrowError(
'ES exited without starting'
);
});

it('rejects when bin/elasticsearch exists with 130 before starting', async () => {
mockEsBin({ exitCode: 130, start: false });

await expect(new Cluster(log).start()).rejects.toThrowError(
'ES exited without starting'
);
});

it('rejects when bin/elasticsearch exists with 1 before starting', async () => {
mockEsBin({ exitCode: 1, start: false });

await expect(new Cluster(log).start()).rejects.toThrowError(
'ES exited with code 1'
);
});

it('resolves when bin/elasticsearch logs "started"', async () => {
mockEsBin({ start: true });

await new Cluster(log).start();
});

it('rejects if #start() was called previously', async () => {
mockEsBin({ start: true });

const cluster = new Cluster(log);
await cluster.start();
await expect(cluster.start()).rejects.toThrowError(
'ES has already been started'
);
});

it('rejects if #run() was called previously', async () => {
mockEsBin({ start: true });

const cluster = new Cluster(log);
await cluster.run();
await expect(cluster.start()).rejects.toThrowError(
'ES has already been started'
);
});
});

describe('#run()', () => {
it('resolves when bin/elasticsearch exists with 0', async () => {
mockEsBin({ exitCode: 0 });

await new Cluster(log).run();
});

it('resolves when bin/elasticsearch exists with 143', async () => {
mockEsBin({ exitCode: 143 });

await new Cluster(log).run();
});

it('resolves when bin/elasticsearch exists with 130', async () => {
mockEsBin({ exitCode: 130 });

await new Cluster(log).run();
});

it('rejects when bin/elasticsearch exists with 1', async () => {
mockEsBin({ exitCode: 1 });

await expect(new Cluster(log).run()).rejects.toThrowError(
'ES exited with code 1'
);
});

it('rejects if #start() was called previously', async () => {
mockEsBin({ exitCode: 0, start: true });

const cluster = new Cluster(log);
await cluster.start();
await expect(cluster.run()).rejects.toThrowError(
'ES has already been started'
);
});

it('rejects if #run() was called previously', async () => {
mockEsBin({ exitCode: 0 });

const cluster = new Cluster(log);
await cluster.run();
await expect(cluster.run()).rejects.toThrowError(
'ES has already been started'
);
});
});

describe('#stop()', () => {
it('rejects if #run() or #start() was not called', async () => {
const cluster = new Cluster(log);
await expect(cluster.stop()).rejects.toThrowError(
'ES has not been started'
);
});

it('resolves when ES exits with 0', async () => {
mockEsBin({ exitCode: 0, start: true });

const cluster = new Cluster(log);
await cluster.start();
await cluster.stop();
});

it('resolves when ES exits with 143', async () => {
mockEsBin({ exitCode: 143, start: true });

const cluster = new Cluster(log);
await cluster.start();
await cluster.stop();
});

it('resolves when ES exits with 130', async () => {
mockEsBin({ exitCode: 130, start: true });

const cluster = new Cluster(log);
await cluster.start();
await cluster.stop();
});

it('rejects when ES exits with 1', async () => {
mockEsBin({ exitCode: 1, start: true });

const cluster = new Cluster(log);
await expect(cluster.run()).rejects.toThrowError('ES exited with code 1');
await expect(cluster.stop()).rejects.toThrowError('ES exited with code 1');
});
});

0 comments on commit 6a7c047

Please sign in to comment.