From 35b90d9dcb5736ec1914ae2d0a8dbc0388a67818 Mon Sep 17 00:00:00 2001 From: oddgrd Date: Sun, 27 Mar 2022 23:24:16 +0200 Subject: [PATCH] Reorganize and decouple tests (WIP), move passport config --- server/src/config/passport.ts | 51 ++++++++ server/src/index.ts | 49 +------- server/src/resolvers/ascent/index.test.ts | 81 +++++-------- server/src/resolvers/board/index.test.ts | 85 ++++---------- server/src/resolvers/problem/index.test.ts | 125 +++++--------------- server/src/resolvers/user/index.test.ts | 60 +++------- server/src/test-utils/gqlMutations.ts | 129 +++++++++++++++++++++ server/src/test-utils/utils.ts | 105 +++++++++++++++++ 8 files changed, 391 insertions(+), 294 deletions(-) create mode 100644 server/src/config/passport.ts create mode 100644 server/src/test-utils/gqlMutations.ts create mode 100644 server/src/test-utils/utils.ts diff --git a/server/src/config/passport.ts b/server/src/config/passport.ts new file mode 100644 index 0000000..02b0e5b --- /dev/null +++ b/server/src/config/passport.ts @@ -0,0 +1,51 @@ +import passport from 'passport'; +import { Strategy } from 'passport-google-oauth20'; +import { __prod__ } from '../constants'; +import { User } from '../entities/User'; + +passport.use( + new Strategy( + { + clientID: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + callbackURL: __prod__ + ? `https://api.myhomeboard.no/api/auth/google/callback` + : 'http://localhost:4000/api/auth/google/callback', + }, + async (_accessToken, _refreshToken, profile: any, done) => { + const user = await User.findOne({ where: { googleId: profile.id } }); + if (user) { + await User.update( + { id: user.id }, + { + name: profile.displayName, + avatar: profile._json.picture, + } + ); + done(null, user); + } else { + try { + const newUser = await User.create({ + name: profile.displayName, + email: profile._json.email, + avatar: profile._json.picture, + googleId: profile.id, + }).save(); + done(null, newUser); + } catch (error) { + done(error); + } + } + } + ) + ); + + passport.serializeUser(function (user: any, done) { + done(null, user.id); + }); + + passport.deserializeUser((id: string, done) => { + User.findOne(id).then((user) => { + done(null, user); + }); + }); \ No newline at end of file diff --git a/server/src/index.ts b/server/src/index.ts index a486747..13af20b 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -7,7 +7,6 @@ import session from 'express-session'; import { graphqlUploadExpress } from 'graphql-upload'; import Redis from 'ioredis'; import passport from 'passport'; -import { Strategy } from 'passport-google-oauth20'; import path from 'path'; import 'reflect-metadata'; import { createConnection } from 'typeorm'; @@ -22,6 +21,9 @@ import { createLayoutLoader } from './utils/createLayoutLoader'; import { createSchema } from './utils/createSchema'; import { createUserLoader } from './utils/createUserLoader'; +// Passport configuration +import "./config/passport"; + const main = async () => { const connection = await createConnection({ applicationName: 'myhomeboard', @@ -74,51 +76,6 @@ const main = async () => { }), }); - passport.use( - new Strategy( - { - clientID: process.env.GOOGLE_CLIENT_ID, - clientSecret: process.env.GOOGLE_CLIENT_SECRET, - callbackURL: __prod__ - ? `https://api.myhomeboard.no/api/auth/google/callback` - : 'http://localhost:4000/api/auth/google/callback', - }, - async (_accessToken, _refreshToken, profile: any, done) => { - const user = await User.findOne({ where: { googleId: profile.id } }); - if (user) { - await User.update( - { id: user.id }, - { - name: profile.displayName, - avatar: profile._json.picture, - } - ); - done(null, user); - } else { - try { - const newUser = await User.create({ - name: profile.displayName, - email: profile._json.email, - avatar: profile._json.picture, - googleId: profile.id, - }).save(); - done(null, newUser); - } catch (error) { - done(error); - } - } - } - ) - ); - passport.serializeUser(function (user: any, done) { - done(null, user.id); - }); - passport.deserializeUser((id: string, done) => { - User.findOne(id).then((user) => { - done(null, user); - }); - }); - app.use( session({ name: 'mhb', diff --git a/server/src/resolvers/ascent/index.test.ts b/server/src/resolvers/ascent/index.test.ts index 2997e9c..eb3e688 100644 --- a/server/src/resolvers/ascent/index.test.ts +++ b/server/src/resolvers/ascent/index.test.ts @@ -1,65 +1,35 @@ import faker from 'faker'; -import { User } from '../../entities/User'; import { gqlWrapper } from '../../test-utils/gqlWrapper'; -import { Board } from '../../entities/Board'; -import { Problem } from '../../entities/Problem'; import { Ascent } from '../../entities/Ascent'; - -const addAscentMutation = ` -mutation AddAscent($options: AddAscentInput!) { - addAscent(options: $options) -} -`; -const editAscentMutation = ` -mutation EditAscent($options: EditAscentInput!) { - editAscent(options: $options) -} - -`; -const deleteAscentMutation = ` -mutation DeleteAscent($problemId: String!) { - deleteAscent(problemId: $problemId) -} -`; +import { deleteAscentMutation, editAscentMutation } from '../../test-utils/gqlMutations'; +import { addAscent, getDummyData } from '../../test-utils/utils'; describe('Ascent tests', () => { - let userId: string; - let problemId: string; it('adds ascent', async () => { - // Get user, board and problem ids from dummy data - const user = await User.findOne({ where: { name: 'odd' } }); - userId = user!.id; - const board = await Board.findOne({ where: { title: 'test' } }); - const problem = await Problem.findOne({ where: { title: 'testproblem' } }); - problemId = problem!.id; + const {response, ascentInput } = await addAscent(); - const ascentInput = { - problemId, - boardId: board!.id, - comment: faker.lorem.sentence(3), - grade: 10, - rating: 2, - attempts: 6, - }; - const response = await gqlWrapper({ - source: addAscentMutation, - variableValues: { - options: ascentInput, - }, - userId: userId, - }); expect(response).toMatchObject({ data: { addAscent: true, }, }); - const ascent = await Ascent.findOne({ where: { problemId } }); + const ascent = await Ascent.findOne({ where: { problemId: ascentInput.problemId } }); expect(ascent).toBeDefined(); }); + it('edits ascent', async () => { + const {user} = await getDummyData(); + + const {response: addAscentResponse, ascentInput } = await addAscent(); + expect(addAscentResponse).toMatchObject({ + data: { + addAscent: true, + }, + }); + const editAscentInput = { - problemId, + problemId: ascentInput.problemId, comment: faker.lorem.sentence(3), grade: 5, rating: 0, @@ -70,25 +40,36 @@ describe('Ascent tests', () => { variableValues: { options: editAscentInput, }, - userId: userId, + userId: user?.id, }); + expect(response).toMatchObject({ data: { editAscent: true, }, }); - const ascent = await Ascent.findOne({ where: { problemId } }); + const ascent = await Ascent.findOne({ where: { problemId: ascentInput.problemId } }); expect(ascent).toBeDefined(); expect(ascent).toMatchObject(editAscentInput); }); + it('deletes ascent', async () => { + const {user} = await getDummyData(); + + const {response: addAscentResponse, ascentInput } = await addAscent(); + expect(addAscentResponse).toMatchObject({ + data: { + addAscent: true, + }, + }); + const response = await gqlWrapper({ source: deleteAscentMutation, variableValues: { - problemId, + problemId: ascentInput.problemId, }, - userId: userId, + userId: user?.id, }); expect(response).toMatchObject({ data: { @@ -96,7 +77,7 @@ describe('Ascent tests', () => { }, }); - const ascent = await Ascent.findOne({ where: { problemId } }); + const ascent = await Ascent.findOne({ where: { problemId: ascentInput.problemId } }); expect(ascent).toBeUndefined(); }); }); diff --git a/server/src/resolvers/board/index.test.ts b/server/src/resolvers/board/index.test.ts index 41ae7ce..0852a3b 100644 --- a/server/src/resolvers/board/index.test.ts +++ b/server/src/resolvers/board/index.test.ts @@ -1,66 +1,13 @@ import faker from 'faker'; -import { User } from '../../entities/User'; import { gqlWrapper } from '../../test-utils/gqlWrapper'; import { Board } from '../../entities/Board'; - -const createBoardMutation = ` - mutation CreateBoard($options: BoardInput!) { - createBoard(options: $options) { - board { - id - title - } - errors { - message - field - } - } - } -`; -const editBoardMutation = ` - mutation EditBoard($options: EditBoardInput!) { - editBoard(options: $options) { - board { - id - title - angles - adjustable - } - errors { - message - field - } - } - } -`; -const deleteBoardMutation = ` - mutation DeleteBoard($boardId: String!) { - deleteBoard(boardId: $boardId) - } -`; +import { deleteBoardMutation, editBoardMutation } from '../../test-utils/gqlMutations'; +import { createBoard, getDummyData } from '../../test-utils/utils'; describe('Board tests', () => { - let userId: string; - let boardId: string; it('should create board', async () => { - // get user from dummy data - const user = await User.findOne({ where: { name: 'odd' } }); - userId = user!.id; - const boardInput = { - title: faker.lorem.sentence(2), - description: faker.lorem.sentence(3), - adjustable: true, - angles: [20, 30], - city: faker.address.city(), - country: faker.address.country(), - }; - const response = await gqlWrapper({ - source: createBoardMutation, - variableValues: { - options: boardInput, - }, - userId, - }); + const {response, boardInput} = await createBoard(); + expect(response).toMatchObject({ data: { createBoard: { @@ -71,12 +18,18 @@ describe('Board tests', () => { }, }, }); - boardId = response.data!.createBoard.board.id; - const board = await Board.findOne(boardId); + + const board = await Board.findOne(response.data!.createBoard.board.id); expect(board).toBeDefined(); }); it('should edit board', async () => { + const {user} = await getDummyData(); + + const {response: createBoardResponse} = await createBoard(); + let boardId = createBoardResponse.data!.createBoard.board.id; + expect(boardId).toBeDefined(); + const editBoardInput = { boardId, title: faker.lorem.sentence(2), @@ -91,8 +44,9 @@ describe('Board tests', () => { variableValues: { options: editBoardInput, }, - userId, + userId: user?.id, }); + expect(response).toMatchObject({ data: { editBoard: { @@ -105,20 +59,29 @@ describe('Board tests', () => { }, }, }); + const board = await Board.findOne(boardId); + expect(board).toBeDefined(); expect(board!.adjustable).toBeFalsy(); expect(board!.angles.length).toBe(1); }); it('should delete board', async () => { + const {user} = await getDummyData(); + + const {response: createBoardResponse} = await createBoard(); + let boardId = createBoardResponse.data?.createBoard.board.id; + expect(boardId).toBeDefined(); + const response = await gqlWrapper({ source: deleteBoardMutation, variableValues: { boardId, }, - userId: userId, + userId: user?.id, }); + expect(response).toMatchObject({ data: { deleteBoard: true, diff --git a/server/src/resolvers/problem/index.test.ts b/server/src/resolvers/problem/index.test.ts index 69c278c..a068204 100644 --- a/server/src/resolvers/problem/index.test.ts +++ b/server/src/resolvers/problem/index.test.ts @@ -1,101 +1,12 @@ import faker from 'faker'; -import { User } from '../../entities/User'; import { gqlWrapper } from '../../test-utils/gqlWrapper'; -import { Board } from '../../entities/Board'; import { Problem } from '../../entities/Problem'; -import { Layout } from '../../entities/Layout'; - -const createProblemMutation = ` - mutation CreateProblem($options: CreateProblemInput!) { - createProblem(options: $options) { - problem { - title - rules - id - layoutId - boardId - grade - angle - coordinates { - x - y - color - } - } - errors { - field - message - } - } - } -`; -const editProblemMutation = ` - mutation EditProblem($options: EditProblemInput!) { - editProblem(options: $options) { - problem { - id - title - grade - angle - rules - } - errors { - field - message - } - } - } -`; - -const deleteProblemMutation = ` - mutation DeleteProblem($id: String!) { - deleteProblem(id: $id) - } -`; +import { createProblem, getDummyData } from '../../test-utils/utils'; +import { deleteProblemMutation, editProblemMutation } from '../../test-utils/gqlMutations'; describe('Problem tests', () => { - let userId: string; - let problemId: string; it('should create problem', async () => { - // Layout, user and board from dummydata - const layout = await Layout.findOne({ where: { title: 'testlayout' } }); - const user = await User.findOne({ where: { name: 'odd' } }); - const board = await Board.findOne({ where: { title: 'test' } }); - userId = user!.id; - - const problemInput = { - title: faker.name.firstName(), - rules: faker.lorem.sentence(3), - layoutId: layout!.id, - boardId: board!.id, - grade: 10, - angle: 30, - coordinates: [ - { - x: 200, - y: 150, - color: 'red', - }, - { - x: 100, - y: 180, - color: 'green', - }, - { - x: 80, - y: 200, - color: 'blue', - }, - ], - }; - - const response = await gqlWrapper({ - source: createProblemMutation, - variableValues: { - options: problemInput, - }, - userId: userId, - }); + const { response, problemInput } = await createProblem(); expect(response).toMatchObject({ data: { @@ -105,13 +16,23 @@ describe('Problem tests', () => { }, }, }); - problemId = response.data!.createProblem.problem.id; + + let problemId = response.data!.createProblem.problem.id; const problem = await Problem.findOne(problemId); + expect(problem).toBeDefined(); expect(problem!.title).toMatch(problemInput.title); }); it('should edit problem', async () => { + const { response: createProblemResponse } = + await createProblem(); + expect(createProblemResponse.data).toBeDefined(); + + let problemId = createProblemResponse.data!.createProblem.problem.id; + const { user } = await getDummyData(); + expect(user?.id).toBeDefined(); + const editProblemInput = { problemId, title: 'edited', @@ -119,13 +40,15 @@ describe('Problem tests', () => { grade: 5, angle: 30, }; + const response = await gqlWrapper({ source: editProblemMutation, variableValues: { options: editProblemInput, }, - userId: userId, + userId: user?.id, }); + expect(response).toMatchObject({ data: { editProblem: { @@ -139,19 +62,29 @@ describe('Problem tests', () => { }, }, }); + const problem = await Problem.findOne(problemId); expect(problem).toBeDefined(); expect(problem!.title).toMatch('edited'); }); it('should delete problem', async () => { + const { response: createProblemResponse } = + await createProblem(); + expect(createProblemResponse.data).toBeDefined(); + + let problemId = createProblemResponse.data!.createProblem.problem.id; + const { user } = await getDummyData(); + expect(user?.id).toBeDefined(); + const response = await gqlWrapper({ source: deleteProblemMutation, variableValues: { id: problemId, }, - userId, + userId: user?.id, }); + expect(response).toMatchObject({ data: { deleteProblem: true, @@ -161,4 +94,4 @@ describe('Problem tests', () => { const problem = await Problem.findOne({ where: { id: problemId } }); expect(problem).toBeUndefined(); }); -}); +}); \ No newline at end of file diff --git a/server/src/resolvers/user/index.test.ts b/server/src/resolvers/user/index.test.ts index dd95170..f6e972c 100644 --- a/server/src/resolvers/user/index.test.ts +++ b/server/src/resolvers/user/index.test.ts @@ -1,34 +1,10 @@ import faker from 'faker'; import { User } from '../../entities/User'; import { gqlWrapper } from '../../test-utils/gqlWrapper'; -import { Board } from '../../entities/Board'; - -const whitelistUserMutation = ` - mutation WhitelistUser($options: WhitelistInput!) { - whitelistUser(options: $options) { - errors { - field - message - } - userId - } - } -`; -const removeFromWhitelistMutation = ` - mutation RemoveFromWhitelist($options: WhitelistInput!) { - removeFromWhitelist(options: $options) { - errors { - field - message - } - userId - } - } -`; +import { removeFromWhitelistMutation, whitelistUserMutation } from '../../test-utils/gqlMutations'; +import { getDummyData } from '../../test-utils/utils'; describe('User tests', () => { - let userId: string; - let email: string; it('should create user', async () => { const newUser = await User.create({ name: faker.name.firstName(), @@ -36,20 +12,17 @@ describe('User tests', () => { avatar: faker.internet.url(), googleId: faker.random.alphaNumeric(8), }).save(); - [userId, email] = [newUser!.id, newUser!.email]; const user = await User.findOne(newUser!.id); expect(user).toBeDefined(); expect(user?.name).toMatch(newUser.name); }); - let boardId: string; it('should whitelist user', async () => { - // get board from dummy data - const board = await Board.findOne({ where: { title: 'test' } }); - boardId = board!.id; + const {user, boardId} = await getDummyData(); + const whitelistInput = { - email, + email: user?.email, boardId, }; @@ -58,22 +31,25 @@ describe('User tests', () => { variableValues: { options: whitelistInput, }, - userId, + userId: user?.id, }); expect(response).toMatchObject({ data: { whitelistUser: { errors: null, - userId, + userId: user?.id, }, }, }); - const whitelistedUser = await User.findOne(userId); + + const whitelistedUser = await User.findOne(user?.id); expect(whitelistedUser!.boardWhitelist).toContain(boardId); }); - it('should error on invalid user', async () => { + it('should error on whitelisting invalid user', async () => { + const {user, boardId} = await getDummyData(); + const whitelistInput = { email: faker.internet.email(), boardId, @@ -84,7 +60,7 @@ describe('User tests', () => { variableValues: { options: whitelistInput, }, - userId, + userId: user?.id, }); expect(response).toMatchObject({ @@ -103,8 +79,10 @@ describe('User tests', () => { }); it('should remove user from whitelist', async () => { + const {user, boardId} = await getDummyData(); + const whitelistInput = { - email, + email: user?.email, boardId, }; @@ -113,18 +91,18 @@ describe('User tests', () => { variableValues: { options: whitelistInput, }, - userId, + userId: user?.id, }); expect(response).toMatchObject({ data: { removeFromWhitelist: { errors: null, - userId, + userId: user?.id, }, }, }); - const whitelistedUser = await User.findOne(userId); + const whitelistedUser = await User.findOne(user?.id); expect(whitelistedUser!.boardWhitelist).not.toContain(boardId); }); }); diff --git a/server/src/test-utils/gqlMutations.ts b/server/src/test-utils/gqlMutations.ts new file mode 100644 index 0000000..353299c --- /dev/null +++ b/server/src/test-utils/gqlMutations.ts @@ -0,0 +1,129 @@ +export const whitelistUserMutation = ` + mutation WhitelistUser($options: WhitelistInput!) { + whitelistUser(options: $options) { + errors { + field + message + } + userId + } + } +`; + +export const removeFromWhitelistMutation = ` + mutation RemoveFromWhitelist($options: WhitelistInput!) { + removeFromWhitelist(options: $options) { + errors { + field + message + } + userId + } + } +`; + +export const createBoardMutation = ` + mutation CreateBoard($options: BoardInput!) { + createBoard(options: $options) { + board { + id + title + } + errors { + message + field + } + } + } +`; + +export const editBoardMutation = ` + mutation EditBoard($options: EditBoardInput!) { + editBoard(options: $options) { + board { + id + title + angles + adjustable + } + errors { + message + field + } + } + } +`; + +export const deleteBoardMutation = ` + mutation DeleteBoard($boardId: String!) { + deleteBoard(boardId: $boardId) + } +`; + +export const addAscentMutation = ` +mutation AddAscent($options: AddAscentInput!) { + addAscent(options: $options) +} +`; + +export const editAscentMutation = ` +mutation EditAscent($options: EditAscentInput!) { + editAscent(options: $options) +} + +`; + +export const deleteAscentMutation = ` +mutation DeleteAscent($problemId: String!) { + deleteAscent(problemId: $problemId) +} +`; + +export const createProblemMutation = ` + mutation CreateProblem($options: CreateProblemInput!) { + createProblem(options: $options) { + problem { + title + rules + id + layoutId + boardId + grade + angle + coordinates { + x + y + color + } + } + errors { + field + message + } + } + } +`; + +export const editProblemMutation = ` + mutation EditProblem($options: EditProblemInput!) { + editProblem(options: $options) { + problem { + id + title + grade + angle + rules + } + errors { + field + message + } + } + } +`; + +export const deleteProblemMutation = ` + mutation DeleteProblem($id: String!) { + deleteProblem(id: $id) + } +`; \ No newline at end of file diff --git a/server/src/test-utils/utils.ts b/server/src/test-utils/utils.ts new file mode 100644 index 0000000..f123935 --- /dev/null +++ b/server/src/test-utils/utils.ts @@ -0,0 +1,105 @@ +import faker from 'faker'; +import { addAscentMutation, createBoardMutation, createProblemMutation } from './gqlMutations'; +import { gqlWrapper } from './gqlWrapper'; +import { Layout } from '../entities/Layout'; +import { Board } from '../entities/Board'; +import { Problem } from '../entities/Problem'; +import { User } from '../entities/User'; + +export const getDummyData = async () => { + const user = await User.findOne({ where: { name: 'odd' } }); + const board = await Board.findOne({ where: { title: 'test' } }); + const layout = await Layout.findOne({ where: { title: 'testlayout' } }); + const problem = await Problem.findOne({ where: { title: 'testproblem' } }); + + return {user: user, boardId: board?.id, layoutId: layout?.id, problemId: problem?.id}; +} + +export const createProblem = async () => { + const {user, boardId, layoutId} = await getDummyData(); + + const problemInput = { + title: faker.name.firstName(), + rules: faker.lorem.sentence(3), + layoutId, + boardId, + grade: 10, + angle: 30, + coordinates: [ + { + x: 200, + y: 150, + color: 'red', + }, + { + x: 100, + y: 180, + color: 'green', + }, + { + x: 80, + y: 200, + color: 'blue', + }, + ], + }; + + const response = await gqlWrapper({ + source: createProblemMutation, + variableValues: { + options: problemInput, + }, + userId: user?.id, + }); + + return {response, problemInput}; +} + +export const createBoard = async () => { + const {user} = await getDummyData(); + + const boardInput = { + title: faker.lorem.sentence(2), + description: faker.lorem.sentence(3), + adjustable: true, + angles: [20, 30], + city: faker.address.city(), + country: faker.address.country(), + }; + + const response = await gqlWrapper({ + source: createBoardMutation, + variableValues: { + options: boardInput, + }, + userId: user?.id, + }); + + return {response, boardInput}; +} + +export const addAscent = async () => { + const {user, boardId } = await getDummyData(); + + // Create a new problem for each ascent test + let newProblem = await createProblem(); + + const ascentInput = { + problemId: newProblem.response.data!.createProblem.problem.id, + boardId, + comment: faker.lorem.sentence(3), + grade: 10, + rating: 2, + attempts: 6, + }; + + const response = await gqlWrapper({ + source: addAscentMutation, + variableValues: { + options: ascentInput, + }, + userId: user?.id, + }); + + return {response, ascentInput}; +} \ No newline at end of file