Skip to content

Commit

Permalink
test: container tests
Browse files Browse the repository at this point in the history
  • Loading branch information
carlocorradini committed Oct 17, 2022
1 parent 19f0a4c commit 104cafa
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 2 deletions.
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"snyk",
"tada",
"tsdoc",
"typedi",
"typegraphql"
]
}
1 change: 1 addition & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default <JestConfigWithTsJest>{
verbose: false,
rootDir: './',
roots: ['<rootDir>/src', '<rootDir>/tests'],
setupFilesAfterEnv: ['<rootDir>/tests/jest.setup.ts'],
testEnvironment: 'node',
collectCoverage: false,
collectCoverageFrom: ['<rootDir>/src/**/*.ts', '!<rootDir>/src/**/*.d.ts'],
Expand Down
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"ts-patch": "^2.0.2",
"tsconfig-paths": "^4.1.0",
"type-graphql": "^1.2.0-rc.1",
"typedi": "^0.10.0",
"typescript": "^4.8.3",
"typescript-transform-paths": "^3.3.1"
},
Expand Down
2 changes: 1 addition & 1 deletion tests/authentication.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* SOFTWARE.
*/

import { ApolloServer } from 'apollo-server';
import type { ApolloServer } from 'apollo-server';
import type { ExpressContext } from 'apollo-server-express';
import { defaultAuthFn } from '../src';
import {
Expand Down
168 changes: 168 additions & 0 deletions tests/container.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* MIT License
*
* Copyright (c) 2022-2022 Carlo Corradini
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

/* eslint-disable max-classes-per-file */

import type { ExpressContext } from 'apollo-server-express';
import { Container, Inject, Service } from 'typedi';
import {
AuthData,
AuthFnClass,
ResolverData,
defaultAuthFn,
ContainerType
} from '../src';
import { buildServer, Context, sign, TokenPayload } from './utils';

@Service()
class WaitService {
public static readonly DEFAULT_WAIT_MS: number = 250;

// eslint-disable-next-line class-methods-use-this
public async wait(time: number = WaitService.DEFAULT_WAIT_MS): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
}
}

@Service()
class AuthService implements AuthFnClass {
@Inject()
private readonly waitService!: WaitService;

public async auth(
resolverData: ResolverData,
authData: AuthData
): Promise<boolean> {
await this.waitService.wait();
return defaultAuthFn(resolverData, authData);
}
}

class Auth implements AuthFnClass {
// eslint-disable-next-line class-methods-use-this
public auth(
resolverData: ResolverData,
authData: AuthData
): boolean | Promise<boolean> {
return defaultAuthFn(resolverData, authData);
}
}

describe('Container', () => {
const user: TokenPayload = { id: 0, roles: [], permissions: [] };
let context: ExpressContext;

beforeEach(() => {
Container.reset();
});

beforeAll(() => {
context = {
req: {
headers: {
authorization: `Bearer ${sign(user)}`
}
}
} as ExpressContext;
});

it('should use default container to instantiate auth class', async () => {
const server = buildServer({ auth: Auth });
const result = await server.executeOperation(
{
query: 'query { protected { id, roles, permissions } }'
},
context
);

expect(result.errors).toBeUndefined();
expect(result.data?.protected).toMatchObject(user);
});

it('should use provided container to load auth class', async () => {
const server = buildServer({ auth: AuthService, container: Container });
const result = await server.executeOperation(
{
query: 'query { protected { id, roles, permissions } }'
},
context
);

expect(result.errors).toBeUndefined();
expect(result.data?.protected).toMatchObject(user);
});

it("should pass resolver's data to container's get", async () => {
let userContext: undefined | TokenPayload;
const container: ContainerType = {
get(someClass, resolverData: ResolverData<Context>) {
userContext = resolverData.context.user;
return Container.get(someClass);
}
};
const server = buildServer({
auth: AuthService,
container
});
const result = await server.executeOperation(
{
query: 'query { protected { id, roles, permissions } }'
},
context
);

expect(result.errors).toBeUndefined();
expect(result.data?.protected).toMatchObject(user);
expect(userContext).toMatchObject(user);
});

it('should get instance from an async container', async () => {
let called = false;
const container: ContainerType = {
async get(someClass) {
await new WaitService().wait();
called = true;
return Container.get(someClass);
}
};

const server = buildServer({
auth: AuthService,
container
});

const result = await server.executeOperation(
{
query: 'query { protected { id, roles, permissions } }'
},
context
);

expect(result.errors).toBeUndefined();
expect(result.data?.protected).toMatchObject(user);
expect(called).toBeTruthy();
});
});
25 changes: 25 additions & 0 deletions tests/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* MIT License
*
* Copyright (c) 2022-2022 Carlo Corradini
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

import 'reflect-metadata';
6 changes: 5 additions & 1 deletion tests/utils/buildServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@

import { ApolloServer } from 'apollo-server';
import { AuthDirectiveArgs, buildAuthDirective } from '../../src';
import type { Context } from './Context';
import type { UserRoles, UserPermissions } from './user';
import { buildSchema } from './buildSchema';
import { contextHelper } from './contextHelper';

export function buildServer(args: AuthDirectiveArgs) {
export function buildServer(
args: AuthDirectiveArgs<Context, UserRoles, UserPermissions>
) {
return new ApolloServer({
schema: buildSchema(buildAuthDirective(args)),
context: contextHelper
Expand Down
4 changes: 4 additions & 0 deletions tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["./src/**/*.ts", "./tests/**/*.ts"]
}

0 comments on commit 104cafa

Please sign in to comment.