Skip to content

Commit

Permalink
feat: Introduce error monitoring for all injectables that return a fu…
Browse files Browse the repository at this point in the history
…nction
  • Loading branch information
Iku-turso committed Feb 10, 2022
1 parent b07dccb commit 6f458f2
Show file tree
Hide file tree
Showing 2 changed files with 269 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
import asyncFn from '@async-fn/jest';
import getDi from '../test-utils/getDiForUnitTesting';
import getInjectable from '../getInjectable/getInjectable';
import lifecycleEnum from './lifecycleEnum';
import { errorMonitorInjectionToken } from './createContainer';
import { first, noop } from 'lodash/fp';
import { pipeline } from '@ogre-tools/fp';

describe('createContainer.error-monitoring-for-injected-functions', () => {
[
{ name: 'given injecting single', inject: di => di.inject },

{
name: 'given injecting many',

inject:
di =>
(...args) =>
pipeline(di.injectMany(...args), first),
},
].forEach(scenario => {
describe(scenario.name, () => {
describe('given an error monitor, sync child-injectable and injected, when invocation of child throws', () => {
let errorMonitorMock;
let thrownErrorMock;

beforeEach(() => {
errorMonitorMock = jest.fn();
const errorMonitorInjectable = getInjectable({
id: 'some-error-monitor',
injectionToken: errorMonitorInjectionToken,
instantiate: () => errorMonitorMock,
});

const syncChildInjectable = getInjectable({
id: 'some-child-injectable',

lifecycle: lifecycleEnum.transient,

instantiate: () => () => {
throw 'some-error';
},
});

const parentInjectable = getInjectable({
id: 'some-parent-injectable',

lifecycle: lifecycleEnum.transient,

instantiate: di =>
scenario.inject(di)(
syncChildInjectable,
'some-instantiation-parameter-for-child',
),
});

const di = getDi(
parentInjectable,
syncChildInjectable,
errorMonitorInjectable,
);

thrownErrorMock = jest.fn();

const throwError = scenario.inject(di)(
parentInjectable,
'some-instantiation-parameter-for-parent',
);

try {
throwError();
} catch (error) {
thrownErrorMock(error);
}
});

it('triggers error monitoring only for child', () => {
expect(errorMonitorMock.mock.calls).toEqual([
[
{
error: 'some-error',

context: [
{
id: 'some-parent-injectable',
instantiationParameter:
'some-instantiation-parameter-for-parent',
},

{
id: 'some-child-injectable',
instantiationParameter:
'some-instantiation-parameter-for-child',
},
],
},
],
]);
});

it('throws', () => {
expect(thrownErrorMock).toHaveBeenCalledWith('some-error');
});
});

describe('given an error monitor, async child-injectable, when injected and called', () => {
let errorMonitorMock;
let actualPromise;
let childCallMock;
let parentInjectable;
let di;

beforeEach(async () => {
errorMonitorMock = asyncFn();
const errorMonitorInjectable = getInjectable({
id: 'some-error-monitor',
injectionToken: errorMonitorInjectionToken,
instantiate: () => errorMonitorMock,
});

childCallMock = asyncFn();
const childInjectable = getInjectable({
id: 'some-child-injectable',
lifecycle: lifecycleEnum.transient,
instantiate: () => childCallMock,
});

parentInjectable = getInjectable({
id: 'some-parent-injectable',
lifecycle: lifecycleEnum.transient,

instantiate: di =>
scenario.inject(di)(
childInjectable,
'some-instantiation-parameter-for-child',
),
});

di = getDi(parentInjectable, childInjectable, errorMonitorInjectable);

const instance = scenario.inject(di)(
parentInjectable,
'some-instantiation-parameter-for-parent',
);

actualPromise = instance();
});

describe('when call of child rejects with error', () => {
beforeEach(() => {
childCallMock.reject(new Error('some-error'));
});

it('triggers error monitoring only for child', async () => {
await actualPromise.catch(noop);

expect(errorMonitorMock.mock.calls).toEqual([
[
{
error: expect.any(Error),

context: [
{
id: 'some-parent-injectable',
instantiationParameter:
'some-instantiation-parameter-for-parent',
},

{
id: 'some-child-injectable',
instantiationParameter:
'some-instantiation-parameter-for-child',
},
],
},
],
]);
});

it('throws', () => {
return expect(actualPromise).rejects.toThrow('some-error');
});
});

describe('when call of child rejects with non-error', () => {
beforeEach(() => {
childCallMock.reject('some-non-error-rejection');
});

it('triggers error monitoring only for child', async () => {
await actualPromise.catch(noop);

expect(errorMonitorMock.mock.calls).toEqual([
[
{
error: 'some-non-error-rejection',

context: [
{
id: 'some-parent-injectable',
instantiationParameter:
'some-instantiation-parameter-for-parent',
},

{
id: 'some-child-injectable',
instantiationParameter:
'some-instantiation-parameter-for-child',
},
],
},
],
]);
});

it('rejects as the non-error', () => {
return expect(actualPromise).rejects.toBe(
'some-non-error-rejection',
);
});

it('when same exact non-error rejection occurs again, triggers error monitoring again', async () => {
await actualPromise.catch(noop);

errorMonitorMock.mockClear();

const instance = scenario.inject(di)(
parentInjectable,
'some-instantiation-parameter-for-parent',
);

actualPromise = instance();

childCallMock.reject('some-non-error-rejection');

await actualPromise.catch(noop);

expect(errorMonitorMock.mock.calls).toEqual([
[
{
error: 'some-non-error-rejection',

context: [
{
id: 'some-parent-injectable',
instantiationParameter:
'some-instantiation-parameter-for-parent',
},

{
id: 'some-child-injectable',
instantiationParameter:
'some-instantiation-parameter-for-child',
},
],
},
],
]);
});
});
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,15 @@ const getInstance = ({
withErrorMonitoring,
);

const newInstance = instantiateWithErrorMonitoring(
let newInstance = instantiateWithErrorMonitoring(
minimalDi,
...(isUndefined(instantiationParameter) ? [] : [instantiationParameter]),
);

if (isFunction(newInstance)) {
newInstance = pipeline(newInstance, withErrorMonitoring);
}

if (instanceKey !== nonStoredInstanceKey) {
instanceMap.set(instanceKey, newInstance);
}
Expand Down

0 comments on commit 6f458f2

Please sign in to comment.