diff --git a/application/backend/prisma/seed/seed.ts b/application/backend/prisma/seed/seed.ts index 297fa4f2..c19bba89 100644 --- a/application/backend/prisma/seed/seed.ts +++ b/application/backend/prisma/seed/seed.ts @@ -1,4 +1,4 @@ -import { PrismaClient } from '@prisma/client' +import { InviteStatus, PrismaClient } from '@prisma/client' import { hashPassword } from '../../src/authentication' import { SurveyStep } from '../../../common/types/survey' import { createDefaultAnswers, recalculateAnswers } from '../../src/utils/answers' @@ -20,9 +20,38 @@ const main = async () => { }) const ShortSurveyStepData = require('./shortSurveyData.json') + // Short Study - nested ACCEPTED invites for the four participants seeded below (Michael, Sally, Alice, Judith) const shortStudy = await prisma.study.create({ data: { name: 'Short Study', + invites: { + create: [ + { + email: 'michaelwilson@example.com', + status: InviteStatus.ACCEPTED, + expiresAt: new Date('2026-01-01'), + sentAt: new Date('2025-12-15'), + }, + { + email: 'sallywilson@example.com', + status: InviteStatus.ACCEPTED, + expiresAt: new Date('2026-01-01'), + sentAt: new Date('2025-12-15'), + }, + { + email: 'alicejohnson@example.com', + status: InviteStatus.ACCEPTED, + expiresAt: new Date('2026-01-01'), + sentAt: new Date('2025-12-15'), + }, + { + email: String(process.env.EXAMPLE_PARTICIPANT_EMAIL), + status: InviteStatus.ACCEPTED, + expiresAt: new Date('2026-01-01'), + sentAt: new Date('2025-12-15'), + }, + ], + }, }, }) console.log('Short Study created:', shortStudy) @@ -49,6 +78,7 @@ const main = async () => { const baseAnswers = createDefaultAnswers(ShortSurveyStepData) + // Michael - nested writes for user + profile + nextOfKin + studies + surveys const michael = await prisma.user.upsert({ where: { email: 'michaelwilson@example.com' }, update: {}, @@ -99,7 +129,7 @@ const main = async () => { }, }, }) - + // Sally - same nested pattern as Michael, second guardian on Short Study await prisma.user.upsert({ where: { email: 'sallywilson@example.com' }, update: {}, @@ -150,7 +180,7 @@ const main = async () => { }, }, }) - + // Johnny - dependent profile (no user account, so no invite) linked to Short Study await prisma.participantProfile.upsert({ where: { individualId: 'IND-ABC-003', @@ -193,6 +223,7 @@ const main = async () => { }, }) + // Alice - Participant user on Short Study const alice = await prisma.user.upsert({ where: { email: 'alicejohnson@example.com' }, update: {}, @@ -245,13 +276,22 @@ const main = async () => { }, }, }) - - // Ensure a Study record exists + // Seed Study - nested ACCEPTED invite for Judith below (she is also on Short Study, that invite is nested above) const defaultStudy = await prisma.study.create({ data: { name: 'Seed Study', redcapURL: process.env.REDCAP_API_URL, redcapToken: process.env.REDCAP_API_TOKEN, + invites: { + create: [ + { + email: String(process.env.EXAMPLE_PARTICIPANT_EMAIL), + status: InviteStatus.ACCEPTED, + expiresAt: new Date('2026-01-01'), + sentAt: new Date('2025-12-15'), + }, + ], + }, }, }) @@ -345,6 +385,7 @@ const main = async () => { const exampleAnswers = createDefaultAnswers(SeedSurveyStepData) exampleAnswers[1].answers[0] = false //For DUO testing + // Judith - example participant registered on both Short Study and Seed Study const exampleUser = await prisma.user.upsert({ where: { email: String(process.env.EXAMPLE_PARTICIPANT_EMAIL) }, update: {}, diff --git a/application/backend/src/controllers/ParticipantsController.test.ts b/application/backend/src/controllers/ParticipantsController.test.ts index 168ac58e..97176eff 100644 --- a/application/backend/src/controllers/ParticipantsController.test.ts +++ b/application/backend/src/controllers/ParticipantsController.test.ts @@ -750,6 +750,7 @@ describe('InvitesController', () => { const invite = await prisma.invite.findFirstOrThrow({ where: { email: TestUsers.PARTICIPANT_UNANSWERED.email, + studyId: TestStudies.TEST_STUDY_2.id, }, }) @@ -805,6 +806,7 @@ describe('InvitesController', () => { const invite = await prisma.invite.findFirstOrThrow({ where: { email: TestUsers.PARTICIPANT_UNANSWERED.email, + studyId: TestStudies.TEST_STUDY_2.id, }, }) diff --git a/application/common/testing/seed.ts b/application/common/testing/seed.ts index 9133257c..aa140b68 100644 --- a/application/common/testing/seed.ts +++ b/application/common/testing/seed.ts @@ -23,6 +23,7 @@ export async function seedTests(prisma: PrismaClient) { await prisma.$executeRaw`SELECT setval(pg_get_serial_sequence('"Organisation"', 'id'), 2, false) FROM "Organisation";` // Create four studies + // Test Study - nested ACCEPTED invites for the three participants below (UNANSWERED, COMPLETED, GUARDIAN_2), plus fixture invites in various states for testing invite lifecycle const testStudy = await prisma.study.create({ data: { name: TestStudies.TEST_STUDY.name, @@ -30,6 +31,53 @@ export async function seedTests(prisma: PrismaClient) { redcapToken: 'ABC', redcapURL: 'http://redcaptest.com', contactUsEmail: 'test@contactus.com', + invites: { + create: [ + { + email: TestUsers.PARTICIPANT_UNANSWERED.email, + status: InviteStatus.ACCEPTED, + expiresAt: new Date('2026-01-01'), + sentAt: new Date('2025-12-15'), + }, + { + email: TestUsers.PARTICIPANT_COMPLETED.email, + status: InviteStatus.ACCEPTED, + expiresAt: new Date('2026-01-01'), + sentAt: new Date('2025-12-15'), + }, + { + email: TestUsers.GUARDIAN_2.email, + status: InviteStatus.ACCEPTED, + expiresAt: new Date('2026-01-01'), + sentAt: new Date('2025-12-15'), + }, + { + email: TestInvites.INVITE_PENDING.email, + status: InviteStatus.PENDING, + expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 1 day in the future + }, + { + email: TestInvites.INVITE_ACCEPTED.email, + status: InviteStatus.ACCEPTED, + expiresAt: new Date(Date.now() - 24 * 60 * 60 * 1000), // 1 day in the past + }, + { + email: TestInvites.INVITE_REVOKED.email, + status: InviteStatus.REVOKED, + expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 1 day in the future + }, + { + email: TestInvites.INVITE_EXPIRED.email, + status: InviteStatus.EXPIRED, + expiresAt: new Date(Date.now() - 24 * 60 * 60 * 1000), // 1 day in the past + }, + { + email: TestInvites.INVITE_2_PENDING.email, + status: InviteStatus.PENDING, + expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 1 day in the future + }, + ], + }, }, }) @@ -43,10 +91,27 @@ export async function seedTests(prisma: PrismaClient) { }) // This study will have a survey, a participant user and draft answers (mostly for frontend multistudy testing) + // Nested ACCEPTED invites for GUARDIAN_2 and PARTICIPANT_UNANSWERED (both linked to this study below) const frontendTestStudy = await prisma.study.create({ data: { name: TestStudies.FE_TEST_STUDY.name, id: TestStudies.FE_TEST_STUDY.id, + invites: { + create: [ + { + email: TestUsers.GUARDIAN_2.email, + status: InviteStatus.ACCEPTED, + expiresAt: new Date('2026-01-01'), + sentAt: new Date('2025-12-15'), + }, + { + email: TestUsers.PARTICIPANT_UNANSWERED.email, + status: InviteStatus.ACCEPTED, + expiresAt: new Date('2026-01-01'), + sentAt: new Date('2025-12-15'), + }, + ], + }, }, }) @@ -128,111 +193,160 @@ export async function seedTests(prisma: PrismaClient) { }, }) - //User with unanswered survey - await prisma.user.create({ + // Survey versions - created before participants so they can be nested inside profile creation + const testSurveyVersion = await prisma.surveyVersion.create({ data: { - id: TestUsers.PARTICIPANT_UNANSWERED.id, - email: TestUsers.PARTICIPANT_UNANSWERED.email, - firstName: 'Test', - lastName: 'User', - password: hashPassword(TestUsers.PARTICIPANT_UNANSWERED.password), - role: Role.Participant, - organisations: { - connect: { - id: 1, - }, - }, + id: 800, + status: 'PUBLISHED', + versionNumber: 1, + data: ExampleSurveyStepData as SurveyStep[], + studyId: testStudy.id, }, }) - //Second guardian user - await prisma.user.create({ + await prisma.surveyVersion.create({ data: { - id: TestUsers.GUARDIAN_2.id, - email: TestUsers.GUARDIAN_2.email, - firstName: 'Second', - lastName: 'Guardian', - password: hashPassword(TestUsers.GUARDIAN_2.password), - role: Role.Participant, + status: 'DRAFT', + versionNumber: 2, + data: ExampleSurveyStepData as SurveyStep[], + studyId: testStudy.id, }, }) - //User with completed survey - await prisma.user.create({ + + // Published survey for study 2 + const study2survey = await prisma.surveyVersion.create({ data: { - id: TestUsers.PARTICIPANT_COMPLETED.id, - email: TestUsers.PARTICIPANT_COMPLETED.email, - firstName: 'Test', - lastName: 'User', - password: hashPassword(TestUsers.PARTICIPANT_COMPLETED.password), - role: Role.Participant, + status: 'PUBLISHED', + versionNumber: 1, + data: [ + { + title: 'Study2step', + text: '', + elements: [ + { + type: 'question-checkbox', + data: { + text: 'Hello', + value: null, + }, + }, + ], + }, + ] as SurveyStep[], + studyId: secondTestStudy.id, }, }) - const participantUnansweredProfile = await prisma.participantProfile.create({ - data: { + // User with unanswered survey - nested writes for user + profile + study + nextOfKin + surveys + await prisma.user.upsert({ + where: { email: TestUsers.PARTICIPANT_UNANSWERED.email }, + update: {}, + create: { id: TestUsers.PARTICIPANT_UNANSWERED.id, - firstName: 'Unanswered', + email: TestUsers.PARTICIPANT_UNANSWERED.email, + firstName: 'Test', lastName: 'User', - addressLine: '123 smith st', - dob: '1980-01-24', - mobile: '0412345678', - postcode: '1234', - preferredContact: 'EMAIL', - state: 'VIC', - suburb: 'M', - studies: { - create: { - participantId: `PID-TEST1-${TestUsers.PARTICIPANT_UNANSWERED.id}`, - study: { - connect: { - id: testStudy.id, + password: hashPassword(TestUsers.PARTICIPANT_UNANSWERED.password), + role: Role.Participant, + organisations: { connect: { id: 1 } }, + profiles: { + create: [ + { + id: TestUsers.PARTICIPANT_UNANSWERED.id, + firstName: 'Unanswered', + lastName: 'User', + addressLine: '123 smith st', + dob: '1980-01-24', + mobile: '0412345678', + postcode: '1234', + preferredContact: 'EMAIL', + state: 'VIC', + suburb: 'M', + participantType: 'STANDARD', + studies: { + create: { + participantId: `PID-TEST1-${TestUsers.PARTICIPANT_UNANSWERED.id}`, + study: { connect: { id: testStudy.id } }, + }, + }, + nextOfKin: { + create: { + email: 'alt@email.com', + firstName: 'Alt', + lastName: 'Cont', + }, + }, + surveys: { + create: [ + { + versionId: testSurveyVersion.id, + answers: [ + { status: 'review_required', answers: [] }, + { status: 'review_required', answers: [null, null] }, + ], + }, + { + versionId: study2survey.id, + answers: [{ status: 'review_required', answers: [null] }], + }, + ], }, }, - }, + ], }, - userId: TestUsers.PARTICIPANT_UNANSWERED.id, - participantType: 'STANDARD', }, }) - - await prisma.alternativeContact.create({ - data: { - participantProfileId: TestUsers.PARTICIPANT_UNANSWERED.id, - email: 'alt@email.com', - firstName: 'Alt', - lastName: 'Cont', - }, - }) - - await prisma.participantProfile.create({ - data: { + // User with completed survey + await prisma.user.upsert({ + where: { email: TestUsers.PARTICIPANT_COMPLETED.email }, + update: {}, + create: { id: TestUsers.PARTICIPANT_COMPLETED.id, - firstName: 'Completed', + email: TestUsers.PARTICIPANT_COMPLETED.email, + firstName: 'Test', lastName: 'User', - addressLine: '123 smith st', - dob: '1980-01-23', - mobile: '0412345678', - postcode: '1234', - preferredContact: 'EMAIL', - state: 'VIC', - suburb: 'Melbourne', - studies: { - create: { - participantId: `PID-TEST1-${TestUsers.PARTICIPANT_COMPLETED.id}`, - study: { - connect: { - id: testStudy.id, + password: hashPassword(TestUsers.PARTICIPANT_COMPLETED.password), + role: Role.Participant, + profiles: { + create: [ + { + id: TestUsers.PARTICIPANT_COMPLETED.id, + firstName: 'Completed', + lastName: 'User', + addressLine: '123 smith st', + dob: '1980-01-23', + mobile: '0412345678', + postcode: '1234', + preferredContact: 'EMAIL', + state: 'VIC', + suburb: 'Melbourne', + familyId: 100, + participantType: 'GUARDIAN', + studies: { + create: { + participantId: `PID-TEST1-${TestUsers.PARTICIPANT_COMPLETED.id}`, + study: { connect: { id: testStudy.id } }, + }, + }, + surveys: { + create: { + versionId: testSurveyVersion.id, + answers: [ + { status: 'viewed', answers: [] }, + { + status: 'completed', + answers: [false, 'Choice 2'], + last_updated: '2024-12-02T23:45:27.815Z', + }, + ], + }, }, }, - }, + ], }, - userId: TestUsers.PARTICIPANT_COMPLETED.id, - familyId: 100, - participantType: 'GUARDIAN', }, }) - - //Dependent Profile (no user) + // Dependent Profile (no user - so profile.create with nested studies + surveys) await prisma.participantProfile.create({ data: { id: TestUsers.DEPENDENT.id, @@ -245,156 +359,81 @@ export async function seedTests(prisma: PrismaClient) { preferredContact: 'EMAIL', state: 'VIC', suburb: 'Melbourne', + familyId: 100, + userId: null, + participantType: 'DEPENDENT_AGE', studies: { create: { participantId: `PID-TEST1-${TestUsers.DEPENDENT.id}`, - study: { - connect: { - id: testStudy.id, + study: { connect: { id: testStudy.id } }, + }, + }, + surveys: { + create: { + versionId: testSurveyVersion.id, + answers: [ + { status: 'viewed', answers: [] }, + { + status: 'review_required', + answers: [null, null], + last_updated: '2024-12-02T23:45:27.815Z', }, - }, + ], }, }, - familyId: 100, - userId: null, - participantType: 'DEPENDENT_AGE', }, }) - //Second guardian - await prisma.participantProfile.create({ - data: { + // Second guardian user + await prisma.user.upsert({ + where: { email: TestUsers.GUARDIAN_2.email }, + update: {}, + create: { id: TestUsers.GUARDIAN_2.id, + email: TestUsers.GUARDIAN_2.email, firstName: 'Second', lastName: 'Guardian', - addressLine: '123 smith st', - dob: '1990-01-23', - mobile: '0412345678', - postcode: '1234', - preferredContact: 'EMAIL', - state: 'VIC', - suburb: 'Melbourne', - studies: { - create: { - participantId: `PID-TEST1-${TestUsers.GUARDIAN_2.id}`, - study: { - connect: { - id: testStudy.id, + password: hashPassword(TestUsers.GUARDIAN_2.password), + role: Role.Participant, + profiles: { + create: [ + { + id: TestUsers.GUARDIAN_2.id, + firstName: 'Second', + lastName: 'Guardian', + addressLine: '123 smith st', + dob: '1990-01-23', + mobile: '0412345678', + postcode: '1234', + preferredContact: 'EMAIL', + state: 'VIC', + suburb: 'Melbourne', + familyId: 100, + participantType: 'GUARDIAN', + studies: { + create: { + participantId: `PID-TEST1-${TestUsers.GUARDIAN_2.id}`, + study: { connect: { id: testStudy.id } }, + }, }, - }, - }, - }, - familyId: 100, - userId: TestUsers.GUARDIAN_2.id, - participantType: 'GUARDIAN', - }, - }) - - const testSurveyVersion = await prisma.surveyVersion.create({ - data: { - id: 800, - status: 'PUBLISHED', - versionNumber: 1, - data: ExampleSurveyStepData as SurveyStep[], - studyId: testStudy.id, - }, - }) - - await prisma.surveyVersion.create({ - data: { - status: 'DRAFT', - versionNumber: 2, - data: ExampleSurveyStepData as SurveyStep[], - studyId: testStudy.id, - }, - }) - // publish a survey for study 2 - const study2survey = await prisma.surveyVersion.create({ - data: { - status: 'PUBLISHED', - versionNumber: 1, - data: [ - { - title: 'Study2step', - text: '', - elements: [ - { - type: 'question-checkbox', - data: { - text: 'Hello', - value: null, + surveys: { + create: { + versionId: testSurveyVersion.id, + answers: [ + { status: 'viewed', answers: [] }, + { + status: 'review_required', + answers: [null, null], + last_updated: '2024-12-02T23:45:27.815Z', + }, + ], }, }, - ], - }, - ] as SurveyStep[], - studyId: secondTestStudy.id, - }, - }) - - await prisma.surveyVersionAnswers.create({ - data: { - versionId: testSurveyVersion.id, - profileId: TestUsers.PARTICIPANT_UNANSWERED.id, - answers: [ - { status: 'review_required', answers: [] }, - { status: 'review_required', answers: [null, null] }, - ], - }, - }) - await prisma.surveyVersionAnswers.create({ - data: { - versionId: study2survey.id, - profileId: TestUsers.PARTICIPANT_UNANSWERED.id, - answers: [{ status: 'review_required', answers: [null] }], - }, - }) - - await prisma.surveyVersionAnswers.create({ - data: { - versionId: testSurveyVersion.id, - profileId: TestUsers.PARTICIPANT_COMPLETED.id, - answers: [ - { status: 'viewed', answers: [] }, - { - status: 'completed', - answers: [false, 'Choice 2'], - last_updated: '2024-12-02T23:45:27.815Z', - }, - ], - }, - }) - - await prisma.surveyVersionAnswers.create({ - data: { - versionId: testSurveyVersion.id, - profileId: TestUsers.DEPENDENT.id, - answers: [ - { status: 'viewed', answers: [] }, - { - status: 'review_required', - answers: [null, null], - last_updated: '2024-12-02T23:45:27.815Z', - }, - ], - }, - }) - - await prisma.surveyVersionAnswers.create({ - data: { - versionId: testSurveyVersion.id, - profileId: TestUsers.GUARDIAN_2.id, - answers: [ - { status: 'viewed', answers: [] }, - { - status: 'review_required', - answers: [null, null], - last_updated: '2024-12-02T23:45:27.815Z', - }, - ], + }, + ], + }, }, }) - // Seed a user and password reset token await prisma.user.create({ data: { @@ -416,43 +455,6 @@ export async function seedTests(prisma: PrismaClient) { }, }) - // Setup invites - await prisma.invite.createMany({ - data: [ - { - email: TestInvites.INVITE_PENDING.email, - status: InviteStatus.PENDING, - studyId: testStudy.id, - expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 1 day in the future - }, - { - email: TestInvites.INVITE_ACCEPTED.email, - status: InviteStatus.ACCEPTED, - studyId: testStudy.id, - expiresAt: new Date(Date.now() - 24 * 60 * 60 * 1000), // 1 day in the past - }, - { - email: TestInvites.INVITE_REVOKED.email, - status: InviteStatus.REVOKED, - studyId: testStudy.id, - expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 1 day in the future - }, - { - email: TestInvites.INVITE_EXPIRED.email, - status: InviteStatus.EXPIRED, - studyId: testStudy.id, - expiresAt: new Date(Date.now() - 24 * 60 * 60 * 1000), // 1 day in the past - }, - // Pending invites for testing - { - email: TestInvites.INVITE_2_PENDING.email, - status: InviteStatus.PENDING, - studyId: testStudy.id, - expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 1 day in the future - }, - ], - }) - // Frontend multistudy test seed data const frontendTestSurveryVersion = await prisma.surveyVersion.create({ data: { @@ -497,7 +499,7 @@ export async function seedTests(prisma: PrismaClient) { await prisma.participantProfile.update({ where: { - id: participantUnansweredProfile.id, + id: TestUsers.PARTICIPANT_UNANSWERED.id, }, data: { studies: {