diff --git a/packages/container/libraries/core/src/metadata/actions/updateMetadataName.spec.ts b/packages/container/libraries/core/src/metadata/actions/updateMetadataName.spec.ts new file mode 100644 index 00000000..b5454750 --- /dev/null +++ b/packages/container/libraries/core/src/metadata/actions/updateMetadataName.spec.ts @@ -0,0 +1,92 @@ +import { beforeAll, describe, expect, it } from '@jest/globals'; + +import { InversifyCoreError } from '../../error/models/InversifyCoreError'; +import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind'; +import { ClassElementMetadataKind } from '../models/ClassElementMetadataKind'; +import { ManagedClassElementMetadata } from '../models/ManagedClassElementMetadata'; +import { MaybeManagedClassElementMetadata } from '../models/MaybeManagedClassElementMetadata'; +import { MetadataName } from '../models/MetadataName'; +import { updateMetadataName } from './updateMetadataName'; + +describe(updateMetadataName.name, () => { + describe('having metadata with no name', () => { + let metadataFixture: + | ManagedClassElementMetadata + | MaybeManagedClassElementMetadata; + let nameFixture: MetadataName; + + beforeAll(() => { + metadataFixture = { + kind: ClassElementMetadataKind.singleInjection, + name: undefined, + optional: false, + tags: new Map(), + targetName: undefined, + value: 'service-id', + }; + nameFixture = 'name-fixture'; + }); + + describe('when called', () => { + let result: unknown; + + beforeAll(() => { + result = updateMetadataName(nameFixture)(metadataFixture); + }); + + it('should return metadata', () => { + const expected: + | ManagedClassElementMetadata + | MaybeManagedClassElementMetadata = { + ...metadataFixture, + name: nameFixture, + }; + + expect(result).toStrictEqual(expected); + }); + }); + }); + + describe('having metadata with name', () => { + let metadataFixture: + | ManagedClassElementMetadata + | MaybeManagedClassElementMetadata; + let nameFixture: MetadataName; + + beforeAll(() => { + metadataFixture = { + kind: ClassElementMetadataKind.singleInjection, + name: 'name-fixture', + optional: false, + tags: new Map(), + targetName: undefined, + value: 'service-id', + }; + nameFixture = 'name-fixture'; + }); + + describe('when called', () => { + let result: unknown; + + beforeAll(() => { + try { + updateMetadataName(nameFixture)(metadataFixture); + } catch (error: unknown) { + result = error; + } + }); + + it('should throw InversifyCoreError', () => { + const expectedErrorProperties: Partial = { + kind: InversifyCoreErrorKind.injectionDecoratorConflict, + message: 'Unexpected duplicated named decorator', + }; + + expect(result).toBeInstanceOf(InversifyCoreError); + expect(result).toStrictEqual( + expect.objectContaining(expectedErrorProperties), + ); + }); + }); + }); +}); diff --git a/packages/container/libraries/core/src/metadata/actions/updateMetadataName.ts b/packages/container/libraries/core/src/metadata/actions/updateMetadataName.ts new file mode 100644 index 00000000..6eb87648 --- /dev/null +++ b/packages/container/libraries/core/src/metadata/actions/updateMetadataName.ts @@ -0,0 +1,26 @@ +import { InversifyCoreError } from '../../error/models/InversifyCoreError'; +import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind'; +import { ManagedClassElementMetadata } from '../models/ManagedClassElementMetadata'; +import { MaybeManagedClassElementMetadata } from '../models/MaybeManagedClassElementMetadata'; +import { MetadataName } from '../models/MetadataName'; + +export function updateMetadataName( + name: MetadataName, +): ( + metadata: ManagedClassElementMetadata | MaybeManagedClassElementMetadata, +) => ManagedClassElementMetadata | MaybeManagedClassElementMetadata { + return ( + metadata: ManagedClassElementMetadata | MaybeManagedClassElementMetadata, + ): ManagedClassElementMetadata | MaybeManagedClassElementMetadata => { + if (metadata.name !== undefined) { + throw new InversifyCoreError( + InversifyCoreErrorKind.injectionDecoratorConflict, + 'Unexpected duplicated named decorator', + ); + } + + metadata.name = name; + + return metadata; + }; +} diff --git a/packages/container/libraries/core/src/metadata/calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata.spec.ts b/packages/container/libraries/core/src/metadata/calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata.spec.ts index d7850b80..7af393e9 100644 --- a/packages/container/libraries/core/src/metadata/calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata.spec.ts +++ b/packages/container/libraries/core/src/metadata/calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata.spec.ts @@ -1,11 +1,10 @@ -import { beforeAll, describe, expect, it } from '@jest/globals'; +import { beforeAll, describe, expect, it, jest } from '@jest/globals'; import { InversifyCoreError } from '../../error/models/InversifyCoreError'; import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind'; import { ClassElementMetadataKind } from '../models/ClassElementMetadataKind'; import { ManagedClassElementMetadata } from '../models/ManagedClassElementMetadata'; import { MaybeManagedClassElementMetadata } from '../models/MaybeManagedClassElementMetadata'; -import { MetadataTag } from '../models/MetadataTag'; import { UnmanagedClassElementMetadata } from '../models/UnmanagedClassElementMetadata'; import { buildMaybeClassElementMetadataFromMaybeClassElementMetadata } from './buildMaybeClassElementMetadataFromMaybeClassElementMetadata'; @@ -13,14 +12,20 @@ describe( buildMaybeClassElementMetadataFromMaybeClassElementMetadata.name, () => { describe('having unmanaged metadata', () => { - let metadataPartialFixture: Partial; let metadataFixture: UnmanagedClassElementMetadata; + let updateMetadataMock: jest.Mock< + ( + metadata: + | ManagedClassElementMetadata + | MaybeManagedClassElementMetadata, + ) => ManagedClassElementMetadata | MaybeManagedClassElementMetadata + >; beforeAll(() => { - metadataPartialFixture = {}; metadataFixture = { kind: ClassElementMetadataKind.unmanaged, }; + updateMetadataMock = jest.fn(); }); describe('when called', () => { @@ -29,7 +34,7 @@ describe( beforeAll(() => { try { buildMaybeClassElementMetadataFromMaybeClassElementMetadata( - metadataPartialFixture, + updateMetadataMock, )(metadataFixture); } catch (error: unknown) { result = error; @@ -52,58 +57,16 @@ describe( }); describe('having non unmanaged metadata', () => { - let metadataPartialFixture: Partial; let metadataFixture: ManagedClassElementMetadata; - - beforeAll(() => { - metadataPartialFixture = { - name: 'name-fixture', - optional: true, - targetName: 'target-name-fixture', - }; - metadataFixture = { - kind: ClassElementMetadataKind.singleInjection, - name: undefined, - optional: false, - tags: new Map([['foo', 'bar']]), - targetName: undefined, - value: 'service-identifier', - }; - }); - - describe('when called', () => { - let result: unknown; - - beforeAll(() => { - result = buildMaybeClassElementMetadataFromMaybeClassElementMetadata( - metadataPartialFixture, - )(metadataFixture); - }); - - it('should return ManagedClassElementMetadata', () => { - const expected: + let updateMetadataMock: jest.Mock< + ( + metadata: | ManagedClassElementMetadata - | MaybeManagedClassElementMetadata = { - ...metadataFixture, - ...metadataPartialFixture, - }; - - expect(result).toStrictEqual(expected); - }); - }); - }); - - describe('having non unmanaged metadata and partial metadata with tags', () => { - let metadataPartialFixture: Partial; - let metadataFixture: ManagedClassElementMetadata; + | MaybeManagedClassElementMetadata, + ) => ManagedClassElementMetadata | MaybeManagedClassElementMetadata + >; beforeAll(() => { - metadataPartialFixture = { - name: 'name-fixture', - optional: true, - tags: new Map([['bar', 'baz']]), - targetName: 'target-name-fixture', - }; metadataFixture = { kind: ClassElementMetadataKind.singleInjection, name: undefined, @@ -112,30 +75,23 @@ describe( targetName: undefined, value: 'service-identifier', }; + updateMetadataMock = jest.fn(); }); describe('when called', () => { let result: unknown; beforeAll(() => { - result = buildMaybeClassElementMetadataFromMaybeClassElementMetadata( - metadataPartialFixture, - )(metadataFixture); + updateMetadataMock.mockReturnValueOnce(metadataFixture); + + result = + buildMaybeClassElementMetadataFromMaybeClassElementMetadata( + updateMetadataMock, + )(metadataFixture); }); it('should return ManagedClassElementMetadata', () => { - const expected: - | ManagedClassElementMetadata - | MaybeManagedClassElementMetadata = { - ...metadataFixture, - ...metadataPartialFixture, - tags: new Map([ - ...metadataFixture.tags, - ...(metadataPartialFixture.tags as Map), - ]), - }; - - expect(result).toStrictEqual(expected); + expect(result).toBe(metadataFixture); }); }); }); diff --git a/packages/container/libraries/core/src/metadata/calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata.ts b/packages/container/libraries/core/src/metadata/calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata.ts index 51908fea..64be565b 100644 --- a/packages/container/libraries/core/src/metadata/calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata.ts +++ b/packages/container/libraries/core/src/metadata/calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata.ts @@ -7,7 +7,9 @@ import { MaybeManagedClassElementMetadata } from '../models/MaybeManagedClassEle import { buildDefaultMaybeClassElementMetadata } from './buildDefaultMaybeClassElementMetadata'; export function buildMaybeClassElementMetadataFromMaybeClassElementMetadata( - metadataPartial: Partial, + updateMetadata: ( + metadata: ManagedClassElementMetadata | MaybeManagedClassElementMetadata, + ) => ManagedClassElementMetadata | MaybeManagedClassElementMetadata, ): ( metadata: MaybeClassElementMetadata | undefined, ) => ManagedClassElementMetadata | MaybeManagedClassElementMetadata { @@ -24,25 +26,7 @@ export function buildMaybeClassElementMetadataFromMaybeClassElementMetadata( 'Unexpected injection found. Found @unmanaged injection with additional @named, @optional, @tagged or @targetName injections', ); default: - return buildMergedMetadata(definedMetadata, metadataPartial); + return updateMetadata(definedMetadata); } }; } - -function buildMergedMetadata( - metadata: ManagedClassElementMetadata | MaybeManagedClassElementMetadata, - metadataPartial: Partial, -): ManagedClassElementMetadata | MaybeManagedClassElementMetadata { - const mergedMetadata: - | ManagedClassElementMetadata - | MaybeManagedClassElementMetadata = { - ...metadata, - ...metadataPartial, - }; - - if (metadataPartial.tags !== undefined) { - mergedMetadata.tags = new Map([...metadata.tags, ...metadataPartial.tags]); - } - - return mergedMetadata; -} diff --git a/packages/container/libraries/core/src/metadata/decorators/named.spec.ts b/packages/container/libraries/core/src/metadata/decorators/named.spec.ts index b59ae056..aaf0a7cf 100644 --- a/packages/container/libraries/core/src/metadata/decorators/named.spec.ts +++ b/packages/container/libraries/core/src/metadata/decorators/named.spec.ts @@ -1,11 +1,13 @@ import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals'; +jest.mock('../actions/updateMetadataName'); jest.mock( '../calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata', ); jest.mock('../calculations/handleInjectionError'); jest.mock('./injectBase'); +import { updateMetadataName } from '../actions/updateMetadataName'; import { buildMaybeClassElementMetadataFromMaybeClassElementMetadata } from '../calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata'; import { handleInjectionError } from '../calculations/handleInjectionError'; import { ManagedClassElementMetadata } from '../models/ManagedClassElementMetadata'; @@ -15,6 +17,20 @@ import { injectBase } from './injectBase'; import { named } from './named'; describe(named.name, () => { + let updateMetadataNameResultMock: jest.Mock< + ( + metadata: ManagedClassElementMetadata | MaybeManagedClassElementMetadata, + ) => ManagedClassElementMetadata | MaybeManagedClassElementMetadata + >; + + beforeAll(() => { + updateMetadataNameResultMock = jest.fn(); + + ( + updateMetadataName as jest.Mock + ).mockReturnValue(updateMetadataNameResultMock); + }); + describe('having a non undefined propertyKey and an undefined parameterIndex', () => { let targetFixture: object; let propertyKeyFixture: string | symbol; @@ -73,7 +89,7 @@ describe(named.name, () => { ).toHaveBeenCalledTimes(1); expect( buildMaybeClassElementMetadataFromMaybeClassElementMetadata, - ).toHaveBeenCalledWith({ name: nameFixture }); + ).toHaveBeenCalledWith(expect.any(Function)); }); it('should call injectBase()', () => { @@ -152,7 +168,7 @@ describe(named.name, () => { ).toHaveBeenCalledTimes(1); expect( buildMaybeClassElementMetadataFromMaybeClassElementMetadata, - ).toHaveBeenCalledWith({ name: nameFixture }); + ).toHaveBeenCalledWith(expect.any(Function)); }); it('should call injectBase()', () => { @@ -238,7 +254,7 @@ describe(named.name, () => { ).toHaveBeenCalledTimes(1); expect( buildMaybeClassElementMetadataFromMaybeClassElementMetadata, - ).toHaveBeenCalledWith({ name: nameFixture }); + ).toHaveBeenCalledWith(expect.any(Function)); }); it('should call injectBase()', () => { @@ -318,7 +334,7 @@ describe(named.name, () => { ).toHaveBeenCalledTimes(1); expect( buildMaybeClassElementMetadataFromMaybeClassElementMetadata, - ).toHaveBeenCalledWith({ name: nameFixture }); + ).toHaveBeenCalledWith(expect.any(Function)); }); it('should call injectBase()', () => { diff --git a/packages/container/libraries/core/src/metadata/decorators/named.ts b/packages/container/libraries/core/src/metadata/decorators/named.ts index 7980de2a..9d5cb38e 100644 --- a/packages/container/libraries/core/src/metadata/decorators/named.ts +++ b/packages/container/libraries/core/src/metadata/decorators/named.ts @@ -1,3 +1,4 @@ +import { updateMetadataName } from '../actions/updateMetadataName'; import { buildMaybeClassElementMetadataFromMaybeClassElementMetadata } from '../calculations/buildMaybeClassElementMetadataFromMaybeClassElementMetadata'; import { handleInjectionError } from '../calculations/handleInjectionError'; import { ManagedClassElementMetadata } from '../models/ManagedClassElementMetadata'; @@ -17,9 +18,9 @@ export function named( const updateMetadata: ( metadata: MaybeClassElementMetadata | undefined, ) => ManagedClassElementMetadata | MaybeManagedClassElementMetadata = - buildMaybeClassElementMetadataFromMaybeClassElementMetadata({ - name, - }); + buildMaybeClassElementMetadataFromMaybeClassElementMetadata( + updateMetadataName(name), + ); try { if (parameterIndex === undefined) {