diff --git a/backend/src/graphql/resolvers/badges.spec.ts b/backend/src/graphql/resolvers/badges.spec.ts index 839727a9d..6c58de257 100644 --- a/backend/src/graphql/resolvers/badges.spec.ts +++ b/backend/src/graphql/resolvers/badges.spec.ts @@ -165,7 +165,7 @@ describe('Badges', () => { errors: [ { message: - 'Error: Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.', + 'UserInputError: Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.', }, ], }) @@ -184,7 +184,7 @@ describe('Badges', () => { errors: [ { message: - 'Error: Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.', + 'UserInputError: Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.', }, ], }) @@ -203,7 +203,7 @@ describe('Badges', () => { errors: [ { message: - 'Error: Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.', + 'UserInputError: Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.', }, ], }) diff --git a/backend/src/graphql/resolvers/badges.ts b/backend/src/graphql/resolvers/badges.ts index f3faf1fe4..0b840bb7c 100644 --- a/backend/src/graphql/resolvers/badges.ts +++ b/backend/src/graphql/resolvers/badges.ts @@ -4,6 +4,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { UserInputError } from 'apollo-server' import { neo4jgraphql } from 'neo4j-graphql-js' import { TROPHY_BADGES_SELECTED_MAX } from '@constants/badges' @@ -62,10 +63,7 @@ export default { try { const { relation, user } = await writeTxResultPromise if (!relation) { - context.logger.error( - 'Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.', - ) - throw new Error( + throw new UserInputError( 'Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.', ) } @@ -129,10 +127,7 @@ export default { ).records.map((record) => record.get('user')) if (users.length !== 1) { - context.logger.error( - 'Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.', - ) - throw new Error( + throw new UserInputError( 'Could not reward badge! Ensure the user and the badge exist and the badge is of the correct type.', ) } @@ -140,7 +135,7 @@ export default { return users[0] }, - revokeBadge: async (_object, args, context, _resolveInfo) => { + revokeBadge: async (_object, args, context: Context, _resolveInfo) => { const { badgeId, userId } = args const session = context.driver.session() @@ -167,7 +162,7 @@ export default { context.logger.error('revokeBadge', error) throw new Error(error) } finally { - session.close() + await session.close() } }, }, diff --git a/backend/src/graphql/resolvers/emails.spec.ts b/backend/src/graphql/resolvers/emails.spec.ts index 367e1102a..240b76d37 100644 --- a/backend/src/graphql/resolvers/emails.spec.ts +++ b/backend/src/graphql/resolvers/emails.spec.ts @@ -1,22 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/await-thenable */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getDriver, getNeode } from '@db/neo4j' -import createServer from '@src/server' - -const neode = getNeode() +import createServer, { getContext } from '@src/server' let mutate, query let authenticatedUser let user let variables -const driver = getDriver() + +const database = databaseContext() + +let server: ApolloServer const loggerErrorMock: (e) => void = jest.fn() @@ -27,22 +30,21 @@ jest.mock('@src/logger', () => ({ beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - } - }, - }) - mutate = createTestClient(server).mutate - query = createTestClient(server).query + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + + const createTestClientResult = createTestClient(server) + query = createTestClientResult.query + mutate = createTestClientResult.mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) beforeEach(async () => { @@ -116,7 +118,7 @@ describe('AddEmailAddress', () => { it('connects `UnverifiedEmailAddress` to the authenticated user', async () => { await mutate({ mutation, variables }) - const result = await neode.cypher( + const result = await database.neode.cypher( ` MATCH(u:User)-[:PRIMARY_EMAIL]->(:EmailAddress {email: "user@example.org"}) MATCH(u:User)<-[:BELONGS_TO]-(e:UnverifiedEmailAddress {email: "new-email@example.org"}) @@ -124,7 +126,11 @@ describe('AddEmailAddress', () => { `, {}, ) - const email = neode.hydrateFirst(result, 'e', neode.model('UnverifiedEmailAddress')) + const email = database.neode.hydrateFirst( + result, + 'e', + database.neode.model('UnverifiedEmailAddress'), + ) await expect(email.toJson()).resolves.toMatchObject({ email: 'new-email@example.org', nonce: expect.any(String), @@ -266,14 +272,18 @@ describe('VerifyEmailAddress', () => { it('connects the new `EmailAddress` as PRIMARY', async () => { await mutate({ mutation, variables }) - const result = await neode.cypher( + const result = await database.neode.cypher( ` MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "to-be-verified@example.org"}) RETURN e `, {}, ) - const email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) + const email = database.neode.hydrateFirst( + result, + 'e', + database.neode.model('EmailAddress'), + ) await expect(email.toJson()).resolves.toMatchObject({ email: 'to-be-verified@example.org', }) @@ -284,14 +294,18 @@ describe('VerifyEmailAddress', () => { MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "user@example.org"}) RETURN e ` - let result = await neode.cypher(cypherStatement, {}) - let email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) + let result = await database.neode.cypher(cypherStatement, {}) + let email = database.neode.hydrateFirst( + result, + 'e', + database.neode.model('EmailAddress'), + ) await expect(email.toJson()).resolves.toMatchObject({ email: 'user@example.org', }) await mutate({ mutation, variables }) - result = await neode.cypher(cypherStatement, {}) - email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) + result = await database.neode.cypher(cypherStatement, {}) + email = database.neode.hydrateFirst(result, 'e', database.neode.model('EmailAddress')) await expect(email).toBe(false) }) @@ -300,14 +314,18 @@ describe('VerifyEmailAddress', () => { MATCH(u:User {id: "567"})<-[:BELONGS_TO]-(e:EmailAddress {email: "user@example.org"}) RETURN e ` - let result = await neode.cypher(cypherStatement, {}) - let email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) + let result = await database.neode.cypher(cypherStatement, {}) + let email = database.neode.hydrateFirst( + result, + 'e', + database.neode.model('EmailAddress'), + ) await expect(email.toJson()).resolves.toMatchObject({ email: 'user@example.org', }) await mutate({ mutation, variables }) - result = await neode.cypher(cypherStatement, {}) - email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) + result = await database.neode.cypher(cypherStatement, {}) + email = database.neode.hydrateFirst(result, 'e', database.neode.model('EmailAddress')) await expect(email).toBe(false) }) @@ -331,14 +349,18 @@ describe('VerifyEmailAddress', () => { it('connects the new `EmailAddress` as PRIMARY', async () => { await mutate({ mutation, variables }) - const result = await neode.cypher( + const result = await database.neode.cypher( ` MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "to-be-verified@example.org"}) RETURN e `, {}, ) - const email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) + const email = database.neode.hydrateFirst( + result, + 'e', + database.neode.model('EmailAddress'), + ) await expect(email.toJson()).resolves.toMatchObject({ email: 'to-be-verified@example.org', }) diff --git a/backend/src/graphql/resolvers/emails.ts b/backend/src/graphql/resolvers/emails.ts index 3b7b13a00..f52f451d6 100644 --- a/backend/src/graphql/resolvers/emails.ts +++ b/backend/src/graphql/resolvers/emails.ts @@ -16,7 +16,7 @@ import Resolver from './helpers/Resolver' export default { Query: { - VerifyNonce: async (_parent, args, context, _resolveInfo) => { + VerifyNonce: async (_parent, args, context: Context, _resolveInfo) => { args.email = normalizeEmail(args.email) const session = context.driver.session() const readTxResultPromise = session.readTransaction(async (txc) => { @@ -32,8 +32,11 @@ export default { try { const txResult = await readTxResultPromise return txResult.records[0].get('result') + } catch (e) { + context.logger.error('VerifyNonce query', e) + throw new Error(e.message) } finally { - session.close() + await session.close() } }, }, @@ -45,7 +48,6 @@ export default { const { neode } = context await new Validator(neode, neode.model('UnverifiedEmailAddress'), args) } catch (e) { - // context.logger.error('must be a valid email') throw new UserInputError('must be a valid email') } @@ -79,6 +81,9 @@ export default { try { const txResult = await writeTxResultPromise response = txResult[0] + } catch (e) { + context.logger.error('AddEmailAddress mutation', e) + throw new Error(e.message) } finally { await session.close() } @@ -115,16 +120,14 @@ export default { response = txResult[0] } catch (e) { if (e.code === 'Neo.ClientError.Schema.ConstraintValidationFailed') { - // context.logger.error('A user account with this email already exists.') throw new UserInputError('A user account with this email already exists.') } - // context.logger.error('VerifyEmailAddress', e) + context.logger.error('VerifyEmailAddress', e) throw new Error(e) } finally { await session.close() } if (!response) { - // context.logger.error('Invalid nonce or no email address found.') throw new UserInputError('Invalid nonce or no email address found.') } return response