From 72d9f0bbb9c333c654d8bd1c50f9fcf272fb1a5f Mon Sep 17 00:00:00 2001 From: Rohan Port Date: Mon, 2 Oct 2023 15:10:01 +1100 Subject: [PATCH] RN-502: Fixed bugs and cleaned up tests --- .../src/connections/mocks/MockAuthApi.ts | 82 +++++++++++++------ .../src/connections/mocks/MockCentralApi.ts | 78 +++++++++++++++--- .../saveResponsesToDatabase.js | 4 +- .../src/tests/apiV2/surveyResponse.test.js | 37 +++++---- .../__integration__/AuthRoute.test.ts | 41 ++++++++-- .../ChangePasswordRoute.test.ts | 21 ++--- .../__integration__/RegisterUserRoute.test.ts | 9 +- .../__integration__/UserRewardsRoute.test.ts | 2 +- .../src/__tests__/__integration__/fixtures.ts | 19 +++-- .../socialFeed/SocialFeedRoute.test.ts | 2 +- .../sync/PushChangesRoute.test.ts | 35 +++----- .../pullChanges/ChangesMetadataRoute.test.ts | 2 +- .../pullChanges/CountChangesRoute.test.ts | 2 +- .../sync/pullChanges/PullChangesRoute.test.ts | 2 +- .../src/__tests__/utilities/AuthApiMock.ts | 51 ------------ .../src/__tests__/utilities/CentralApiMock.ts | 12 --- .../src/__tests__/utilities/index.ts | 2 - .../validateInboundSurveyResponses.ts | 12 ++- 18 files changed, 221 insertions(+), 192 deletions(-) delete mode 100644 packages/meditrak-app-server/src/__tests__/utilities/AuthApiMock.ts delete mode 100644 packages/meditrak-app-server/src/__tests__/utilities/CentralApiMock.ts diff --git a/packages/api-client/src/connections/mocks/MockAuthApi.ts b/packages/api-client/src/connections/mocks/MockAuthApi.ts index 23fa8409d0..67b7ede3ab 100644 --- a/packages/api-client/src/connections/mocks/MockAuthApi.ts +++ b/packages/api-client/src/connections/mocks/MockAuthApi.ts @@ -7,31 +7,65 @@ import { AuthApiInterface } from '..'; import { AccessPolicyObject } from '../../types'; +type User = { email: string; password: string; accessPolicy: AccessPolicyObject }; +type Session = { email: string; refreshToken: string; accessToken: string }; + export class MockAuthApi implements AuthApiInterface { - public login(authDetails: { - emailAddress: string; - password: string; - deviceName: string; - devicePlatform?: string | undefined; - installId?: string | undefined; - }): Promise<{ - accessToken: string; - refreshToken: string; - accessPolicy: AccessPolicyObject; - email: string; - user: { email: string; accessPolicy: AccessPolicyObject }; - }> { - throw new Error('Method not implemented.'); + private readonly users: User[]; + private readonly sessions: Session[]; + + public constructor(users: User[] = [], sessions: Session[] = []) { + this.users = users; + this.sessions = sessions; } - public refreshAccessToken( - refreshToken: string, - ): Promise<{ - accessToken: string; - refreshToken: string; - accessPolicy: AccessPolicyObject; - email: string; - user: { email: string; accessPolicy: AccessPolicyObject }; - }> { - throw new Error('Method not implemented.'); + + public async login(authDetails: { emailAddress: string; password: string; deviceName: string }) { + const { emailAddress, password } = authDetails; + const user = this.users.find( + ({ email, password: userPassword }) => emailAddress === email && password === userPassword, + ); + if (!user) { + throw new Error('Incorrect username or password'); + } + + let session = this.sessions.find(({ email }) => emailAddress === email); + if (!session) { + session = { + email: emailAddress, + accessToken: `${emailAddress}_accessToken`, + refreshToken: `${emailAddress}_refreshToken`, + }; + this.sessions.push(session); + } + + return { + email: emailAddress, + accessPolicy: user.accessPolicy, + accessToken: session.accessToken, + refreshToken: session.refreshToken, + user: { email: user.email, accessPolicy: user.accessPolicy }, + }; + } + + public async refreshAccessToken(refreshToken: string) { + const session = this.sessions.find( + ({ refreshToken: sessionRefreshToken }) => refreshToken === sessionRefreshToken, + ); + if (!session) { + throw new Error(`Refresh token expired, please login again`); + } + + const user = this.users.find(({ email }) => email === session.email); + if (!user) { + throw new Error(`No user exists for session: ${session.email}`); + } + + return { + email: session.email, + accessPolicy: user.accessPolicy, + accessToken: session.accessToken, + refreshToken: session.refreshToken, + user: { email: user.email, accessPolicy: user.accessPolicy }, + }; } } diff --git a/packages/api-client/src/connections/mocks/MockCentralApi.ts b/packages/api-client/src/connections/mocks/MockCentralApi.ts index a2ca17789e..bb64edb469 100644 --- a/packages/api-client/src/connections/mocks/MockCentralApi.ts +++ b/packages/api-client/src/connections/mocks/MockCentralApi.ts @@ -9,28 +9,84 @@ import { CentralApiInterface } from '..'; import { RequestBody } from '../ApiConnection'; export class MockCentralApi implements CentralApiInterface { + private readonly user: { email: string; password: string } | undefined; + private readonly resources: Record[]>; + + public constructor( + mockData: { + user?: { email: string; password: string }; + resources?: Record[]>; + } = {}, + ) { + this.user = mockData.user; + this.resources = mockData.resources || {}; + } + public getUser(): Promise { throw new Error('Method not implemented.'); } - public registerUserAccount( - userFields: Record, - ): Promise<{ userId: string; message: string }> { - throw new Error('Method not implemented.'); + + public async registerUserAccount(userFields: { id: string }) { + return { message: 'Successfully created user', userId: userFields.id }; } - public changeUserPassword( + + public async changeUserPassword( passwordChangeFields: Record, ): Promise<{ message: string }> { - throw new Error('Method not implemented.'); + if (!this.user) { + throw new Error( + 'Must provide a user to the MockCentralApi in order to call changeUserPassword', + ); + } + + const { oldPassword, password, passwordConfirm } = passwordChangeFields; + if (oldPassword !== this.user.password) { + throw new Error('Incorrect old password'); + } + + if (password !== passwordConfirm) { + throw new Error('password != confirm'); + } + + return { message: 'Successfully changed password' }; } - public createSurveyResponses(responses: MeditrakSurveyResponseRequest[]): Promise { - throw new Error('Method not implemented.'); + + public async createSurveyResponses(responses: MeditrakSurveyResponseRequest[]) { + // Do nothing } - public fetchResources( + + public async fetchResources( endpoint: string, - params?: Record | undefined, + params: { filter?: Record; columns?: string[] } = {}, ): Promise { - throw new Error('Method not implemented.'); + const resourcesOfType = this.resources[endpoint]; + if (!resourcesOfType) { + throw new Error(`No resources of type ${endpoint} provided to MockCentralApi`); + } + + const { filter, columns } = params; + if (!filter && !columns) { + return resourcesOfType; + } + + let resourcesToReturn = [...resourcesOfType]; + if (filter) { + resourcesToReturn = resourcesToReturn.filter(r => + Object.entries(r).every( + ([field, value]) => filter[field] === undefined || filter[field] === value, + ), + ); + } + + if (columns) { + resourcesToReturn = resourcesToReturn.map(r => + Object.fromEntries(columns.map(c => [c, r[c]])), + ); + } + + return resourcesToReturn; } + public createResource( endpoint: string, params: Record, diff --git a/packages/central-server/src/apiV2/surveyResponses/saveResponsesToDatabase.js b/packages/central-server/src/apiV2/surveyResponses/saveResponsesToDatabase.js index 699be0a264..37776c43c9 100644 --- a/packages/central-server/src/apiV2/surveyResponses/saveResponsesToDatabase.js +++ b/packages/central-server/src/apiV2/surveyResponses/saveResponsesToDatabase.js @@ -1,6 +1,6 @@ import { generateId } from '@tupaia/database'; import { getTimezoneNameFromTimestamp } from '@tupaia/tsutils'; -import { stripTimezoneFromDate } from '@tupaia/utils'; +import { ValidationError, stripTimezoneFromDate } from '@tupaia/utils'; import keyBy from 'lodash.keyby'; import { upsertAnswers } from '../../dataAccessors'; @@ -66,7 +66,7 @@ function buildResponseRecord(user, entitiesByCode, body) { if (value) return new Date(value).toISOString(); if (timestamp) return new Date(timestamp).toISOString(); - throw new Error(`Must provide ${parameterName} or timestamp`); + throw new ValidationError(`Must provide ${parameterName} or timestamp`); }; const startTime = defaultToTimestampOrThrow(inputStartTime, 'start_time'); diff --git a/packages/central-server/src/tests/apiV2/surveyResponse.test.js b/packages/central-server/src/tests/apiV2/surveyResponse.test.js index f506bd178d..4d555a1c7a 100644 --- a/packages/central-server/src/tests/apiV2/surveyResponse.test.js +++ b/packages/central-server/src/tests/apiV2/surveyResponse.test.js @@ -4,6 +4,7 @@ import moment from 'moment'; import { buildAndInsertSurveys, generateTestId, upsertDummyRecord } from '@tupaia/database'; import { oneSecondSleep, randomIntBetween } from '@tupaia/utils'; import { + expectError, expectErrors, expectSuccess, setupDummySyncQueue, @@ -34,7 +35,8 @@ const ENTITY_NON_CLINIC_ID = generateTestId(); const questionCode = key => `TEST-${key}`; -const expectError = (response, expectedError) => expectErrors(response, expectedError, 400); +const expectValidationErrors = (response, expectedError) => + expectErrors(response, expectedError, 400); let surveyId; @@ -236,7 +238,7 @@ describe('surveyResponse endpoint', () => { }, }); - expectError(response, /survey_id/); + expectValidationErrors(response, /survey_id/); }); it('Should throw if a submission has a missing entity code', async () => { @@ -250,8 +252,8 @@ describe('surveyResponse endpoint', () => { }, }); - expectError(response, /entity_id/); - expectError(response, /Must provide one of/); + expectValidationErrors(response, /entity_id/); + expectValidationErrors(response, /Must provide one of/); }); it('Should throw if a submission has an invalid entity id', async () => { @@ -266,7 +268,7 @@ describe('surveyResponse endpoint', () => { }, }); - expectError(response, /No entity with id/); + expectValidationErrors(response, /No entity with id/); }); it('Should throw if a submission has an invalid entity code', async () => { @@ -281,7 +283,7 @@ describe('surveyResponse endpoint', () => { }, }); - expectError(response, /No entity with code/); + expectValidationErrors(response, /No entity with code/); }); it('Should throw if a submission has an invalid question code', async () => { @@ -296,7 +298,7 @@ describe('surveyResponse endpoint', () => { }, }); - expectError(response, /Could not find question/); + expectValidationErrors(response, /Could not find question/); }); it('Should throw if a question is not present on the selected survey', async () => { @@ -311,7 +313,7 @@ describe('surveyResponse endpoint', () => { }, }); - expectError(response, /Could not find question/); + expectValidationErrors(response, /Could not find question/); }); it('Should allow a survey response with no answers', async () => { @@ -344,7 +346,7 @@ describe('surveyResponse endpoint', () => { }, }); - expectError(response, /is missing value/); + expectValidationErrors(response, /is missing value/); }); it('Should throw if a survey is missing a timestamp', async () => { @@ -358,8 +360,7 @@ describe('surveyResponse endpoint', () => { }, }); - expectError(response, /timestamp/); - expectError(response, /Should not be empty/); + expectError(response, /Must provide .* or timestamp/, 400); }); it('Should handle timezones correctly', async () => { @@ -544,37 +545,37 @@ describe('surveyResponse endpoint', () => { it('Should reject an invalid Binary answer', async () => { const response = await postTypeCheck('Binary', 'Maybe'); - expectError(response, /Maybe is not an accepted value/); + expectValidationErrors(response, /Maybe is not an accepted value/); }); it('Should reject an invalid Instruction answer', async () => { const response = await postTypeCheck('Instruction', 'Any text'); - expectError(response, /Should be empty/); + expectValidationErrors(response, /Should be empty/); }); it('Should reject an invalid Number answer', async () => { const response = await postTypeCheck('Number', 'Three'); - expectError(response, /Should contain a number/); + expectValidationErrors(response, /Should contain a number/); }); it('Should reject an invalid Radio answer', async () => { const response = await postTypeCheck('Radio', 'RadioX'); - expectError(response, /RadioX is not an accepted value/); + expectValidationErrors(response, /RadioX is not an accepted value/); }); it('Should reject an invalid Date answer', async () => { const response = await postTypeCheck('Date', '1/1/2019'); - expectError(response, /Dates should be in ISO 8601 format/); + expectValidationErrors(response, /Dates should be in ISO 8601 format/); }); it('Should reject an invalid SubmissionDate answer', async () => { const response = await postTypeCheck('SubmissionDate', '1/1/2019'); - expectError(response, /Dates should be in ISO 8601 format/); + expectValidationErrors(response, /Dates should be in ISO 8601 format/); }); it('Should reject an invalid DateOfData answer', async () => { const response = await postTypeCheck('DateOfData', '1/1/2019'); - expectError(response, /Dates should be in ISO 8601 format/); + expectValidationErrors(response, /Dates should be in ISO 8601 format/); }); }); }); diff --git a/packages/meditrak-app-server/src/__tests__/__integration__/AuthRoute.test.ts b/packages/meditrak-app-server/src/__tests__/__integration__/AuthRoute.test.ts index b5dfbc4a49..2791a7dab4 100644 --- a/packages/meditrak-app-server/src/__tests__/__integration__/AuthRoute.test.ts +++ b/packages/meditrak-app-server/src/__tests__/__integration__/AuthRoute.test.ts @@ -4,14 +4,23 @@ */ import { TestableServer } from '@tupaia/server-boilerplate'; -import { AuthApiMock, setupTestApp } from '../utilities'; -import { CAT_USER, CAT_USER_SESSION, TEST_DATA } from './fixtures'; +import { MockAuthApi, MockCentralApi } from '@tupaia/api-client'; +import { getTestModels, upsertDummyRecord } from '@tupaia/database'; +import { setupTestApp } from '../utilities'; +import { CAT_USER, CAT_USER_SESSION, SESSIONS, USERS } from './fixtures'; +import { TestModelRegistry } from '../types'; describe('auth', () => { let app: TestableServer; beforeAll(async () => { - app = await setupTestApp({ auth: new AuthApiMock(TEST_DATA) }); + const models = getTestModels() as TestModelRegistry; + await Promise.all(USERS.map(user => upsertDummyRecord(models.user, user))); + + app = await setupTestApp({ + auth: new MockAuthApi(USERS, SESSIONS), + central: new MockCentralApi({ resources: { user: USERS } }), + }); }); describe('/auth (login)', () => { @@ -42,8 +51,16 @@ describe('auth', () => { }, }); - expect(response.statusCode).toEqual(200); - expect(response.body).toEqual(CAT_USER); + expect(response.body).toEqual({ + accessPolicy: CAT_USER.accessPolicy, + email: CAT_USER.email, + accessToken: CAT_USER_SESSION.accessToken, + refreshToken: CAT_USER_SESSION.refreshToken, + user: { + accessPolicy: CAT_USER.accessPolicy, + email: CAT_USER.email, + }, + }); }); }); @@ -62,12 +79,20 @@ describe('auth', () => { it('can re-authenticate a user', async () => { const response = await app.post('auth', { - body: { refreshToken: CAT_USER_SESSION.refresh_token }, + body: { refreshToken: CAT_USER_SESSION.refreshToken }, query: { grantType: 'refresh_token' }, }); - expect(response.statusCode).toEqual(200); - expect(response.body).toEqual(CAT_USER); + expect(response.body).toEqual({ + accessPolicy: CAT_USER.accessPolicy, + email: CAT_USER.email, + accessToken: CAT_USER_SESSION.accessToken, + refreshToken: CAT_USER_SESSION.refreshToken, + user: { + accessPolicy: CAT_USER.accessPolicy, + email: CAT_USER.email, + }, + }); }); }); }); diff --git a/packages/meditrak-app-server/src/__tests__/__integration__/ChangePasswordRoute.test.ts b/packages/meditrak-app-server/src/__tests__/__integration__/ChangePasswordRoute.test.ts index 7de8049188..3bf4914808 100644 --- a/packages/meditrak-app-server/src/__tests__/__integration__/ChangePasswordRoute.test.ts +++ b/packages/meditrak-app-server/src/__tests__/__integration__/ChangePasswordRoute.test.ts @@ -4,6 +4,7 @@ */ import { constructAccessToken } from '@tupaia/auth'; +import { MockCentralApi } from '@tupaia/api-client'; import { clearTestData, getTestDatabase } from '@tupaia/database'; import { TestableServer } from '@tupaia/server-boilerplate'; import { createBearerHeader } from '@tupaia/utils'; @@ -18,26 +19,14 @@ describe('me/changePassword', () => { beforeAll(async () => { app = await setupTestApp({ - central: { - async changeUserPassword(passwordChangeFields: Record) { - const { oldPassword, password, passwordConfirm } = passwordChangeFields; - if (oldPassword !== CAT_USER.password) { - throw new Error('Incorrect old password'); - } - - if (password !== passwordConfirm) { - throw new Error('password != confirm'); - } - return { message: mockResponseMsg, newPassword: password }; - }, - }, + central: new MockCentralApi({ user: CAT_USER }), }); const user = await setupTestUser(); authHeader = createBearerHeader( constructAccessToken({ userId: user.id, - refreshToken: CAT_USER_SESSION.refresh_token, + refreshToken: CAT_USER_SESSION.refreshToken, apiClientUserId: undefined, }), ); @@ -77,7 +66,7 @@ describe('me/changePassword', () => { }, }); - expect(response.body).toEqual({ message: mockResponseMsg, newPassword }); + expect(response.body).toEqual({ message: mockResponseMsg }); }); it('it supports older meditrak-app password params', async () => { @@ -93,7 +82,7 @@ describe('me/changePassword', () => { }, }); - expect(response.body).toEqual({ message: mockResponseMsg, newPassword }); + expect(response.body).toEqual({ message: mockResponseMsg }); }); }); }); diff --git a/packages/meditrak-app-server/src/__tests__/__integration__/RegisterUserRoute.test.ts b/packages/meditrak-app-server/src/__tests__/__integration__/RegisterUserRoute.test.ts index f130ea23ed..c921be4054 100644 --- a/packages/meditrak-app-server/src/__tests__/__integration__/RegisterUserRoute.test.ts +++ b/packages/meditrak-app-server/src/__tests__/__integration__/RegisterUserRoute.test.ts @@ -4,6 +4,7 @@ */ import { TestableServer } from '@tupaia/server-boilerplate'; +import { MockCentralApi } from '@tupaia/api-client'; import { setupTestApp } from '../utilities'; const mockResponseMsg = 'Successfully created user'; @@ -13,11 +14,7 @@ describe('user', () => { beforeAll(async () => { app = await setupTestApp({ - central: { - async registerUserAccount(userFields: Record) { - return { message: mockResponseMsg, id: userFields.id }; - }, - }, + central: new MockCentralApi(), }); }); @@ -29,7 +26,7 @@ describe('user', () => { body: user, }); - expect(response.body).toEqual({ message: mockResponseMsg, id: userId }); + expect(response.body).toEqual({ message: mockResponseMsg, userId }); }); }); }); diff --git a/packages/meditrak-app-server/src/__tests__/__integration__/UserRewardsRoute.test.ts b/packages/meditrak-app-server/src/__tests__/__integration__/UserRewardsRoute.test.ts index f18be92034..a4beedc670 100644 --- a/packages/meditrak-app-server/src/__tests__/__integration__/UserRewardsRoute.test.ts +++ b/packages/meditrak-app-server/src/__tests__/__integration__/UserRewardsRoute.test.ts @@ -30,7 +30,7 @@ describe('me/rewards', () => { authHeader = createBearerHeader( constructAccessToken({ userId: user.id, - refreshToken: CAT_USER_SESSION.refresh_token, + refreshToken: CAT_USER_SESSION.refreshToken, apiClientUserId: undefined, }), ); diff --git a/packages/meditrak-app-server/src/__tests__/__integration__/fixtures.ts b/packages/meditrak-app-server/src/__tests__/__integration__/fixtures.ts index 1bd920ec5c..6c477b7637 100644 --- a/packages/meditrak-app-server/src/__tests__/__integration__/fixtures.ts +++ b/packages/meditrak-app-server/src/__tests__/__integration__/fixtures.ts @@ -3,20 +3,28 @@ * Copyright (c) 2017 - 2022 Beyond Essential Systems Pty Ltd */ +import { generateTestId } from '@tupaia/database'; + export const API_CLIENT_NAME = 'meditrak-app-server@tupaia.org'; export const API_CLIENT_PASSWORD = 'test_api_client_password'; export const CAT_USER = { + id: generateTestId(), firstName: 'Cat', lastName: 'Meow', email: 'cat@cathouse.com', password: 'dogbad', + accessPolicy: {}, +}; +export const CAT_USER_SESSION = { + email: 'cat@cathouse.com', + accessToken: 'dog_cat', + refreshToken: 'cat_dog', }; -export const CAT_USER_SESSION = { email: 'cat@cathouse.com', refresh_token: 'cat_dog' }; -const USERS = [CAT_USER]; +export const USERS = [CAT_USER]; -const SESSIONS = [CAT_USER_SESSION]; +export const SESSIONS = [CAT_USER_SESSION]; export const CAT_QUESTION = { code: 'CAT_QUESTION', text: 'What kind of cat do you have?' }; export const CAT_SURVEY = { @@ -24,8 +32,3 @@ export const CAT_SURVEY = { name: 'Cat Survey', questions: [CAT_QUESTION], }; - -export const TEST_DATA = { - users: USERS, - sessions: SESSIONS, -}; diff --git a/packages/meditrak-app-server/src/__tests__/__integration__/socialFeed/SocialFeedRoute.test.ts b/packages/meditrak-app-server/src/__tests__/__integration__/socialFeed/SocialFeedRoute.test.ts index edf9c932dd..5ee174598a 100644 --- a/packages/meditrak-app-server/src/__tests__/__integration__/socialFeed/SocialFeedRoute.test.ts +++ b/packages/meditrak-app-server/src/__tests__/__integration__/socialFeed/SocialFeedRoute.test.ts @@ -80,7 +80,7 @@ describe('socialFeed', () => { authHeader = createBearerHeader( constructAccessToken({ userId: user.id, - refreshToken: CAT_USER_SESSION.refresh_token, + refreshToken: CAT_USER_SESSION.refreshToken, apiClientUserId: undefined, }), ); diff --git a/packages/meditrak-app-server/src/__tests__/__integration__/sync/PushChangesRoute.test.ts b/packages/meditrak-app-server/src/__tests__/__integration__/sync/PushChangesRoute.test.ts index 71d6930561..e98e0d5ac7 100644 --- a/packages/meditrak-app-server/src/__tests__/__integration__/sync/PushChangesRoute.test.ts +++ b/packages/meditrak-app-server/src/__tests__/__integration__/sync/PushChangesRoute.test.ts @@ -22,7 +22,6 @@ import { setupDummySyncQueue, setupTestApp, setupTestUser, - CentralApiMock, grantUserAccess, revokeAccess, } from '../../utilities'; @@ -35,7 +34,6 @@ import { upsertUserEntityPermission, insertEntityAndFacility, } from '../../utilities/database'; -import { upsertSurveyResponsesMock } from '../../utilities/CentralApiMock'; import { RawSurveyResponseObject } from '../../../routes/sync/PushChangesRoute'; const clinicId = generateTestId(); @@ -75,6 +73,8 @@ const S3ClientMock = { }, }; +const upsertSurveyResponsesMock = jest.fn(); + jest.mock('@tupaia/utils', () => { const original = jest.requireActual('@tupaia/utils'); return { @@ -118,17 +118,22 @@ describe('changes (POST)', () => { let authHeader: string; const models = getTestModels() as TestModelRegistry; const syncQueue = setupDummySyncQueue(models); - const centralApiMock = new CentralApiMock(); beforeAll(async () => { - app = await setupTestApp({ central: centralApiMock }); + app = await setupTestApp({ + central: { + createSurveyResponses: (surveyResponses: Record[]) => { + upsertSurveyResponsesMock(surveyResponses); + }, + }, + }); const user = await setupTestUser(); userId = user.id; authHeader = createBearerHeader( constructAccessToken({ userId, - refreshToken: CAT_USER_SESSION.refresh_token, + refreshToken: CAT_USER_SESSION.refreshToken, apiClientUserId: undefined, }), ); @@ -363,26 +368,6 @@ describe('changes (POST)', () => { }); describe('Reject if invalid', () => { - it('rejects if answer provide non-exist question_id', async () => { - const answerObject = generateDummyAnswer(100); - const surveyResponseObject = generateDummySurveyResponse({ - answers: [answerObject], - }); - - const action = { - action: 'SubmitSurveyResponse', - payload: surveyResponseObject, - }; - const response = await app.post('changes', { - headers: { - Authorization: authHeader, - }, - body: [action], - }); - - expect(response.statusCode).toBe(400); - }); - it('rejects if entities_created provide non-exist entity type', async () => { const { entity } = await findOrCreateDummyCountryEntity(models, { code: 'DL', diff --git a/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/ChangesMetadataRoute.test.ts b/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/ChangesMetadataRoute.test.ts index 9457cd73ba..ace6cd35a0 100644 --- a/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/ChangesMetadataRoute.test.ts +++ b/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/ChangesMetadataRoute.test.ts @@ -55,7 +55,7 @@ describe('changes/metadata', () => { authHeader = createBearerHeader( constructAccessToken({ userId: user.id, - refreshToken: CAT_USER_SESSION.refresh_token, + refreshToken: CAT_USER_SESSION.refreshToken, apiClientUserId: undefined, }), ); diff --git a/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/CountChangesRoute.test.ts b/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/CountChangesRoute.test.ts index 01f7a15264..3cc5b12cb4 100644 --- a/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/CountChangesRoute.test.ts +++ b/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/CountChangesRoute.test.ts @@ -32,7 +32,7 @@ describe('changes/count', () => { authHeader = createBearerHeader( constructAccessToken({ userId: user.id, - refreshToken: CAT_USER_SESSION.refresh_token, + refreshToken: CAT_USER_SESSION.refreshToken, apiClientUserId: undefined, }), ); diff --git a/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/PullChangesRoute.test.ts b/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/PullChangesRoute.test.ts index 9735ffdaab..46f73eb373 100644 --- a/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/PullChangesRoute.test.ts +++ b/packages/meditrak-app-server/src/__tests__/__integration__/sync/pullChanges/PullChangesRoute.test.ts @@ -121,7 +121,7 @@ describe('changes (GET)', () => { authHeader = createBearerHeader( constructAccessToken({ userId, - refreshToken: CAT_USER_SESSION.refresh_token, + refreshToken: CAT_USER_SESSION.refreshToken, apiClientUserId: undefined, }), ); diff --git a/packages/meditrak-app-server/src/__tests__/utilities/AuthApiMock.ts b/packages/meditrak-app-server/src/__tests__/utilities/AuthApiMock.ts deleted file mode 100644 index 47926ce12e..0000000000 --- a/packages/meditrak-app-server/src/__tests__/utilities/AuthApiMock.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Tupaia - * Copyright (c) 2017 - 2022 Beyond Essential Systems Pty Ltd - */ - -type User = { - email: string; - password: string; -}; - -type Session = { - email: string; - refresh_token: string; -}; - -export class AuthApiMock { - private readonly users: User[]; - private readonly sessions: Session[]; - - public constructor({ users, sessions }: { users: User[]; sessions: Session[] }) { - this.users = users; - this.sessions = sessions; - } - - public login(userDetails: { emailAddress: string; password: string }) { - const matchingUser = this.users.find( - user => user.email === userDetails.emailAddress && user.password === userDetails.password, - ); - if (!matchingUser) { - throw new Error( - `No user found with email: ${userDetails.emailAddress} and password: ${userDetails.password}`, - ); - } - - return matchingUser; - } - - public refreshAccessToken(refreshToken: string) { - const matchingSession = this.sessions.find(session => session.refresh_token === refreshToken); - if (!matchingSession) { - throw new Error(`No session found with refresh_token: ${refreshToken}`); - } - - const matchingUser = this.users.find(user => user.email === matchingSession.email); - if (!matchingUser) { - throw new Error(`No user found with email: ${matchingSession.email}`); - } - - return matchingUser; - } -} diff --git a/packages/meditrak-app-server/src/__tests__/utilities/CentralApiMock.ts b/packages/meditrak-app-server/src/__tests__/utilities/CentralApiMock.ts deleted file mode 100644 index 8c6956cd89..0000000000 --- a/packages/meditrak-app-server/src/__tests__/utilities/CentralApiMock.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Tupaia - * Copyright (c) 2017 - 2022 Beyond Essential Systems Pty Ltd - */ - -export const upsertSurveyResponsesMock = jest.fn(); - -export class CentralApiMock { - public createSurveyResponses(surveyResponses: Record[]) { - upsertSurveyResponsesMock(surveyResponses); - } -} diff --git a/packages/meditrak-app-server/src/__tests__/utilities/index.ts b/packages/meditrak-app-server/src/__tests__/utilities/index.ts index 0f69b949c6..b8de0d8dbf 100644 --- a/packages/meditrak-app-server/src/__tests__/utilities/index.ts +++ b/packages/meditrak-app-server/src/__tests__/utilities/index.ts @@ -3,8 +3,6 @@ * Copyright (c) 2017 - 2022 Beyond Essential Systems Pty Ltd */ -export { AuthApiMock } from './AuthApiMock'; -export { CentralApiMock } from './CentralApiMock'; export { grantUserAccess, revokeAccess } from './grantUserAccess'; export { setupTestApp } from './setupTestApp'; export { setupTestUser } from './setupTestUser'; diff --git a/packages/meditrak-app-server/src/routes/sync/PushChangesRoute/validateInboundSurveyResponses.ts b/packages/meditrak-app-server/src/routes/sync/PushChangesRoute/validateInboundSurveyResponses.ts index c94ea77ab5..5df89b1dbf 100644 --- a/packages/meditrak-app-server/src/routes/sync/PushChangesRoute/validateInboundSurveyResponses.ts +++ b/packages/meditrak-app-server/src/routes/sync/PushChangesRoute/validateInboundSurveyResponses.ts @@ -13,8 +13,12 @@ export const validateSurveyResponseObject = (surveyResponseObject: RawSurveyResp throw new ValidationError('Payload must contain survey_response_object'); } - return ajvValidate( - MeditrakSurveyResponseRequestSchema, - surveyResponseObject, - ); + try { + return ajvValidate( + MeditrakSurveyResponseRequestSchema, + surveyResponseObject, + ); + } catch (err: any) { + throw new ValidationError(`Survey response validation error: ${err.message}`); + } };