-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(core): add throwAtInvalidClassMetadata
- Loading branch information
1 parent
2e38c02
commit e28ccc6
Showing
2 changed files
with
188 additions
and
0 deletions.
There are no files selected for viewing
132 changes: 132 additions & 0 deletions
132
...es/container/libraries/core/src/metadata/calculations/throwAtInvalidClassMetadata.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,132 @@ | ||
import { beforeAll, describe, expect, it } from '@jest/globals'; | ||
|
||
import { Newable } from '@inversifyjs/common'; | ||
|
||
import { InversifyCoreError } from '../../error/models/InversifyCoreError'; | ||
import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind'; | ||
import { ClassMetadata } from '../models/ClassMetadata'; | ||
import { MaybeClassElementMetadata } from '../models/MaybeClassElementMetadata'; | ||
import { MaybeClassElementMetadataKind } from '../models/MaybeClassElementMetadataKind'; | ||
import { MaybeClassMetadata } from '../models/MaybeClassMetadata'; | ||
import { throwAtInvalidClassMetadata } from './throwAtInvalidClassMetadata'; | ||
|
||
describe(throwAtInvalidClassMetadata.name, () => { | ||
describe('having valid class metadata', () => { | ||
let typefixture: Newable; | ||
let classMetadataFixure: ClassMetadata; | ||
|
||
beforeAll(() => { | ||
typefixture = class Foo {}; | ||
classMetadataFixure = { | ||
constructorArguments: [], | ||
lifecycle: { | ||
postConstructMethodName: undefined, | ||
preDestroyMethodName: undefined, | ||
}, | ||
properties: new Map(), | ||
}; | ||
}); | ||
|
||
describe('when called', () => { | ||
let result: unknown; | ||
|
||
beforeAll(() => { | ||
try { | ||
throwAtInvalidClassMetadata(typefixture, classMetadataFixure); | ||
} catch (error: unknown) { | ||
result = error; | ||
} | ||
}); | ||
|
||
it('should throw an Error', () => { | ||
const expectedErrorProperties: Partial<InversifyCoreError> = { | ||
kind: InversifyCoreErrorKind.unknown, | ||
message: `Unexpected class metadata for type "${typefixture.name}" with uncompletion traces. | ||
This might be caused by one of the following reasons: | ||
1. A third party library is targeting inversify reflection metadata. | ||
2. A bug is causing the issue. Consider submiting an issue to fix it.`, | ||
}; | ||
|
||
expect(result).toBeInstanceOf(InversifyCoreError); | ||
expect(result).toStrictEqual( | ||
expect.objectContaining(expectedErrorProperties), | ||
); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('having invalid class metadata with both undefined and invalid constructor metadata and invalid property metadata', () => { | ||
let typeFixture: Newable; | ||
let classMetadataFixure: MaybeClassMetadata; | ||
let emptyConstructorIndex: number; | ||
let invalidConstructorIndex: number; | ||
let invalidPropertyName: string | symbol; | ||
|
||
beforeAll(() => { | ||
typeFixture = class Foo {}; | ||
|
||
invalidConstructorIndex = 0; | ||
emptyConstructorIndex = 1; | ||
invalidPropertyName = 'foo'; | ||
|
||
const constructorClassMetadata: MaybeClassElementMetadata[] = []; | ||
|
||
const maybeClassElementMetadata: MaybeClassElementMetadata = { | ||
kind: MaybeClassElementMetadataKind.unknown, | ||
name: undefined, | ||
optional: false, | ||
tags: new Map(), | ||
targetName: undefined, | ||
}; | ||
|
||
constructorClassMetadata[invalidConstructorIndex] = | ||
maybeClassElementMetadata; | ||
|
||
constructorClassMetadata[emptyConstructorIndex] = | ||
undefined as unknown as MaybeClassElementMetadata; | ||
|
||
classMetadataFixure = { | ||
constructorArguments: constructorClassMetadata, | ||
lifecycle: { | ||
postConstructMethodName: undefined, | ||
preDestroyMethodName: undefined, | ||
}, | ||
properties: new Map([[invalidPropertyName, maybeClassElementMetadata]]), | ||
}; | ||
}); | ||
|
||
describe('when called', () => { | ||
let result: unknown; | ||
|
||
beforeAll(() => { | ||
try { | ||
throwAtInvalidClassMetadata(typeFixture, classMetadataFixure); | ||
} catch (error: unknown) { | ||
result = error; | ||
} | ||
}); | ||
|
||
it('should throw an Error', () => { | ||
const expectedErrorProperties: Partial<InversifyCoreError> = { | ||
kind: InversifyCoreErrorKind.missingInjectionDecorator, | ||
message: `Invalid class metadata at type ${typeFixture.name}: | ||
- Missing or incomplete metadata for type "${typeFixture.name}" at constructor argument with index ${invalidConstructorIndex.toString()}. | ||
Every constructor parameter must be decorated either with @inject, @multiInject or @unmanaged decorator. | ||
- Missing or incomplete metadata for type "${typeFixture.name}" at constructor argument with index ${emptyConstructorIndex.toString()}. | ||
Every constructor parameter must be decorated either with @inject, @multiInject or @unmanaged decorator. | ||
- Missing or incomplete metadata for type "${typeFixture.name}" at property "${invalidPropertyName.toString()}". | ||
This property must be decorated either with @inject or @multiInject decorator.`, | ||
}; | ||
|
||
expect(result).toBeInstanceOf(InversifyCoreError); | ||
expect(result).toStrictEqual( | ||
expect.objectContaining(expectedErrorProperties), | ||
); | ||
}); | ||
}); | ||
}); | ||
}); |
56 changes: 56 additions & 0 deletions
56
packages/container/libraries/core/src/metadata/calculations/throwAtInvalidClassMetadata.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,56 @@ | ||
import { Newable } from '@inversifyjs/common'; | ||
|
||
import { InversifyCoreError } from '../../error/models/InversifyCoreError'; | ||
import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind'; | ||
import { MaybeClassElementMetadata } from '../models/MaybeClassElementMetadata'; | ||
import { MaybeClassElementMetadataKind } from '../models/MaybeClassElementMetadataKind'; | ||
import { MaybeClassMetadata } from '../models/MaybeClassMetadata'; | ||
|
||
export function throwAtInvalidClassMetadata( | ||
type: Newable, | ||
classMetadata: MaybeClassMetadata, | ||
): never { | ||
const errors: string[] = []; | ||
|
||
for (let i: number = 0; i < classMetadata.constructorArguments.length; ++i) { | ||
const constructorArgument: MaybeClassElementMetadata | undefined = | ||
classMetadata.constructorArguments[i]; | ||
|
||
if ( | ||
constructorArgument === undefined || | ||
constructorArgument.kind === MaybeClassElementMetadataKind.unknown | ||
) { | ||
errors.push( | ||
` - Missing or incomplete metadata for type "${type.name}" at constructor argument with index ${i.toString()}. | ||
Every constructor parameter must be decorated either with @inject, @multiInject or @unmanaged decorator.`, | ||
); | ||
} | ||
} | ||
|
||
for (const [propertyKey, property] of classMetadata.properties) { | ||
if (property.kind === MaybeClassElementMetadataKind.unknown) { | ||
errors.push( | ||
` - Missing or incomplete metadata for type "${type.name}" at property "${propertyKey.toString()}". | ||
This property must be decorated either with @inject or @multiInject decorator.`, | ||
); | ||
} | ||
} | ||
|
||
if (errors.length === 0) { | ||
throw new InversifyCoreError( | ||
InversifyCoreErrorKind.unknown, | ||
`Unexpected class metadata for type "${type.name}" with uncompletion traces. | ||
This might be caused by one of the following reasons: | ||
1. A third party library is targeting inversify reflection metadata. | ||
2. A bug is causing the issue. Consider submiting an issue to fix it.`, | ||
); | ||
} | ||
|
||
throw new InversifyCoreError( | ||
InversifyCoreErrorKind.missingInjectionDecorator, | ||
`Invalid class metadata at type ${type.name}: | ||
${errors.join('\n\n')}`, | ||
); | ||
} |