Skip to content

Commit

Permalink
refactor(testing-handlers): create real context
Browse files Browse the repository at this point in the history
  • Loading branch information
VinceOPS committed May 31, 2020
1 parent 7bd6719 commit 4957378
Show file tree
Hide file tree
Showing 10 changed files with 383 additions and 262 deletions.
14 changes: 0 additions & 14 deletions packages/testing/handlers/create-context.ts

This file was deleted.

28 changes: 18 additions & 10 deletions packages/testing/handlers/http-testing-handler.ts
@@ -1,6 +1,5 @@
import { Type } from '@nestjs/common';

import { createContext } from './create-context';
import { TestingHandler } from './testing-handler';

type ObjectLiteral = Record<string | symbol, unknown>;
Expand All @@ -26,6 +25,7 @@ export class HttpTestingHandler<TOutput> implements TestingHandler<TOutput> {
private req: TestRequest;

private constructor(
private readonly createContext: () => (...args: any[]) => Promise<TOutput>,
private readonly controllerClass: Type<any>,
private readonly methodName: string,
) {}
Expand All @@ -34,29 +34,37 @@ export class HttpTestingHandler<TOutput> implements TestingHandler<TOutput> {
* Create a new instance of `HttpTestingHandler`, with
* @template TOutput Expected type of the response sent to the client.
*
* * @param createContext Function building the context handler.
* @param controllerClass Target controller class.
* @param methodName Controller method name.
*
* @return New instance of HttpTestingHandler.
*/
public static create<TOutput>(
createContext: () => (...args: any[]) => Promise<TOutput>,
controllerClass: Type<any>,
methodName: string,
) {
return new HttpTestingHandler<TOutput>(controllerClass, methodName);
return new HttpTestingHandler<TOutput>(
createContext,
controllerClass,
methodName,
);
}

setRequest(req: TestRequest) {
this.req = req;
this.req = {
headers: {},
body: {},
params: {},
query: {},
...req,
};
return this;
}

run(): TOutput {
const runnable = createContext<TOutput>(
this.controllerClass,
this.methodName,
'http',
);
return runnable(this.req, {}, {});
async run(): Promise<TOutput> {
const runnable = await this.createContext();
return runnable(this.req);
}
}
18 changes: 10 additions & 8 deletions packages/testing/handlers/rpc-testing-handler.ts
@@ -1,6 +1,5 @@
import { Type } from '@nestjs/common';

import { createContext } from './create-context';
import { TestingHandler } from './testing-handler';

/**
Expand All @@ -14,6 +13,7 @@ export class RpcTestingHandler<TOutput> implements TestingHandler<TOutput> {
private context: any;

private constructor(
private readonly createContext: () => (...args: any[]) => Promise<TOutput>,
private readonly controllerClass: Type<any>,
private readonly methodName: string,
) {}
Expand All @@ -22,16 +22,22 @@ export class RpcTestingHandler<TOutput> implements TestingHandler<TOutput> {
* Create a new instance of `RpcTestingHandler`, with
* @template TOutput Expected type of the response sent to the client.
*
* @param createContext Function building the context handler.
* @param controllerClass Target controller class.
* @param methodName Controller method name.
*
* @return New instance of RpcTestingHandler.
*/
public static create<TOutput>(
createContext: () => (...args: any[]) => Promise<TOutput>,
controllerClass: Type<any>,
methodName: string,
) {
return new RpcTestingHandler<TOutput>(controllerClass, methodName);
return new RpcTestingHandler<TOutput>(
createContext,
controllerClass,
methodName,
);
}

setData(data: any) {
Expand All @@ -44,12 +50,8 @@ export class RpcTestingHandler<TOutput> implements TestingHandler<TOutput> {
return this;
}

run(): TOutput {
const runnable = createContext<TOutput>(
this.controllerClass,
this.methodName,
'rpc',
);
async run(): Promise<TOutput> {
const runnable = await this.createContext();
return runnable(this.data, this.context);
}
}
2 changes: 1 addition & 1 deletion packages/testing/handlers/testing-handler.ts
@@ -1,3 +1,3 @@
export interface TestingHandler<TOutput = any> {
run(): TOutput;
run(): Promise<TOutput>;
}
18 changes: 10 additions & 8 deletions packages/testing/handlers/ws-testing-handler.ts
@@ -1,6 +1,5 @@
import { Type } from '@nestjs/common';

import { createContext } from './create-context';
import { TestingHandler } from './testing-handler';

/**
Expand All @@ -14,6 +13,7 @@ export class WsTestingHandler<TOutput> implements TestingHandler<TOutput> {
private data: any;

private constructor(
private readonly createContext: () => (...args: any[]) => Promise<TOutput>,
private readonly gatewayClass: Type<any>,
private readonly methodName: string,
) {}
Expand All @@ -22,16 +22,22 @@ export class WsTestingHandler<TOutput> implements TestingHandler<TOutput> {
* Create a new instance of `WsTestingHandler`, with
* @template TOutput Expected type of the response sent to the client.
*
* @param createContext Function building the context handler.
* @param controllerClass Target controller class.
* @param methodName Controller method name.
*
* @return New instance of WsTestingHandler.
*/
public static create<TOutput>(
createContext: () => (...args: any[]) => Promise<TOutput>,
controllerClass: Type<any>,
methodName: string,
) {
return new WsTestingHandler<TOutput>(controllerClass, methodName);
return new WsTestingHandler<TOutput>(
createContext,
controllerClass,
methodName,
);
}

setClient(client: any) {
Expand All @@ -44,12 +50,8 @@ export class WsTestingHandler<TOutput> implements TestingHandler<TOutput> {
return this;
}

run(): TOutput {
const runnable = createContext<TOutput>(
this.gatewayClass,
this.methodName,
'ws',
);
async run(): Promise<TOutput> {
const runnable = await this.createContext();
return runnable(this.client, this.data);
}
}
135 changes: 135 additions & 0 deletions packages/testing/test/handlers/http-testing-handler.spec.ts
@@ -0,0 +1,135 @@
import { expect } from 'chai';

import {
Body,
Controller,
createParamDecorator,
ExecutionContext,
Headers,
Param,
Put,
Get,
Delete,
} from '../../../common';
import { Test } from '../../test';
import { TestingModule } from '../../testing-module';

describe('Http Testing Handlers', () => {
let testingModule: TestingModule;

describe('With a simple HTTP controller...', () => {
@Controller('dogs')
class DogsController {
@Get()
fetchAllDogs() {
return ['Dogo', 'Doge', 'Dogg'];
}
}

beforeEach(async () => {
testingModule = await Test.createTestingModule({
controllers: [DogsController],
}).compile();
});

afterEach(() => testingModule.close());

it('creates an executable handler for the given HTTP controller class and method', async () => {
const request = { params: { name: 'Dogo' }, body: { age: 10 } };
const handler = testingModule
.createHttpHandler({
class: DogsController,
methodName: 'fetchAllDogs',
})
.setRequest(request);

const result = await handler.run();

expect(result).to.eql(['Dogo', 'Doge', 'Dogg']);
});
});

describe('With a custom Request...', () => {
@Controller('dogs')
class DogsController {
@Put(':name')
update(
@Param('name') name: string,
@Body('age') age: number,
@Headers() headers: Record<string, string>,
) {
if (headers && headers.Authorization) {
return { name, age, chipId: '1234567' };
}

return { name, age };
}
}

beforeEach(async () => {
testingModule = await Test.createTestingModule({
controllers: [DogsController],
}).compile();
});

afterEach(() => testingModule.close());

it('allows configuring request', async () => {
const request = {
params: { name: 'Doggo' },
body: { age: 9 },
headers: { Authorization: 'AccessKey K0tL3t' },
};
const handler = testingModule.createHttpHandler({
class: DogsController,
methodName: 'update',
});

const result = await handler.setRequest(request).run();

expect(result).to.eql({ name: 'Doggo', age: 9, chipId: '1234567' });
});
});

describe('With custom Param Decorators', () => {
const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest();
return req.user;
},
);
@Controller('tasks')
class TasksController {
@Delete()
deleteTasks(@CurrentUser() user: { userId: string }) {
return { userId: user.userId, deleteCount: 16 };
}
}

beforeEach(async () => {
testingModule = await Test.createTestingModule({
controllers: [TasksController],
}).compile();
});

afterEach(() => testingModule.close());

it('handles custom param decorators', async () => {
const request = {
params: { name: 'Doggy' },
user: { userId: '2d2301c6-5e4d-4257-97e0-ec59005c77c8' },
};
const handler = testingModule.createHttpHandler({
class: TasksController,
methodName: 'deleteTasks',
});

const result = await handler.setRequest(request).run();

expect(result).to.eql({
userId: '2d2301c6-5e4d-4257-97e0-ec59005c77c8',
deleteCount: 16,
});
});
});
});
69 changes: 69 additions & 0 deletions packages/testing/test/handlers/rpc-testing-handler.spec.ts
@@ -0,0 +1,69 @@
import { expect } from 'chai';

import { Controller } from '../../../common';
import { MessagePattern, Payload, Ctx } from '../../../microservices';
import { Test } from '../../test';
import { TestingModule } from '../../testing-module';

describe('RPC Testing Handlers', () => {
let testingModule: TestingModule;

describe('With a simple RPC controller', () => {
@Controller()
class MathController {
@MessagePattern({ cmd: 'sum' })
accumulate(data: number[]): number {
return (data || []).reduce((a, b) => a + b, 0);
}
}

beforeEach(async () => {
testingModule = await Test.createTestingModule({
controllers: [MathController],
}).compile();
});

afterEach(() => testingModule.close());

it('creates an executable handler for the given RPC controller class and method', async () => {
const handler = testingModule.createRpcHandler({
class: MathController,
methodName: 'accumulate',
});

const result = await handler.run();

expect(result).to.equal(0);
});
});

describe('With a custom context', () => {
@Controller()
class MathController {
@MessagePattern({ cmd: 'sum' })
accumulate(data: number[], context: any): number {
return (data || []).reduce((a, b) => a + b, context.offset);
}
}

beforeEach(async () => {
testingModule = await Test.createTestingModule({
controllers: [MathController],
}).compile();
});

afterEach(() => testingModule.close());

it('allows configuring the data and context passed to the handler', async () => {
const handler = testingModule.createRpcHandler({
class: MathController,
methodName: 'accumulate',
});
const data = [1, 2, 3];
const context = { offset: 10 };
const result = await handler.setData(data).setContext(context).run();

expect(result).to.equal(16);
});
});
});

0 comments on commit 4957378

Please sign in to comment.