Skip to content

Commit

Permalink
Merge branch 'main' into renovate/auto-merge-on-patch-or-minor
Browse files Browse the repository at this point in the history
  • Loading branch information
notaphplover authored Nov 26, 2024
2 parents 3949aab + 128c19f commit cc69b4c
Show file tree
Hide file tree
Showing 11 changed files with 1,042 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export enum InversifyCoreErrorKind {
injectionDecoratorConflict,
missingInjectionDecorator,
planning,
unknown,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { beforeAll, describe, expect, it, jest } from '@jest/globals';

jest.mock('@inversifyjs/common');

import {
ServiceIdentifier,
stringifyServiceIdentifier,
} from '@inversifyjs/common';

import { InversifyCoreError } from '../../error/models/InversifyCoreError';
import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind';
import { BasePlanParams } from '../models/BasePlanParams';
import { addBranchService } from './addBranchService';

describe(addBranchService.name, () => {
describe('having BasePlanParams with empty servicesBranch', () => {
let basePlanParamsFixture: BasePlanParams;
let serviceIdentifierFixture: ServiceIdentifier;

beforeAll(() => {
basePlanParamsFixture = {
servicesBranch: new Set(),
} as Partial<BasePlanParams> as BasePlanParams;
serviceIdentifierFixture = 'service-id';
});

describe('when called', () => {
let result: unknown;

beforeAll(() => {
result = addBranchService(
basePlanParamsFixture,
serviceIdentifierFixture,
);
});

it('should add seriveIdentifier to basePlanParams.serviceBranch', () => {
expect(basePlanParamsFixture.servicesBranch).toContain(
serviceIdentifierFixture,
);
});

it('should return undefined', () => {
expect(result).toBeUndefined();
});
});
});

describe('having BasePlanParams with servicesBranch with serviceIdentifier', () => {
let basePlanParamsFixture: BasePlanParams;
let serviceIdentifierFixture: ServiceIdentifier;

beforeAll(() => {
serviceIdentifierFixture = 'service-id';
basePlanParamsFixture = {
servicesBranch: new Set([serviceIdentifierFixture]),
} as Partial<BasePlanParams> as BasePlanParams;
});

describe('when called', () => {
let stringifiedServiceIdentifier: string;

let result: unknown;

beforeAll(() => {
stringifiedServiceIdentifier = 'stringified-service-id';

(
stringifyServiceIdentifier as jest.Mock<
typeof stringifyServiceIdentifier
>
)
.mockReturnValueOnce(stringifiedServiceIdentifier)
.mockReturnValueOnce(stringifiedServiceIdentifier);

try {
addBranchService(basePlanParamsFixture, serviceIdentifierFixture);
} catch (error: unknown) {
result = error;
}
});

it('should throw an InversifyCoreError', () => {
const expected: Partial<InversifyCoreError> = {
kind: InversifyCoreErrorKind.planning,
message: `Circular dependency found: ${stringifiedServiceIdentifier} -> ${stringifiedServiceIdentifier}`,
};

expect(result).toBeInstanceOf(InversifyCoreError);
expect(result).toStrictEqual(expect.objectContaining(expected));
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
ServiceIdentifier,
stringifyServiceIdentifier,
} from '@inversifyjs/common';

import { InversifyCoreError } from '../../error/models/InversifyCoreError';
import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind';
import { BasePlanParams } from '../models/BasePlanParams';

export function addBranchService(
params: BasePlanParams,
serviceIdentifier: ServiceIdentifier,
): void {
if (params.servicesBranch.has(serviceIdentifier)) {
throwError(params, serviceIdentifier);
}

params.servicesBranch.add(serviceIdentifier);
}

function stringifyServiceIdentifierTrace(
serviceIdentifiers: Iterable<ServiceIdentifier>,
): string {
return [...serviceIdentifiers].map(stringifyServiceIdentifier).join(' -> ');
}

function throwError(
params: BasePlanParams,
serviceIdentifier: ServiceIdentifier,
): never {
const stringifiedCircularDependencies: string =
stringifyServiceIdentifierTrace([
...params.servicesBranch,
serviceIdentifier,
]);

throw new InversifyCoreError(
InversifyCoreErrorKind.planning,
`Circular dependency found: ${stringifiedCircularDependencies}`,
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals';

jest.mock('./isPlanServiceRedirectionBindingNode');
jest.mock('./throwErrorWhenUnexpectedBindingsAmountFound');

import { bindingScopeValues } from '../../binding/models/BindingScope';
import { bindingTypeValues } from '../../binding/models/BindingType';
import { ServiceRedirectionBinding } from '../../binding/models/ServiceRedirectionBinding';
import { BindingNodeParent } from '../models/BindingNodeParent';
import { PlanServiceRedirectionBindingNode } from '../models/PlanServiceRedirectionBindingNode';
import { checkPlanServiceRedirectionBindingNodeSingleInjectionBindings } from './checkPlanServiceRedirectionBindingNodeSingleInjectionBindings';
import { isPlanServiceRedirectionBindingNode } from './isPlanServiceRedirectionBindingNode';
import { throwErrorWhenUnexpectedBindingsAmountFound } from './throwErrorWhenUnexpectedBindingsAmountFound';

describe(
checkPlanServiceRedirectionBindingNodeSingleInjectionBindings.name,
() => {
describe('having a PlanServiceRedirectionBindingNode with no redirections', () => {
let planServiceRedirectionBindingNodeFixture: PlanServiceRedirectionBindingNode;
let isOptionalFixture: boolean;

beforeAll(() => {
planServiceRedirectionBindingNodeFixture = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
binding: Symbol() as unknown as ServiceRedirectionBinding<any>,
parent: Symbol() as unknown as BindingNodeParent,
redirections: [],
};
isOptionalFixture = false;
});

describe('when called', () => {
let result: unknown;

beforeAll(() => {
result =
checkPlanServiceRedirectionBindingNodeSingleInjectionBindings(
planServiceRedirectionBindingNodeFixture,
isOptionalFixture,
);
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call throwErrorWhenUnexpectedBindingsAmountFound()', () => {
expect(
throwErrorWhenUnexpectedBindingsAmountFound,
).toHaveBeenCalledTimes(1);
expect(
throwErrorWhenUnexpectedBindingsAmountFound,
).toHaveBeenCalledWith(
planServiceRedirectionBindingNodeFixture.redirections,
isOptionalFixture,
planServiceRedirectionBindingNodeFixture,
);
});

it('should return undefined', () => {
expect(result).toBeUndefined();
});
});
});

describe('having a PlanServiceRedirectionBindingNode with a single redirection to a leaf node', () => {
let planServiceRedirectionBindingNodeFixture: PlanServiceRedirectionBindingNode;
let isOptionalFixture: boolean;

beforeAll(() => {
planServiceRedirectionBindingNodeFixture = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
binding: Symbol() as unknown as ServiceRedirectionBinding<any>,
parent: Symbol() as unknown as BindingNodeParent,
redirections: [
{
binding: {
cache: {
isRight: true,
value: Symbol(),
},
id: 1,
isSatisfiedBy: () => true,
moduleId: undefined,
onActivation: {
isRight: false,
value: undefined,
},
onDeactivation: {
isRight: false,
value: undefined,
},
scope: bindingScopeValues.Singleton,
serviceIdentifier: 'service-id',
type: bindingTypeValues.ConstantValue,
},
parent: Symbol() as unknown as BindingNodeParent,
},
],
};
isOptionalFixture = false;
});

describe('when called, and isPlanServiceRedirectionBindingNode() returns false', () => {
let result: unknown;

beforeAll(() => {
(
isPlanServiceRedirectionBindingNode as unknown as jest.Mock<
typeof isPlanServiceRedirectionBindingNode
>
).mockReturnValueOnce(false);

result =
checkPlanServiceRedirectionBindingNodeSingleInjectionBindings(
planServiceRedirectionBindingNodeFixture,
isOptionalFixture,
);
});

afterAll(() => {
jest.clearAllMocks();
});

it('should not call throwErrorWhenUnexpectedBindingsAmountFound()', () => {
expect(
throwErrorWhenUnexpectedBindingsAmountFound,
).not.toHaveBeenCalled();
});

it('should return undefined', () => {
expect(result).toBeUndefined();
});
});
});

describe('having a PlanServiceRedirectionBindingNode with a single redirection to a PlanServiceRedirectionBindingNode with no redirections', () => {
let planServiceRedirectionBindingNodeRedirectionFixture: PlanServiceRedirectionBindingNode;
let planServiceRedirectionBindingNodeFixture: PlanServiceRedirectionBindingNode;
let isOptionalFixture: boolean;

beforeAll(() => {
planServiceRedirectionBindingNodeRedirectionFixture = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
binding: Symbol() as unknown as ServiceRedirectionBinding<any>,
parent: Symbol() as unknown as BindingNodeParent,
redirections: [],
};
planServiceRedirectionBindingNodeFixture = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
binding: Symbol() as unknown as ServiceRedirectionBinding<any>,
parent: Symbol() as unknown as BindingNodeParent,
redirections: [planServiceRedirectionBindingNodeRedirectionFixture],
};
isOptionalFixture = false;
});

describe('when called, and isPlanServiceRedirectionBindingNode() returns true', () => {
let result: unknown;

beforeAll(() => {
(
isPlanServiceRedirectionBindingNode as unknown as jest.Mock<
typeof isPlanServiceRedirectionBindingNode
>
).mockReturnValueOnce(true);

result =
checkPlanServiceRedirectionBindingNodeSingleInjectionBindings(
planServiceRedirectionBindingNodeFixture,
isOptionalFixture,
);
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call throwErrorWhenUnexpectedBindingsAmountFound()', () => {
expect(
throwErrorWhenUnexpectedBindingsAmountFound,
).toHaveBeenCalledTimes(1);
expect(
throwErrorWhenUnexpectedBindingsAmountFound,
).toHaveBeenCalledWith(
planServiceRedirectionBindingNodeRedirectionFixture.redirections,
isOptionalFixture,
planServiceRedirectionBindingNodeRedirectionFixture,
);
});

it('should return undefined', () => {
expect(result).toBeUndefined();
});
});
});
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { PlanBindingNode } from '../models/PlanBindingNode';
import { PlanServiceRedirectionBindingNode } from '../models/PlanServiceRedirectionBindingNode';
import { isPlanServiceRedirectionBindingNode } from './isPlanServiceRedirectionBindingNode';
import { throwErrorWhenUnexpectedBindingsAmountFound } from './throwErrorWhenUnexpectedBindingsAmountFound';

const SINGLE_INJECTION_BINDINGS: number = 1;

export function checkPlanServiceRedirectionBindingNodeSingleInjectionBindings(
serviceRedirectionBindingNode: PlanServiceRedirectionBindingNode,
isOptional: boolean,
): void {
if (
serviceRedirectionBindingNode.redirections.length ===
SINGLE_INJECTION_BINDINGS
) {
const [planBindingNode]: [PlanBindingNode] =
serviceRedirectionBindingNode.redirections as [PlanBindingNode];

if (isPlanServiceRedirectionBindingNode(planBindingNode)) {
checkPlanServiceRedirectionBindingNodeSingleInjectionBindings(
planBindingNode,
isOptional,
);
}

return;
}

throwErrorWhenUnexpectedBindingsAmountFound(
serviceRedirectionBindingNode.redirections,
isOptional,
serviceRedirectionBindingNode,
);
}
Loading

0 comments on commit cc69b4c

Please sign in to comment.