From 3d327ca3d46134fa226b4a7b2df02c3cd0476623 Mon Sep 17 00:00:00 2001 From: Eric Cabrel TIOGO Date: Thu, 6 Jun 2024 23:35:48 +0200 Subject: [PATCH] test(backend): improvements on integration tests related to auth --- .../src/features/app/app.service.spec.ts | 4 +- .../auth/graphql/auth.integration.spec.ts | 176 +++--------------- apps/backend/src/utils/tests/helpers.ts | 134 ++++++++----- 3 files changed, 111 insertions(+), 203 deletions(-) diff --git a/apps/backend/src/features/app/app.service.spec.ts b/apps/backend/src/features/app/app.service.spec.ts index 03241601..d1b70839 100644 --- a/apps/backend/src/features/app/app.service.spec.ts +++ b/apps/backend/src/features/app/app.service.spec.ts @@ -9,6 +9,8 @@ const prismaServiceMock = mock(); const roleServiceMock = mock(); const userServiceMock = mock(); +const { ADMIN_PASSWORD } = process.env; + describe('Test App Service', () => { let appService: AppService; let roleService: RoleService; @@ -46,6 +48,6 @@ describe('Test App Service', () => { expect(roleService.loadRoles).toHaveBeenCalledTimes(1); expect(userService.loadAdminUser).toHaveBeenCalledTimes(1); - expect(userService.loadAdminUser).toHaveBeenCalledWith(role, 'qwerty'); + expect(userService.loadAdminUser).toHaveBeenCalledWith(role, ADMIN_PASSWORD); }); }); diff --git a/apps/backend/src/features/auth/graphql/auth.integration.spec.ts b/apps/backend/src/features/auth/graphql/auth.integration.spec.ts index e2bff88c..f9e7207e 100644 --- a/apps/backend/src/features/auth/graphql/auth.integration.spec.ts +++ b/apps/backend/src/features/auth/graphql/auth.integration.spec.ts @@ -1,5 +1,5 @@ -import { PrismaService, RoleService, SessionService, UserService } from '@snipcode/domain'; -import { generateJwtToken, isValidUUIDV4 } from '@snipcode/utils'; +import { SessionService } from '@snipcode/domain'; +import { isValidUUIDV4 } from '@snipcode/utils'; import request from 'supertest'; import { TestHelper } from '../../../utils/tests/helpers'; @@ -10,20 +10,14 @@ const graphqlEndpoint = '/graphql'; describe('Test Authentication', () => { let server: TestServer; let testHelper: TestHelper; - let prismaService: PrismaService; - let roleService: RoleService; let sessionService: SessionService; - let userService: UserService; beforeAll(async () => { server = await startTestServer(); - prismaService = server.app.get(PrismaService); - roleService = server.app.get(RoleService); - userService = server.app.get(UserService); sessionService = server.app.get(SessionService); - testHelper = new TestHelper(prismaService, roleService, userService); + testHelper = new TestHelper(server.app, graphqlEndpoint); }); beforeEach(async () => { @@ -81,7 +75,7 @@ describe('Test Authentication', () => { }, }; - await testHelper.createTestUser({ email: variables.input.email }); + await testHelper.signupUser({ email: variables.input.email }); const response = await request(server.app.getHttpServer()) .post(graphqlEndpoint) @@ -127,11 +121,10 @@ describe('Test Authentication', () => { } `; - await testHelper.createTestUser({ + await testHelper.signupUser({ email: 'jane.doe@snipcode.dev', isEnabled: true, password: 'password', - role: 'user', }); const variables = { @@ -163,11 +156,10 @@ describe('Test Authentication', () => { } `; - await testHelper.createTestUser({ + await testHelper.signupUser({ email: 'disabled.user@snipcode.dev', isEnabled: false, password: 'password', - role: 'user', }); const variables = { @@ -186,14 +178,14 @@ describe('Test Authentication', () => { expect(error.message).toEqual('Your account is disabled!'); }); - test('Returns when retrieving the authenticated user without an authentication token', async () => { + test('Returns an error when retrieving the authenticated user without an authentication token', async () => { const authenticatedUserQuery = ` - query AuthenticatedUser { - authenticatedUser { - id - } + query AuthenticatedUser { + authenticatedUser { + id } - `; + } + `; const response = await request(server.app.getHttpServer()) .post(graphqlEndpoint) @@ -207,71 +199,13 @@ describe('Test Authentication', () => { }); test('Retrieve the authenticated user', async () => { - const signUpQuery = ` - mutation SignupUser($input: SignupUserInput!) { - signupUser(input: $input) { - __typename - message - userId - } - } - `; - - const signUpVariables = { - input: { - email: 'jon.doe@snipcode.dev', - name: 'John Doe', - password: 'password', - }, - }; - - const signUpResponse = await request(server.app.getHttpServer()) - .post(graphqlEndpoint) - .send({ query: signUpQuery, variables: signUpVariables }) - .expect(200); - - const confirmationToken = generateJwtToken({ - expiresIn: '1h', - payload: { userId: signUpResponse.body.data.signupUser.userId }, - secret: process.env.JWT_SECRET, - }); - - const confirmUserQuery = ` - mutation ConfirmUser($token: String!) { - confirmUser(token: $token) { - message - } - } - `; - - const confirmUserVariables = { - token: confirmationToken, + const input = { + email: 'jon.doe@snipcode.dev', + name: 'John Doe', + password: 'password', }; - await request(server.app.getHttpServer()) - .post(graphqlEndpoint) - .send({ query: confirmUserQuery, variables: confirmUserVariables }) - .expect(200); - - const loginQuery = ` - mutation LoginUser($email: String!, $password: String!) { - loginUser(email: $email, password: $password) { - token - } - } - `; - - const loginVariables = { - email: signUpVariables.input.email, - password: signUpVariables.input.password, - }; - - const loginResponse = await request(server.app.getHttpServer()) - .post(graphqlEndpoint) - .send({ query: loginQuery, variables: loginVariables }) - .expect(200); - - const authToken = loginResponse.body.data.loginUser.token; + const { authToken, userId } = await testHelper.createAuthenticatedUser({ ...input }); const authenticatedUserQuery = ` query AuthenticatedUser { @@ -306,10 +240,10 @@ describe('Test Authentication', () => { expect(authenticatedUser).toMatchObject({ createdAt: expect.any(Number), - email: loginVariables.email, - id: signUpResponse.body.data.signupUser.userId, + email: input.email, + id: userId, isEnabled: true, - name: signUpVariables.input.name, + name: input.name, oauthProvider: 'email', pictureUrl: null, role: { @@ -325,72 +259,12 @@ describe('Test Authentication', () => { }); test('Log out the authenticated user', async () => { - const signUpQuery = ` - mutation SignupUser($input: SignupUserInput!) { - signupUser(input: $input) { - __typename - message - userId - } - } - `; - - const signUpVariables = { - input: { - email: 'jane.doe@snipcode.dev', - name: 'Jane Doe', - password: 'password', - }, - }; - - const signUpResponse = await request(server.app.getHttpServer()) - .post(graphqlEndpoint) - .send({ query: signUpQuery, variables: signUpVariables }) - .expect(200); - - const confirmationToken = generateJwtToken({ - expiresIn: '1h', - payload: { userId: signUpResponse.body.data.signupUser.userId }, - secret: process.env.JWT_SECRET, + const { authToken, userId } = await testHelper.createAuthenticatedUser({ + email: 'jane.doe@snipcode.dev', + name: 'Jane Doe', + password: 'password', }); - const confirmUserQuery = ` - mutation ConfirmUser($token: String!) { - confirmUser(token: $token) { - message - } - } - `; - - const confirmUserVariables = { - token: confirmationToken, - }; - - await request(server.app.getHttpServer()) - .post(graphqlEndpoint) - .send({ query: confirmUserQuery, variables: confirmUserVariables }) - .expect(200); - - const loginQuery = ` - mutation LoginUser($email: String!, $password: String!) { - loginUser(email: $email, password: $password) { - token - } - } - `; - - const loginVariables = { - email: signUpVariables.input.email, - password: signUpVariables.input.password, - }; - - const loginResponse = await request(server.app.getHttpServer()) - .post(graphqlEndpoint) - .send({ query: loginQuery, variables: loginVariables }) - .expect(200); - - const authToken = loginResponse.body.data.loginUser.token; - const authenticatedUserQuery = ` query AuthenticatedUser { authenticatedUser { @@ -407,7 +281,7 @@ describe('Test Authentication', () => { const { authenticatedUser } = response.body.data; - expect(authenticatedUser.id).toEqual(signUpResponse.body.data.signupUser.userId); + expect(authenticatedUser.id).toEqual(userId); const logoutQuery = ` mutation LogoutUser { diff --git a/apps/backend/src/utils/tests/helpers.ts b/apps/backend/src/utils/tests/helpers.ts index 902c8a88..efcf5076 100644 --- a/apps/backend/src/utils/tests/helpers.ts +++ b/apps/backend/src/utils/tests/helpers.ts @@ -1,74 +1,106 @@ -import { randEmail, randFullName, randImg, randNumber, randPassword, randTimeZone, randUserName } from '@ngneat/falso'; -import { - CreateUserInput, - OauthProvider, - PrismaService, - Role, - RoleName, - RoleService, - User, - UserService, -} from '@snipcode/domain'; +import { INestApplication } from '@nestjs/common'; +import { randEmail, randFullName, randPassword } from '@ngneat/falso'; +import { PrismaService, RoleName } from '@snipcode/domain'; +import { generateJwtToken } from '@snipcode/utils'; +import request from 'supertest'; export type CreateUserInputArgs = { email: string; isEnabled: boolean; name: string; - oauthProvider: OauthProvider; - password?: string | null; - pictureUrl: string | null; + password: string | null; role: RoleName; - roleId: string; - timezone: string | null; - username: string | null; }; export class TestHelper { constructor( - private readonly prismaService: PrismaService, - private readonly roleService: RoleService, - private readonly userService: UserService, + private readonly app: INestApplication, + private readonly graphqlEndpoint: string, ) {} - static createTestUserInput(override: Partial): CreateUserInput { - const input = new CreateUserInput({ - email: randEmail(), - name: randFullName(), - oauthProvider: 'github', - password: randPassword(), - pictureUrl: randImg(), - roleId: 'roleId', - timezone: randTimeZone(), - username: randUserName(), - ...override, - }); - - input.isEnabled = Boolean(override.isEnabled ?? randNumber({ max: 1, min: 0 })); - - return input; + async cleanDatabase(): Promise { + const prismaService = this.app.get(PrismaService); + + await prismaService.snippet.deleteMany(); + await prismaService.folder.deleteMany(); + await prismaService.session.deleteMany(); + await prismaService.user.deleteMany(); } - async findTestRole(name: RoleName): Promise { - const role = await this.roleService.findByName(name); + async signupUser(input: Partial): Promise { + const query = ` + mutation SignupUser($input: SignupUserInput!) { + signupUser(input: $input) { + __typename + message + userId + } + } + `; + const variables = { + input: { + email: input.email ?? randEmail(), + name: input.name ?? randFullName(), + password: input.password ?? randPassword(), + }, + }; + + const response = await request(this.app.getHttpServer()).post(this.graphqlEndpoint).send({ query, variables }); + + if (input.isEnabled) { + const confirmationToken = generateJwtToken({ + expiresIn: '1h', + payload: { userId: response.body.data.signupUser.userId }, + secret: process.env.JWT_SECRET, + }); + + const confirmUserQuery = ` + mutation ConfirmUser($token: String!) { + confirmUser(token: $token) { + message + } + } + `; - if (!role) { - throw new Error(`Role with the name "${name}" not found!`); + const confirmUserVariables = { + token: confirmationToken, + }; + + await request(this.app.getHttpServer()) + .post(this.graphqlEndpoint) + .send({ query: confirmUserQuery, variables: confirmUserVariables }); } - return role; + return response.body.data.signupUser.userId; } - async createTestUser(input: Partial): Promise { - const role = await this.findTestRole(input.role ?? 'user'); + async createAuthenticatedUser(input: Partial): Promise<{ authToken: string; userId: string }> { + const createUserInput: Partial = { + ...input, + email: input.email ?? randEmail(), + isEnabled: input.isEnabled ?? true, + password: input.password ?? randPassword(), + }; - const createUserInput = TestHelper.createTestUserInput({ ...input, roleId: role.id }); + const userId = await this.signupUser(createUserInput); - return this.userService.create(createUserInput); - } + const query = ` + mutation LoginUser($email: String!, $password: String!) { + loginUser(email: $email, password: $password) { + token + } + } + `; - async cleanDatabase(): Promise { - await this.prismaService.snippet.deleteMany(); - await this.prismaService.folder.deleteMany(); - await this.prismaService.session.deleteMany(); - await this.prismaService.user.deleteMany(); + const variables = { + email: createUserInput.email, + password: createUserInput.password, + }; + + const response = await request(this.app.getHttpServer()).post(this.graphqlEndpoint).send({ query, variables }); + + return { + authToken: response.body.data.loginUser.token, + userId, + }; } }