Skip to content

Commit

Permalink
refactor(core): add throwAtInvalidClassMetadata
Browse files Browse the repository at this point in the history
  • Loading branch information
notaphplover committed Dec 16, 2024
1 parent 2e38c02 commit e28ccc6
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 0 deletions.
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),
);
});
});
});
});
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')}`,
);
}

0 comments on commit e28ccc6

Please sign in to comment.