-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into renovate/auto-merge-on-patch-or-minor
- Loading branch information
Showing
11 changed files
with
1,042 additions
and
0 deletions.
There are no files selected for viewing
1 change: 1 addition & 0 deletions
1
packages/container/libraries/core/src/error/models/InversifyCoreErrorKind.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
export enum InversifyCoreErrorKind { | ||
injectionDecoratorConflict, | ||
missingInjectionDecorator, | ||
planning, | ||
unknown, | ||
} |
94 changes: 94 additions & 0 deletions
94
packages/container/libraries/core/src/planning/actions/addBranchService.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
}); | ||
}); | ||
}); | ||
}); |
41 changes: 41 additions & 0 deletions
41
packages/container/libraries/core/src/planning/actions/addBranchService.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}`, | ||
); | ||
} |
198 changes: 198 additions & 0 deletions
198
...anning/calculations/checkPlanServiceRedirectionBindingNodeSingleInjectionBindings.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
}); | ||
}); | ||
}, | ||
); |
34 changes: 34 additions & 0 deletions
34
...rc/planning/calculations/checkPlanServiceRedirectionBindingNodeSingleInjectionBindings.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
); | ||
} |
Oops, something went wrong.