From 5374c19f514674f9a17a21aca7863ce2d2bb7d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Genevi=C3=A8ve=20Bastien?= Date: Tue, 9 Apr 2024 15:02:38 -0400 Subject: [PATCH] interview: Make sure _startedAt is initialized at interview creation It should not depend on the frontend's section preload to have this important data in the interview. --- .../interviews/__tests__/interviews.test.ts | 16 +++++++++++----- .../src/services/interviews/interviews.ts | 15 ++++++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/evolution-backend/src/services/interviews/__tests__/interviews.test.ts b/packages/evolution-backend/src/services/interviews/__tests__/interviews.test.ts index 24c726b5..4b62817b 100644 --- a/packages/evolution-backend/src/services/interviews/__tests__/interviews.test.ts +++ b/packages/evolution-backend/src/services/interviews/__tests__/interviews.test.ts @@ -12,6 +12,7 @@ import interviewsQueries from '../../../models/interviews.db.queries'; import interviewsAccessesQueries from '../../../models/interviewsAccesses.db.queries'; import { registerAccessCodeValidationFunction } from '../../accessCode'; import { updateInterview } from '../interview'; +import moment from 'moment'; jest.mock('../../../models/interviews.db.queries', () => ({ findByResponse: jest.fn(), @@ -181,11 +182,12 @@ describe('Create interviews', () => { }); test('Create with empty responses', async() => { + const newInterview = await Interviews.createInterviewForUser(participantId, {}); expect(mockDbCreate).toHaveBeenCalledTimes(1); expect(mockDbCreate).toHaveBeenCalledWith({ participant_id: participantId, - responses: {}, + responses: { _startedAt: expect.anything() }, is_active: true, validations: {}, logs: [] @@ -207,7 +209,7 @@ describe('Create interviews', () => { expect(mockDbCreate).toHaveBeenCalledTimes(1); expect(mockDbCreate).toHaveBeenCalledWith({ participant_id: participantId, - responses, + responses: { ...responses, _startedAt: expect.anything() }, is_active: true, validations: {}, logs: [] @@ -226,7 +228,7 @@ describe('Create interviews', () => { expect(mockDbCreate).toHaveBeenCalledTimes(1); expect(mockDbCreate).toHaveBeenCalledWith({ participant_id: participantId, - responses: {}, + responses: { _startedAt: expect.anything() }, is_active: true, validations: {}, logs: [] @@ -237,19 +239,23 @@ describe('Create interviews', () => { }); test('Create and return many other field', async() => { + const initialTimeStamp = moment().unix(); const returningFields = ['participant_id', 'responses', 'uuid']; const newInterview = await Interviews.createInterviewForUser(participantId, {}, returningFields); expect(mockDbCreate).toHaveBeenCalledTimes(1); expect(mockDbCreate).toHaveBeenCalledWith({ participant_id: participantId, - responses: {}, + responses: { _startedAt: expect.anything() }, is_active: true, validations: {}, logs: [] }, returningFields); - expect(newInterview).toEqual({ participant_id: participantId, uuid: expect.anything(), responses: {} }); + expect(newInterview).toEqual({ participant_id: participantId, uuid: expect.anything(), responses: { _startedAt: expect.anything() } }); expect(mockDbGetByUuid).not.toHaveBeenCalled(); expect(mockInterviewUpdate).not.toHaveBeenCalled(); + + // Make sure timestamp in response is higher than the one at the beginning of the test + expect((newInterview.responses as any)._startedAt).toBeGreaterThanOrEqual(initialTimeStamp); }); }); diff --git a/packages/evolution-backend/src/services/interviews/interviews.ts b/packages/evolution-backend/src/services/interviews/interviews.ts index d91c8da9..7ee49d20 100644 --- a/packages/evolution-backend/src/services/interviews/interviews.ts +++ b/packages/evolution-backend/src/services/interviews/interviews.ts @@ -4,6 +4,8 @@ * This file is licensed under the MIT License. * License text available at https://opensource.org/licenses/MIT */ +import _cloneDeep from 'lodash/cloneDeep'; +import moment from 'moment'; import { auditInterview, getChangesAfterCleaningInterview } from 'evolution-common/lib/services/interviews/interview'; import { updateInterview, setInterviewFields, copyResponsesToValidatedData } from './interview'; import { mapResponsesToValidatedData } from './interviewUtils'; @@ -105,9 +107,13 @@ export default class Interviews { ): Promise>> => { // TODO Make sure there is no active interview for this user already? - // Create the interview for this user + // Create the interview for this user, make sure the start time is set + const responses = _cloneDeep(initialResponses); + if (responses._startedAt === undefined) { + responses._startedAt = moment().unix(); + } const interview = await interviewsDbQueries.create( - { participant_id: participantId, responses: initialResponses, is_active: true, validations: {}, logs: [] }, + { participant_id: participantId, responses, is_active: true, validations: {}, logs: [] }, returning ); if (!interview.uuid || Object.keys(initialResponses).length === 0) { @@ -160,7 +166,10 @@ export default class Interviews { static getValidationAuditStats = async ( params: { filter?: { is_valid?: 'valid' | 'invalid' | 'notInvalid' | 'notValidated' | 'all' } & { - [key: string]: string | string[] | { value: string | string[] | boolean | number | null; op?: keyof OperatorSigns }; + [key: string]: + | string + | string[] + | { value: string | string[] | boolean | number | null; op?: keyof OperatorSigns }; }; } = {} ): Promise<{ auditStats: AuditsByLevelAndObjectType }> => {