From 24c95f4801336bf2e88680bb75cc2355de684095 Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Wed, 4 Oct 2023 12:20:50 +0200 Subject: [PATCH 1/7] refactor: Remove reduntant global knex instance --- packages/backend/test/setup/global-hooks.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend/test/setup/global-hooks.ts b/packages/backend/test/setup/global-hooks.ts index c2a0132f..bccc9c68 100644 --- a/packages/backend/test/setup/global-hooks.ts +++ b/packages/backend/test/setup/global-hooks.ts @@ -3,7 +3,6 @@ import { client as knex } from '../../src/config/database'; import logger from '../../src/helpers/logger'; global.beforeAll(async () => { - global.knexInstance = knex; global.knex = null; logger.silent = true; }); @@ -22,6 +21,5 @@ global.afterEach(async () => { }); global.afterAll(async () => { - global.knexInstance.destroy(); logger.silent = false; }); From ffb2f4f5db2a14719fb038025bee96a862779a94 Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Wed, 4 Oct 2023 12:23:45 +0200 Subject: [PATCH 2/7] refactor: Use unauthorized user describe block for getUser tests --- .../src/graphql/queries/get-user.test.ts | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/backend/src/graphql/queries/get-user.test.ts b/packages/backend/src/graphql/queries/get-user.test.ts index fd266f13..818d0baf 100644 --- a/packages/backend/src/graphql/queries/get-user.test.ts +++ b/packages/backend/src/graphql/queries/get-user.test.ts @@ -4,26 +4,28 @@ import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id' import Crypto from 'crypto'; describe('getUser', () => { - it('should throw not authorized error for an unauthorized user', async () => { - const invalidUserId = '123123123'; + describe('with unauthorized user', () => { + it('should throw not authorized error', async () => { + const invalidUserId = '123123123'; - const query = ` - query { - getUser(id: "${invalidUserId}") { - id - email + const query = ` + query { + getUser(id: "${invalidUserId}") { + id + email + } } - } - `; + `; - const response = await request(app) - .post('/graphql') - .set('Authorization', 'invalid-token') - .send({ query }) - .expect(200); + const response = await request(app) + .post('/graphql') + .set('Authorization', 'invalid-token') + .send({ query }) + .expect(200); - expect(response.body.errors).toBeDefined(); - expect(response.body.errors[0].message).toEqual('Not Authorised!'); + expect(response.body.errors).toBeDefined(); + expect(response.body.errors[0].message).toEqual('Not Authorised!'); + }); }); describe('with authorized user', () => { From a29b3c6db4fdd95020d5e33c85694dbe22ca135a Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Wed, 4 Oct 2023 18:15:55 +0200 Subject: [PATCH 3/7] refactor: Use fixtures for getUser graphQL tests --- .../src/graphql/queries/get-user.test.ts | 105 ++++++------------ packages/backend/test/fixtures/permission.ts | 24 ++++ packages/backend/test/fixtures/role.ts | 15 +++ packages/backend/test/fixtures/user.ts | 27 +++++ 4 files changed, 102 insertions(+), 69 deletions(-) create mode 100644 packages/backend/test/fixtures/permission.ts create mode 100644 packages/backend/test/fixtures/role.ts create mode 100644 packages/backend/test/fixtures/user.ts diff --git a/packages/backend/src/graphql/queries/get-user.test.ts b/packages/backend/src/graphql/queries/get-user.test.ts index 818d0baf..01f8c6e8 100644 --- a/packages/backend/src/graphql/queries/get-user.test.ts +++ b/packages/backend/src/graphql/queries/get-user.test.ts @@ -2,6 +2,9 @@ import request from 'supertest'; import app from '../../app'; import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id'; import Crypto from 'crypto'; +import createRole from '../../../test/fixtures/role'; +import createPermission from '../../../test/fixtures/permission'; +import createUser from '../../../test/fixtures/user'; describe('getUser', () => { describe('with unauthorized user', () => { @@ -29,41 +32,32 @@ describe('getUser', () => { }); describe('with authorized user', () => { - it('should return user data for a valid user id', async () => { - const [role] = await knex - .table('roles') - .insert({ - key: 'sample', - name: 'sample', - }) - .returning('*'); + let role: any, currentUser: any, anotherUser: any, token: any; - await knex.table('permissions').insert({ - action: 'read', - subject: 'User', - role_id: role.id, + beforeEach(async () => { + role = await createRole({ + key: 'sample', + name: 'sample', }); - const [currentUser] = await knex - .table('users') - .insert({ - full_name: 'Test User', - email: 'sample@sample.com', - password: 'secret', - role_id: role.id, - }) - .returning('*'); + await createPermission({ + action: 'read', + subject: 'User', + roleId: role.id, + }); - const [anotherUser] = await global.knex - .table('users') - .insert({ - full_name: 'Another User', - email: 'another@sample.com', - password: 'secret', - role_id: role.id, - }) - .returning('*'); + currentUser = await createUser({ + roleId: role.id, + }); + anotherUser = await createUser({ + roleId: role.id, + }); + + token = createAuthTokenByUserId(currentUser.id); + }); + + it('should return user data for a valid user id', async () => { const query = ` query { getUser(id: "${anotherUser.id}") { @@ -81,8 +75,6 @@ describe('getUser', () => { } `; - const token = createAuthTokenByUserId(currentUser.id); - const response = await request(app) .post('/graphql') .set('Authorization', `${token}`) @@ -106,49 +98,24 @@ describe('getUser', () => { }); it('should return not found for invalid user id', async () => { - const [role] = await knex('roles') - .insert({ - key: 'sample', - name: 'sample', - }) - .returning('*'); - - await knex.table('permissions').insert({ - action: 'read', - subject: 'User', - role_id: role.id, - }); - - const [currentUser] = await knex - .table('users') - .insert({ - full_name: 'Test User', - email: 'sample@sample.com', - password: 'secret', - role_id: role.id, - }) - .returning('*'); - const invalidUserId = Crypto.randomUUID(); const query = ` - query { - getUser(id: "${invalidUserId}") { + query { + getUser(id: "${invalidUserId}") { + id + email + fullName + email + createdAt + updatedAt + role { id - email - fullName - email - createdAt - updatedAt - role { - id - name - } + name } } - `; - - const token = createAuthTokenByUserId(currentUser.id); + } + `; const response = await request(app) .post('/graphql') diff --git a/packages/backend/test/fixtures/permission.ts b/packages/backend/test/fixtures/permission.ts new file mode 100644 index 00000000..398dd31a --- /dev/null +++ b/packages/backend/test/fixtures/permission.ts @@ -0,0 +1,24 @@ +import createRole from './role'; + +type PermissionParams = { + roleId?: string; + action?: string; + subject?: string; +}; + +const createPermission = async (params: PermissionParams = {}) => { + const permissionData = { + role_id: params?.roleId || (await createRole()).id, + action: params?.action || 'read', + subject: params?.subject || 'User', + }; + + const [permission] = await global.knex + .table('permissions') + .insert(permissionData) + .returning('*'); + + return permission; +}; + +export default createPermission; diff --git a/packages/backend/test/fixtures/role.ts b/packages/backend/test/fixtures/role.ts new file mode 100644 index 00000000..b6124116 --- /dev/null +++ b/packages/backend/test/fixtures/role.ts @@ -0,0 +1,15 @@ +type RoleParams = { + name?: string; + key?: string; +}; + +const createRole = async (params: RoleParams = {}) => { + params.name = params?.name || 'Viewer'; + params.key = params?.key || 'viewer'; + + const [role] = await knex.table('roles').insert(params).returning('*'); + + return role; +}; + +export default createRole; diff --git a/packages/backend/test/fixtures/user.ts b/packages/backend/test/fixtures/user.ts new file mode 100644 index 00000000..7bb9bd03 --- /dev/null +++ b/packages/backend/test/fixtures/user.ts @@ -0,0 +1,27 @@ +import createRole from './role'; +import { faker } from '@faker-js/faker'; + +type UserParams = { + roleId?: string; + fullName?: string; + email?: string; + password?: string; +}; + +const createUser = async (params: UserParams = {}) => { + const userData = { + role_id: params?.roleId || (await createRole()).id, + full_name: params?.fullName || faker.person.fullName(), + email: params?.email || faker.internet.email(), + password: params?.password || faker.internet.password(), + }; + + const [user] = await global.knex + .table('users') + .insert(userData) + .returning('*'); + + return user; +}; + +export default createUser; From b290c32aebfbd38cfa7a2f1de6b74abacbcf8696 Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Wed, 4 Oct 2023 18:38:47 +0200 Subject: [PATCH 4/7] refactor: Use shared requestObject for getUser tests --- .../src/graphql/queries/get-user.test.ts | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/graphql/queries/get-user.test.ts b/packages/backend/src/graphql/queries/get-user.test.ts index 01f8c6e8..4c427c7b 100644 --- a/packages/backend/src/graphql/queries/get-user.test.ts +++ b/packages/backend/src/graphql/queries/get-user.test.ts @@ -32,7 +32,11 @@ describe('getUser', () => { }); describe('with authorized user', () => { - let role: any, currentUser: any, anotherUser: any, token: any; + let role: any, + currentUser: any, + anotherUser: any, + token: any, + requestObject: any; beforeEach(async () => { role = await createRole({ @@ -55,6 +59,9 @@ describe('getUser', () => { }); token = createAuthTokenByUserId(currentUser.id); + requestObject = request(app) + .post('/graphql') + .set('Authorization', `${token}`); }); it('should return user data for a valid user id', async () => { @@ -75,11 +82,7 @@ describe('getUser', () => { } `; - const response = await request(app) - .post('/graphql') - .set('Authorization', `${token}`) - .send({ query }) - .expect(200); + const response = await requestObject.send({ query }).expect(200); const expectedResponsePayload = { data: { @@ -117,11 +120,7 @@ describe('getUser', () => { } `; - const response = await request(app) - .post('/graphql') - .set('Authorization', `${token}`) - .send({ query }) - .expect(200); + const response = await requestObject.send({ query }).expect(200); expect(response.body.errors).toBeDefined(); expect(response.body.errors[0].message).toEqual('NotFoundError'); From b5df1a026a5156fa068865cdf821e64b03949287 Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Wed, 4 Oct 2023 20:41:08 +0200 Subject: [PATCH 5/7] chore: Use snake case test mappers for test env --- packages/backend/knexfile.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/knexfile.ts b/packages/backend/knexfile.ts index 4847933e..5a4d0a48 100644 --- a/packages/backend/knexfile.ts +++ b/packages/backend/knexfile.ts @@ -1,3 +1,4 @@ +import { knexSnakeCaseMappers } from 'objection'; import appConfig from './src/config/app'; const fileExtension = appConfig.isDev || appConfig.isTest ? 'ts' : 'js'; @@ -23,6 +24,7 @@ const knexConfig = { seeds: { directory: __dirname + '/src/db/seeds', }, + ...(appConfig.isTest ? knexSnakeCaseMappers() : {}), }; export default knexConfig; From 1683c5630af6780fbbe4624be7c1ce9cf5f8c861 Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Wed, 4 Oct 2023 20:42:47 +0200 Subject: [PATCH 6/7] test: Add types to getUser test file --- .../src/graphql/queries/get-user.test.ts | 19 ++++++++++--------- packages/backend/test/fixtures/permission.ts | 7 +++++-- packages/backend/test/fixtures/role.ts | 4 +++- packages/backend/test/fixtures/user.ts | 4 ++-- packages/types/index.d.ts | 2 ++ 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/packages/backend/src/graphql/queries/get-user.test.ts b/packages/backend/src/graphql/queries/get-user.test.ts index 4c427c7b..071fce5b 100644 --- a/packages/backend/src/graphql/queries/get-user.test.ts +++ b/packages/backend/src/graphql/queries/get-user.test.ts @@ -1,10 +1,11 @@ -import request from 'supertest'; +import request, { Test } from 'supertest'; import app from '../../app'; import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id'; import Crypto from 'crypto'; import createRole from '../../../test/fixtures/role'; import createPermission from '../../../test/fixtures/permission'; import createUser from '../../../test/fixtures/user'; +import { IRole, IUser } from '@automatisch/types'; describe('getUser', () => { describe('with unauthorized user', () => { @@ -32,11 +33,11 @@ describe('getUser', () => { }); describe('with authorized user', () => { - let role: any, - currentUser: any, - anotherUser: any, - token: any, - requestObject: any; + let role: IRole, + currentUser: IUser, + anotherUser: IUser, + token: string, + requestObject: Test; beforeEach(async () => { role = await createRole({ @@ -87,12 +88,12 @@ describe('getUser', () => { const expectedResponsePayload = { data: { getUser: { - createdAt: anotherUser.created_at.getTime().toString(), + createdAt: (anotherUser.createdAt as Date).getTime().toString(), email: anotherUser.email, - fullName: anotherUser.full_name, + fullName: anotherUser.fullName, id: anotherUser.id, role: { id: role.id, name: role.name }, - updatedAt: anotherUser.updated_at.getTime().toString(), + updatedAt: (anotherUser.updatedAt as Date).getTime().toString(), }, }, }; diff --git a/packages/backend/test/fixtures/permission.ts b/packages/backend/test/fixtures/permission.ts index 398dd31a..e5232674 100644 --- a/packages/backend/test/fixtures/permission.ts +++ b/packages/backend/test/fixtures/permission.ts @@ -1,3 +1,4 @@ +import { IPermission } from '@automatisch/types'; import createRole from './role'; type PermissionParams = { @@ -6,9 +7,11 @@ type PermissionParams = { subject?: string; }; -const createPermission = async (params: PermissionParams = {}) => { +const createPermission = async ( + params: PermissionParams = {} +): Promise => { const permissionData = { - role_id: params?.roleId || (await createRole()).id, + roleId: params?.roleId || (await createRole()).id, action: params?.action || 'read', subject: params?.subject || 'User', }; diff --git a/packages/backend/test/fixtures/role.ts b/packages/backend/test/fixtures/role.ts index b6124116..dc568c3d 100644 --- a/packages/backend/test/fixtures/role.ts +++ b/packages/backend/test/fixtures/role.ts @@ -1,9 +1,11 @@ +import { IRole } from '@automatisch/types'; + type RoleParams = { name?: string; key?: string; }; -const createRole = async (params: RoleParams = {}) => { +const createRole = async (params: RoleParams = {}): Promise => { params.name = params?.name || 'Viewer'; params.key = params?.key || 'viewer'; diff --git a/packages/backend/test/fixtures/user.ts b/packages/backend/test/fixtures/user.ts index 7bb9bd03..f7aaeab6 100644 --- a/packages/backend/test/fixtures/user.ts +++ b/packages/backend/test/fixtures/user.ts @@ -10,8 +10,8 @@ type UserParams = { const createUser = async (params: UserParams = {}) => { const userData = { - role_id: params?.roleId || (await createRole()).id, - full_name: params?.fullName || faker.person.fullName(), + roleId: params?.roleId || (await createRole()).id, + fullName: params?.fullName || faker.person.fullName(), email: params?.email || faker.internet.email(), password: params?.password || faker.internet.password(), }; diff --git a/packages/types/index.d.ts b/packages/types/index.d.ts index 74f49837..228558e3 100644 --- a/packages/types/index.d.ts +++ b/packages/types/index.d.ts @@ -99,6 +99,8 @@ export interface IUser { steps: IStep[]; role: IRole; permissions: IPermission[]; + createdAt: string | Date; + updatedAt: string | Date; } export interface IRole { From bd497af89b8f6cf3e9118d588c9b61b5df8c67df Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Wed, 4 Oct 2023 20:45:55 +0200 Subject: [PATCH 7/7] test: Add case to getUser to not return user password --- .../src/graphql/queries/get-user.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/backend/src/graphql/queries/get-user.test.ts b/packages/backend/src/graphql/queries/get-user.test.ts index 071fce5b..a9ba4ea4 100644 --- a/packages/backend/src/graphql/queries/get-user.test.ts +++ b/packages/backend/src/graphql/queries/get-user.test.ts @@ -101,6 +101,25 @@ describe('getUser', () => { expect(response.body).toEqual(expectedResponsePayload); }); + it('should not return user password for a valid user id', async () => { + const query = ` + query { + getUser(id: "${anotherUser.id}") { + id + email + password + } + } + `; + + const response = await requestObject.send({ query }).expect(400); + + expect(response.body.errors).toBeDefined(); + expect(response.body.errors[0].message).toEqual( + 'Cannot query field "password" on type "User".' + ); + }); + it('should return not found for invalid user id', async () => { const invalidUserId = Crypto.randomUUID();