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; diff --git a/packages/backend/src/graphql/queries/get-user.test.ts b/packages/backend/src/graphql/queries/get-user.test.ts index fd266f13..a9ba4ea4 100644 --- a/packages/backend/src/graphql/queries/get-user.test.ts +++ b/packages/backend/src/graphql/queries/get-user.test.ts @@ -1,67 +1,71 @@ -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', () => { - 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', () => { - 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: IRole, + currentUser: IUser, + anotherUser: IUser, + token: string, + requestObject: Test; - 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); + requestObject = request(app) + .post('/graphql') + .set('Authorization', `${token}`); + }); + + it('should return user data for a valid user id', async () => { const query = ` query { getUser(id: "${anotherUser.id}") { @@ -79,23 +83,17 @@ describe('getUser', () => { } `; - const token = createAuthTokenByUserId(currentUser.id); - - 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: { 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(), }, }, }; @@ -103,56 +101,46 @@ 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 [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') - .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'); diff --git a/packages/backend/test/fixtures/permission.ts b/packages/backend/test/fixtures/permission.ts new file mode 100644 index 00000000..e5232674 --- /dev/null +++ b/packages/backend/test/fixtures/permission.ts @@ -0,0 +1,27 @@ +import { IPermission } from '@automatisch/types'; +import createRole from './role'; + +type PermissionParams = { + roleId?: string; + action?: string; + subject?: string; +}; + +const createPermission = async ( + params: PermissionParams = {} +): Promise => { + const permissionData = { + roleId: 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..dc568c3d --- /dev/null +++ b/packages/backend/test/fixtures/role.ts @@ -0,0 +1,17 @@ +import { IRole } from '@automatisch/types'; + +type RoleParams = { + name?: string; + key?: string; +}; + +const createRole = async (params: RoleParams = {}): Promise => { + 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..f7aaeab6 --- /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 = { + roleId: params?.roleId || (await createRole()).id, + fullName: 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; 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; }); 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 {