Skip to content

Commit bb5479c

Browse files
committed
Route scanner and resolver testing
1 parent 913ce90 commit bb5479c

9 files changed

+175
-8
lines changed

Diff for: jest.config.js

+3
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ module.exports = {
99
'^@common/(.*)$': '<rootDir>/src/common/$1',
1010
'^@utils/(.*)$': '<rootDir>/src/utils/$1',
1111
},
12+
setupFiles: [
13+
'<rootDir>/tests/setup.ts'
14+
]
1215
};

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"build": "tsc",
99
"dev:server": "nodemon --watch './**/*.ts' --exec 'ts-node -r tsconfig-paths/register' properjs/app.ts",
1010
"start": "node ./lib/src/index.js",
11-
"test": "jest"
11+
"test": "jest --silent"
1212
},
1313
"repository": {
1414
"type": "git",

Diff for: src/core/route/route-scanner.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import Logger from "@utils/logger";
2-
import {ExceptionHandler} from "../exception/exception-handler";
32
import {ApplicationServerAdapter } from "../adapter/application-server-adapter";
43
import {RouteResolver} from "./route-resolver";
54
import {ApplicationContainer} from "@core/container/application-container";
@@ -10,14 +9,12 @@ export default class RouteScanner {
109

1110
private readonly container: ApplicationContainer;
1211
private readonly routeResolver: RouteResolver
13-
private readonly exceptionHandler: ExceptionHandler;
1412
private readonly app: ApplicationServerAdapter
1513

16-
constructor(container: ApplicationContainer, app: ApplicationServerAdapter) {
14+
constructor(container: ApplicationContainer, app: ApplicationServerAdapter, resolver: RouteResolver = new RouteResolver(app)) {
1715
this.container = container;
1816
this.app = app;
19-
this.exceptionHandler = new ExceptionHandler();
20-
this.routeResolver = new RouteResolver(this.app);
17+
this.routeResolver = resolver
2118
}
2219

2320
public scanRoutes() {

Diff for: tests/core/discovery/dependency-discover.spec.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import {Controller} from "@common/decorators/controller.decorator";
55

66
describe("DependencyDiscover", () => {
77

8-
const applicationContainer: ApplicationContainer = new ApplicationContainer();
9-
const dependencyDiscover = new DependencyDiscover(applicationContainer);
8+
let applicationContainer: ApplicationContainer
9+
let dependencyDiscover: DependencyDiscover
1010

1111
@Component()
1212
class DependencyExample {}
@@ -21,6 +21,11 @@ describe("DependencyDiscover", () => {
2121
constructor(private readonly _: DependencyExample) {}
2222
}
2323

24+
beforeEach(() => {
25+
applicationContainer = new ApplicationContainer();
26+
dependencyDiscover = new DependencyDiscover(applicationContainer);
27+
})
28+
2429
it('should throw an error when a constructor dependency provider is not registered in the container', function () {
2530
// given
2631
applicationContainer.addProvider(ComponentExample)

Diff for: tests/core/loader/instance-loader.spec.ts

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import {InstanceLoader} from "@core/loader/instance-loader";
2+
import {ApplicationContainer} from "@core/container/application-container";
3+
import ConfigurationComponentScanner from "@core/scanner/strategy/configuration-component-scanner";
4+
import Component from "@common/type/component";
5+
import {ComponentType} from "@common/type/component-type";
6+
jest.mock('@core/scanner/strategy/configuration-component-scanner')
7+
8+
describe('InstanceLoader', () => {
9+
10+
const container = new ApplicationContainer();
11+
const scanner = new ConfigurationComponentScanner();
12+
13+
const loader = new InstanceLoader(container, scanner);
14+
15+
afterEach(() => {
16+
jest.clearAllMocks();
17+
})
18+
19+
it('should add the providers when component are scanned', async function () {
20+
// given
21+
const components: Component[] = [
22+
{
23+
type: ComponentType.COMPONENT,
24+
reference: jest.fn()
25+
}
26+
]
27+
const scanComponentsSpy = jest.spyOn(scanner, "scanComponents").mockResolvedValue(components)
28+
const addProviderSpy = jest.spyOn(container, "addProvider")
29+
30+
// when
31+
await loader.load();
32+
33+
// then
34+
expect(scanComponentsSpy).toBeCalledTimes(1)
35+
expect(addProviderSpy).toBeCalledTimes(1)
36+
expect(addProviderSpy).toBeCalledWith(components[0].reference)
37+
expect(container.getProviders().size).toBe(1)
38+
});
39+
40+
it('should add the controllers when component are scanned', async function () {
41+
// given
42+
const components: Component[] = [
43+
{
44+
type: ComponentType.CONTROLLER,
45+
reference: jest.fn()
46+
}
47+
]
48+
const scanComponentsSpy = jest.spyOn(scanner, "scanComponents").mockResolvedValue(components)
49+
const addControllerSpy = jest.spyOn(container, "addController")
50+
51+
// when
52+
await loader.load();
53+
54+
// then
55+
expect(scanComponentsSpy).toBeCalledTimes(1)
56+
expect(addControllerSpy).toBeCalledTimes(1)
57+
expect(addControllerSpy).toBeCalledWith(components[0].reference)
58+
expect(container.getControllers().size).toBe(1)
59+
});
60+
})

Diff for: tests/core/route/metadata-extractor.spec.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import MetadataExtractor from "@core/route/metadata-extractor";
2+
3+
describe('MetadataExtractor', () => {
4+
5+
it('should extract methods from prototype when present', () => {
6+
// given
7+
class ExampleClass {
8+
public exampleMethod() {}
9+
}
10+
const prototype = Object.getPrototypeOf(new ExampleClass())
11+
12+
// when
13+
const methods = MetadataExtractor.extractMetadataFromPrototype(prototype)
14+
15+
// then
16+
expect(methods.length).toBe(1)
17+
expect(methods[0]).toBe(prototype['exampleMethod'])
18+
})
19+
20+
})

Diff for: tests/core/route/route-resolver.spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import {RouteResolver} from "@core/route/route-resolver";
2+
import {ExpressApplicationServer} from "@core/adapter/express-application-server";
3+
import MetaType from "@common/type/meta-type";
4+
import {Controller as ControllerDecorator} from "@common/decorators/controller.decorator";
5+
import {Get} from "@common/decorators/methods";
6+
import {Controller} from "@common/type/controller";
7+
import {HttpMethod} from "@core/adapter/application-server-adapter";
8+
import MetadataExtractor from "@core/route/metadata-extractor";
9+
jest.mock("@core/adapter/express-application-server")
10+
11+
describe('RouteResolver', function () {
12+
13+
const applicationServer = new ExpressApplicationServer();
14+
const routeResolver = new RouteResolver(applicationServer);
15+
16+
@ControllerDecorator()
17+
class ExampleController {
18+
19+
@Get()
20+
public exampleMethod() {}
21+
}
22+
23+
it('should register route with correct handler when metatype contains all information', function () {
24+
// given
25+
const metaType = new MetaType<Controller>(ExampleController);
26+
metaType.instance = new metaType.reference();
27+
const metadata = MetadataExtractor.extractMetadataFromPrototype(Object.getPrototypeOf(metaType.instance))
28+
29+
// when
30+
routeResolver.resolveControllerRoutes(metaType);
31+
32+
// then
33+
expect(applicationServer.registerRoute).toBeCalledTimes(1)
34+
expect(applicationServer.registerRoute).toBeCalledWith({path: '/', method: HttpMethod.GET, handler: metadata[0]})
35+
});
36+
});

Diff for: tests/core/route/route-scanner.spec.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import RouteScanner from "@core/route/route-scanner";
2+
import {ApplicationContainer} from "@core/container/application-container";
3+
import {ExpressApplicationServer} from "@core/adapter/express-application-server";
4+
import {RouteResolver} from "@core/route/route-resolver";
5+
import MetaType from "@common/type/meta-type";
6+
import {Controller} from "@common/type/controller";
7+
jest.mock('@core/adapter/express-application-server')
8+
jest.mock('@core/container/application-container')
9+
jest.mock('@core/route/route-resolver')
10+
11+
12+
describe('RouteScanner', () => {
13+
14+
const applicationServerAdapter = new ExpressApplicationServer();
15+
const container = new ApplicationContainer();
16+
const resolver = new RouteResolver(applicationServerAdapter);
17+
const routeScanner = new RouteScanner(container, applicationServerAdapter, resolver);
18+
19+
beforeEach(() => {
20+
jest.clearAllMocks();
21+
})
22+
23+
it('should resolve controller when container contains controllers', () => {
24+
// given
25+
const metaTypeMock = new MetaType<Controller>(jest.fn());
26+
const controllerMockMap = new Map<String, MetaType<Controller>>();
27+
controllerMockMap.set("mock", metaTypeMock);
28+
jest.spyOn(container, 'getControllers').mockReturnValue(controllerMockMap)
29+
30+
// when
31+
routeScanner.scanRoutes();
32+
33+
// then
34+
expect(resolver.resolveControllerRoutes).toBeCalledTimes(1);
35+
expect(resolver.resolveControllerRoutes).toBeCalledWith(metaTypeMock);
36+
})
37+
38+
it('should register global exception handler once when routes are resolved', () => {
39+
// when
40+
routeScanner.scanRoutes();
41+
42+
// then
43+
expect(applicationServerAdapter.registerExceptionHandler).toBeCalledTimes(1)
44+
})
45+
})

Diff for: tests/setup.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
jest.mock("@utils/logger") // For logging mocks to avoid verbose logging in tests

0 commit comments

Comments
 (0)