diff --git a/src/main/java/org/broadinstitute/consent/http/db/DatasetDAO.java b/src/main/java/org/broadinstitute/consent/http/db/DatasetDAO.java index 31fba190ec..5b0e08ae64 100644 --- a/src/main/java/org/broadinstitute/consent/http/db/DatasetDAO.java +++ b/src/main/java/org/broadinstitute/consent/http/db/DatasetDAO.java @@ -67,6 +67,7 @@ public interface DatasetDAO extends Transactional { s.description AS s_description, s.data_types AS s_data_types, s.pi_name AS s_pi_name, + s.pi_email AS s_pi_email, s.create_user_id AS s_create_user_id, s.create_date AS s_create_date, s.update_user_id AS s_user_id, @@ -176,6 +177,7 @@ void updateDatasetByDatasetId( s.description AS s_description, s.data_types AS s_data_types, s.pi_name AS s_pi_name, + s.pi_email AS s_pi_email, s.create_user_id AS s_create_user_id, s.create_date AS s_create_date, s.update_user_id AS s_user_id, @@ -349,6 +351,7 @@ LEFT JOIN (SELECT DISTINCT dataset_id AS id FROM dar_dataset) dar_ds_ids ON dar_ s.description AS s_description, s.data_types AS s_data_types, s.pi_name AS s_pi_name, + s.pi_email AS s_pi_email, s.create_user_id AS s_create_user_id, s.create_date AS s_create_date, s.update_user_id AS s_user_id, diff --git a/src/main/java/org/broadinstitute/consent/http/db/StudyDAO.java b/src/main/java/org/broadinstitute/consent/http/db/StudyDAO.java index b7909e513e..f171edeb5b 100644 --- a/src/main/java/org/broadinstitute/consent/http/db/StudyDAO.java +++ b/src/main/java/org/broadinstitute/consent/http/db/StudyDAO.java @@ -6,7 +6,6 @@ import java.util.UUID; import org.broadinstitute.consent.http.db.mapper.FileStorageObjectMapperWithFSOPrefix; import org.broadinstitute.consent.http.db.mapper.StudyReducer; -import org.broadinstitute.consent.http.models.FileStorageObject; import org.broadinstitute.consent.http.models.Study; import org.broadinstitute.consent.http.models.StudyDatasetCountRecord; import org.jdbi.v3.sqlobject.config.RegisterBeanMapper; @@ -36,30 +35,38 @@ public interface StudyDAO extends Transactional { sp.value AS sp_value, sp.type AS sp_type, d.dataset_id AS s_dataset_id, - """ - + FileStorageObject.QUERY_FIELDS_WITH_FSO_PREFIX - + " " - + """ - FROM - study s - LEFT JOIN study_property sp ON sp.study_id = s.study_id - LEFT JOIN file_storage_object fso ON fso.entity_id = s.uuid::text AND fso.deleted = false - LEFT JOIN dataset d ON d.study_id = s.study_id - WHERE s.study_id = :studyId - """) + fso.file_storage_object_id AS fso_file_storage_object_id, + fso.entity_id AS fso_entity_id, + fso.file_name AS fso_file_name, + fso.category AS fso_category, + fso.gcs_file_uri AS fso_gcs_file_uri, + fso.media_type AS fso_media_type, + fso.create_date AS fso_create_date, + fso.create_user_id AS fso_create_user_id, + fso.update_date AS fso_update_date, + fso.update_user_id AS fso_update_user_id, + fso.deleted AS fso_deleted, + fso.delete_user_id AS fso_delete_user_id + FROM + study s + LEFT JOIN study_property sp ON sp.study_id = s.study_id + LEFT JOIN file_storage_object fso ON fso.entity_id = s.uuid::text AND fso.deleted = false + LEFT JOIN dataset d ON d.study_id = s.study_id + WHERE s.study_id = :studyId + """) Study findStudyById(@Bind("studyId") Integer studyId); @SqlUpdate( """ INSERT INTO study ( name, description, - pi_name, data_types, + pi_name, pi_email, data_types, public_visibility, create_user_id, create_date, uuid ) VALUES ( :name, :description, - :piName, :dataTypes, + :piName, :piEmail, :dataTypes, :publicVisibility, :createUserId, :createDate, :uuid @@ -70,6 +77,7 @@ Integer insertStudy( @Bind("name") String name, @Bind("description") String description, @Bind("piName") String piName, + @Bind("piEmail") String piEmail, @Bind("dataTypes") List dataTypes, @Bind("publicVisibility") Boolean publicVisibility, @Bind("createUserId") Integer createUserId, @@ -82,6 +90,7 @@ Integer insertStudy( SET name = :name, description = :description, pi_name = :piName, + pi_email = :piEmail, data_types = :dataTypes, public_visibility = :publicVisibility, update_user_id = :updateUserId, @@ -93,6 +102,7 @@ void updateStudy( @Bind("name") String name, @Bind("description") String description, @Bind("piName") String piName, + @Bind("piEmail") String piEmail, @Bind("dataTypes") List dataTypes, @Bind("publicVisibility") Boolean publicVisibility, @Bind("updateUserId") Integer updateUserId, diff --git a/src/main/java/org/broadinstitute/consent/http/models/Study.java b/src/main/java/org/broadinstitute/consent/http/models/Study.java index dd12caef9d..9f56b66300 100644 --- a/src/main/java/org/broadinstitute/consent/http/models/Study.java +++ b/src/main/java/org/broadinstitute/consent/http/models/Study.java @@ -13,6 +13,7 @@ public class Study { private String description; private Boolean publicVisibility; private String piName; + private String piEmail; private List dataTypes; private final Set datasetIds = new HashSet<>(); private Set datasets; @@ -65,6 +66,14 @@ public void setPiName(String piName) { this.piName = piName; } + public String getPiEmail() { + return piEmail; + } + + public void setPiEmail(String piEmail) { + this.piEmail = piEmail; + } + public List getDataTypes() { return dataTypes; } diff --git a/src/main/java/org/broadinstitute/consent/http/models/StudyConversion.java b/src/main/java/org/broadinstitute/consent/http/models/StudyConversion.java index d5cbb3149c..aed1f35f17 100644 --- a/src/main/java/org/broadinstitute/consent/http/models/StudyConversion.java +++ b/src/main/java/org/broadinstitute/consent/http/models/StudyConversion.java @@ -13,6 +13,7 @@ public class StudyConversion { private String phenotype; private String species; private String piName; + private String piEmail; private String dataSubmitterEmail; private Boolean publicVisibility; private String nihAnvilUse; @@ -71,6 +72,14 @@ public void setPiName(String piName) { this.piName = piName; } + public String getPiEmail() { + return piEmail; + } + + public void setPiEmail(String piEmail) { + this.piEmail = piEmail; + } + public String getDataSubmitterEmail() { return dataSubmitterEmail; } @@ -149,6 +158,7 @@ public Study createNewStudyStub() { study.setDescription(getDescription()); study.setPublicVisibility(getPublicVisibility()); study.setPiName(getPiName()); + study.setPiEmail(getPiEmail()); study.setDataTypes(getDataTypes()); return study; } diff --git a/src/main/java/org/broadinstitute/consent/http/models/StudyPatch.java b/src/main/java/org/broadinstitute/consent/http/models/StudyPatch.java index c63670f72d..80cc984d0c 100644 --- a/src/main/java/org/broadinstitute/consent/http/models/StudyPatch.java +++ b/src/main/java/org/broadinstitute/consent/http/models/StudyPatch.java @@ -24,6 +24,7 @@ public record StudyPatch( String phenotypeIndication, String species, String piName, + String piEmail, List dataCustodianEmail, String alternativeDataSharingPlanTargetDeliveryDate, String alternativeDataSharingPlanTargetPublicReleaseDate, @@ -98,6 +99,7 @@ public boolean isPatchable(Study study) { checks.add(checkPhenotypeIndication(study)); checks.add(checkSpecies(study)); checks.add(checkPiName(study)); + checks.add(checkPiEmail(study)); checks.add(checkDataCustodians(study)); checks.add(checkTargetDate(study)); checks.add(checkTargetReleaseDate(study)); @@ -159,6 +161,10 @@ private boolean checkPiName(Study study) { return piName() != null && !piName().equals(study.getPiName()) && !piName().isBlank(); } + private boolean checkPiEmail(Study study) { + return piEmail() != null && !piEmail().equals(study.getPiEmail()) && !piEmail().isBlank(); + } + private boolean checkDataCustodians(Study study) { Optional custodiansProp = study.getProperties().stream() diff --git a/src/main/java/org/broadinstitute/consent/http/models/dataset_registration_v1/DatasetRegistrationSchemaV1.java b/src/main/java/org/broadinstitute/consent/http/models/dataset_registration_v1/DatasetRegistrationSchemaV1.java index 781381df99..a5e58725b8 100644 --- a/src/main/java/org/broadinstitute/consent/http/models/dataset_registration_v1/DatasetRegistrationSchemaV1.java +++ b/src/main/java/org/broadinstitute/consent/http/models/dataset_registration_v1/DatasetRegistrationSchemaV1.java @@ -32,6 +32,7 @@ "phenotypeIndication", "species", "piName", + "piEmail", "dataSubmitterUserId", "dataCustodianEmail", "publicVisibility", @@ -107,6 +108,11 @@ public class DatasetRegistrationSchemaV1 { @JsonPropertyDescription("Principal Investigator Name") private String piName; + /** Principal Investigator Email */ + @JsonProperty("piEmail") + @JsonPropertyDescription("Principal Investigator Email") + private String piEmail; + /** The user creating the dataset submission (Required) */ @JsonProperty("dataSubmitterUserId") @JsonPropertyDescription("The user creating the dataset submission") @@ -379,6 +385,18 @@ public void setPiName(String piName) { this.piName = piName; } + /** Principal Investigator Email */ + @JsonProperty("piEmail") + public String getPiEmail() { + return piEmail; + } + + /** Principal Investigator Email */ + @JsonProperty("piEmail") + public void setPiEmail(String piEmail) { + this.piEmail = piEmail; + } + /** The user creating the dataset submission (Required) */ @JsonProperty("dataSubmitterUserId") public Integer getDataSubmitterUserId() { @@ -839,6 +857,10 @@ public String toString() { sb.append('='); sb.append(((this.piName == null) ? "" : this.piName)); sb.append(','); + sb.append("piEmail"); + sb.append('='); + sb.append(((this.piEmail == null) ? "" : this.piEmail)); + sb.append(','); sb.append("dataSubmitterUserId"); sb.append('='); sb.append(((this.dataSubmitterUserId == null) ? "" : this.dataSubmitterUserId)); @@ -1102,6 +1124,7 @@ public int hashCode() { ((result * 31) + ((this.nihICsSupportingStudy == null) ? 0 : this.nihICsSupportingStudy.hashCode())); result = ((result * 31) + ((this.piName == null) ? 0 : this.piName.hashCode())); + result = ((result * 31) + ((this.piEmail == null) ? 0 : this.piEmail.hashCode())); result = ((result * 31) + ((this.dbGaPPhsID == null) ? 0 : this.dbGaPPhsID.hashCode())); result = ((result * 31) @@ -1409,6 +1432,9 @@ public boolean equals(Object other) { && ((this.piName == rhs.piName) || ((this.piName != null) && this.piName.equals(rhs.piName)))) + && ((this.piEmail == rhs.piEmail) + || ((this.piEmail != null) + && this.piEmail.equals(rhs.piEmail))) && ((this.dbGaPPhsID == rhs.dbGaPPhsID) || ((this.dbGaPPhsID != null) && this.dbGaPPhsID.equals(rhs.dbGaPPhsID)))) diff --git a/src/main/java/org/broadinstitute/consent/http/models/dataset_registration_v1/builder/SchemaFromStudy.java b/src/main/java/org/broadinstitute/consent/http/models/dataset_registration_v1/builder/SchemaFromStudy.java index a84e6cd3c9..9532d820b1 100644 --- a/src/main/java/org/broadinstitute/consent/http/models/dataset_registration_v1/builder/SchemaFromStudy.java +++ b/src/main/java/org/broadinstitute/consent/http/models/dataset_registration_v1/builder/SchemaFromStudy.java @@ -67,6 +67,7 @@ public DatasetRegistrationSchemaV1 build(Study study) { findStringPropValue(study.getProperties(), phenotypeIndication)); schemaV1.setSpecies(findStringPropValue(study.getProperties(), species)); schemaV1.setPiName(study.getPiName()); + schemaV1.setPiEmail(study.getPiEmail()); schemaV1.setDataSubmitterUserId(study.getCreateUserId()); schemaV1.setDataCustodianEmail( findListStringPropValue(study.getProperties(), dataCustodianEmail)); diff --git a/src/main/java/org/broadinstitute/consent/http/service/DatasetRegistrationService.java b/src/main/java/org/broadinstitute/consent/http/service/DatasetRegistrationService.java index b5b85a1f9f..1041d22094 100644 --- a/src/main/java/org/broadinstitute/consent/http/service/DatasetRegistrationService.java +++ b/src/main/java/org/broadinstitute/consent/http/service/DatasetRegistrationService.java @@ -167,6 +167,7 @@ public Study updateStudyFromRegistration( registration.getStudyDescription(), registration.getDataTypes(), registration.getPiName(), + registration.getPiEmail(), registration.getPublicVisibility(), user.getUserId(), studyProps, @@ -250,6 +251,7 @@ private DatasetServiceDAO.StudyInsert createStudyInsert( registration.getStudyDescription(), registration.getDataTypes(), registration.getPiName(), + registration.getPiEmail(), registration.getPublicVisibility(), user.getUserId(), convertRegistrationToStudyProperties(registration), diff --git a/src/main/java/org/broadinstitute/consent/http/service/DatasetService.java b/src/main/java/org/broadinstitute/consent/http/service/DatasetService.java index cb5a26e4af..eccb4aecc7 100644 --- a/src/main/java/org/broadinstitute/consent/http/service/DatasetService.java +++ b/src/main/java/org/broadinstitute/consent/http/service/DatasetService.java @@ -714,6 +714,7 @@ private Integer updateStudyFromConversion( study.getName(), study.getDescription(), study.getPiName(), + study.getPiEmail(), study.getDataTypes(), study.getPublicVisibility(), userId, @@ -727,6 +728,7 @@ private Integer updateStudyFromConversion( studyConversion.getName(), studyConversion.getDescription(), studyConversion.getPiName(), + studyConversion.getPiEmail(), studyConversion.getDataTypes(), studyConversion.getPublicVisibility(), userId, diff --git a/src/main/java/org/broadinstitute/consent/http/service/dao/DatasetServiceDAO.java b/src/main/java/org/broadinstitute/consent/http/service/dao/DatasetServiceDAO.java index 4f13daadd9..6b6f935ccd 100644 --- a/src/main/java/org/broadinstitute/consent/http/service/dao/DatasetServiceDAO.java +++ b/src/main/java/org/broadinstitute/consent/http/service/dao/DatasetServiceDAO.java @@ -223,6 +223,7 @@ private Integer executeInsertStudy(Handle handle, StudyInsert insert) { insert.name, insert.description, insert.piName, + insert.piEmail, insert.dataTypes, insert.publicVisibility, insert.userId, @@ -291,6 +292,7 @@ private void executeUpdateStudy(Handle handle, StudyUpdate update, boolean repla update.name, update.description, update.piName, + update.piEmail, update.dataTypes, update.publicVisibility, update.userId, @@ -459,6 +461,7 @@ private StudyUpdate convertToStudyUpdate(Study study, User user, StudyPatch patc patch.description() != null ? patch.description() : study.getDescription(), patch.dataTypes() != null ? patch.dataTypes() : study.getDataTypes(), patch.piName() != null ? patch.piName() : study.getPiName(), + patch.piEmail() != null ? patch.piEmail() : study.getPiEmail(), patch.publicVisibility() != null ? patch.publicVisibility() : study.getPublicVisibility(), @@ -698,6 +701,7 @@ public record StudyInsert( String description, List dataTypes, String piName, + String piEmail, Boolean publicVisibility, Integer userId, List props, @@ -709,6 +713,7 @@ public record StudyUpdate( String description, List dataTypes, String piName, + String piEmail, Boolean publicVisibility, Integer userId, List props, diff --git a/src/main/resources/assets/schemas/Study.yaml b/src/main/resources/assets/schemas/Study.yaml index 5eb8e54714..af271182ea 100644 --- a/src/main/resources/assets/schemas/Study.yaml +++ b/src/main/resources/assets/schemas/Study.yaml @@ -18,6 +18,10 @@ properties: piName: type: string description: The name of the PI of the study. + piEmail: + type: string + format: email + description: Principal Investigator Email publicVisibility: type: boolean description: Whether this study is publicly visible or not. diff --git a/src/main/resources/changelog-master.xml b/src/main/resources/changelog-master.xml index de3ea7b1ea..1beafcaa75 100644 --- a/src/main/resources/changelog-master.xml +++ b/src/main/resources/changelog-master.xml @@ -248,4 +248,5 @@ + diff --git a/src/main/resources/changesets/changelog-consent-2026-06-02-study-pi-email.xml b/src/main/resources/changesets/changelog-consent-2026-06-02-study-pi-email.xml new file mode 100644 index 0000000000..8b7b3653a8 --- /dev/null +++ b/src/main/resources/changesets/changelog-consent-2026-06-02-study-pi-email.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/main/resources/dataset-registration-schema_v1.json b/src/main/resources/dataset-registration-schema_v1.json index 382a0b59c9..d16bd9ae36 100644 --- a/src/main/resources/dataset-registration-schema_v1.json +++ b/src/main/resources/dataset-registration-schema_v1.json @@ -157,6 +157,12 @@ "description" : "Principal Investigator Name", "minLength" : 1 }, + "piEmail" : { + "type" : "string", + "format" : "email", + "label" : "Principal Investigator Email", + "description" : "Principal Investigator Email" + }, "dataSubmitterUserId" : { "type" : "integer", "label" : "Data Submitter", diff --git a/src/test/java/org/broadinstitute/consent/http/db/DatasetDAOTest.java b/src/test/java/org/broadinstitute/consent/http/db/DatasetDAOTest.java index 1f2e5fb657..0c03bcb37a 100644 --- a/src/test/java/org/broadinstitute/consent/http/db/DatasetDAOTest.java +++ b/src/test/java/org/broadinstitute/consent/http/db/DatasetDAOTest.java @@ -1519,6 +1519,7 @@ private Study insertStudyWithProperties(User user) { name, description, piName, + null, dataTypes, publicVisibility, user.getUserId(), diff --git a/src/test/java/org/broadinstitute/consent/http/db/StudyDAOTest.java b/src/test/java/org/broadinstitute/consent/http/db/StudyDAOTest.java index d933732dcb..73b87bfe59 100644 --- a/src/test/java/org/broadinstitute/consent/http/db/StudyDAOTest.java +++ b/src/test/java/org/broadinstitute/consent/http/db/StudyDAOTest.java @@ -53,6 +53,7 @@ void testCreateAndFindStudy() { name, description, piName, + null, dataTypes, publicVisibility, u.getUserId(), @@ -63,6 +64,7 @@ void testCreateAndFindStudy() { RandomStringUtils.randomAlphabetic(20), description, piName, + null, dataTypes, publicVisibility, u.getUserId(), @@ -101,6 +103,7 @@ void testStudyProps() { name, description, piName, + null, dataTypes, publicVisibility, u.getUserId(), @@ -112,6 +115,7 @@ void testStudyProps() { RandomStringUtils.randomAlphabetic(20), description, piName, + null, dataTypes, publicVisibility, u.getUserId(), @@ -156,7 +160,15 @@ void testAlternativeDataSharingPlan() { UUID uuid = UUID.randomUUID(); Integer id = studyDAO.insertStudy( - "name", "description", "asdf", List.of(), true, u.getUserId(), Instant.now(), uuid); + "name", + "description", + "asdf", + null, + List.of(), + true, + u.getUserId(), + Instant.now(), + uuid); FileStorageObject fso = createFileStorageObject(uuid.toString(), FileCategory.ALTERNATIVE_DATA_SHARING_PLAN); @@ -285,6 +297,7 @@ void testUpdateStudy() { newName, newDescription, newPiName, + null, newDataTypes, true, user.getUserId(), @@ -419,6 +432,7 @@ private Study insertStudyWithProperties() { name, description, piName, + null, dataTypes, publicVisibility, u.getUserId(), diff --git a/src/test/java/org/broadinstitute/consent/http/models/StudyConversionTest.java b/src/test/java/org/broadinstitute/consent/http/models/StudyConversionTest.java new file mode 100644 index 0000000000..e98cc2b500 --- /dev/null +++ b/src/test/java/org/broadinstitute/consent/http/models/StudyConversionTest.java @@ -0,0 +1,229 @@ +package org.broadinstitute.consent.http.models; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Collection; +import java.util.List; +import org.broadinstitute.consent.http.AbstractTestHelper; +import org.broadinstitute.consent.http.enumeration.PropertyType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class StudyConversionTest extends AbstractTestHelper { + + // ------------------------------------------------------------------------- + // createNewStudyStub() + // ------------------------------------------------------------------------- + + @Test + void testCreateNewStudyStub_allFieldsPopulated() { + StudyConversion conversion = new StudyConversion(); + conversion.setName(randomAlphabetic(10)); + conversion.setDescription(randomAlphabetic(20)); + conversion.setPublicVisibility(true); + conversion.setPiName(randomAlphabetic(10)); + conversion.setPiEmail("pi@example.test"); + conversion.setDataTypes(List.of("Genomics", "Proteomics")); + + Study stub = conversion.createNewStudyStub(); + + assertNotNull(stub); + assertEquals(conversion.getName(), stub.getName()); + assertEquals(conversion.getDescription(), stub.getDescription()); + assertEquals(conversion.getPublicVisibility(), stub.getPublicVisibility()); + assertEquals(conversion.getPiName(), stub.getPiName()); + assertEquals(conversion.getPiEmail(), stub.getPiEmail()); + assertEquals(conversion.getDataTypes(), stub.getDataTypes()); + } + + @Test + void testCreateNewStudyStub_nullableFieldsAreNull() { + StudyConversion conversion = new StudyConversion(); + // Leave all fields at their default null values + + Study stub = conversion.createNewStudyStub(); + + assertNotNull(stub); + assertNull(stub.getName()); + assertNull(stub.getDescription()); + assertNull(stub.getPublicVisibility()); + assertNull(stub.getPiName()); + assertNull(stub.getPiEmail()); + assertNull(stub.getDataTypes()); + } + + @Test + void testCreateNewStudyStub_publicVisibilityFalse() { + StudyConversion conversion = new StudyConversion(); + conversion.setName(randomAlphabetic(8)); + conversion.setPublicVisibility(false); + + Study stub = conversion.createNewStudyStub(); + + assertNotNull(stub); + assertEquals(Boolean.FALSE, stub.getPublicVisibility()); + } + + @Test + void testCreateNewStudyStub_dataTypesPreservedExactly() { + List dataTypes = List.of("WholeGenome", "RNASeq", "Methylation"); + StudyConversion conversion = new StudyConversion(); + conversion.setDataTypes(dataTypes); + + Study stub = conversion.createNewStudyStub(); + + assertEquals(dataTypes, stub.getDataTypes()); + } + + // ------------------------------------------------------------------------- + // getStudyProperties() + // ------------------------------------------------------------------------- + + @Test + void testGetStudyProperties_allThreePresent() { + StudyConversion conversion = new StudyConversion(); + conversion.setPhenotype("Hypertension"); + conversion.setSpecies("Homo sapiens"); + conversion.setNihAnvilUse("AnVIL_Yes"); + + Collection props = conversion.getStudyProperties(); + + assertNotNull(props); + assertEquals(3, props.size()); + assertTrue(containsProperty(props, "phenotypeIndication", "Hypertension", PropertyType.String)); + assertTrue(containsProperty(props, "species", "Homo sapiens", PropertyType.String)); + assertTrue(containsProperty(props, "nihAnvilUse", "AnVIL_Yes", PropertyType.String)); + } + + @Test + void testGetStudyProperties_emptyWhenNoFieldsSet() { + StudyConversion conversion = new StudyConversion(); + + Collection props = conversion.getStudyProperties(); + + assertNotNull(props); + assertTrue(props.isEmpty()); + } + + @Test + void testGetStudyProperties_onlyPhenotype() { + StudyConversion conversion = new StudyConversion(); + conversion.setPhenotype("Diabetes"); + + Collection props = conversion.getStudyProperties(); + + assertEquals(1, props.size()); + assertTrue(containsProperty(props, "phenotypeIndication", "Diabetes", PropertyType.String)); + } + + @Test + void testGetStudyProperties_onlySpecies() { + StudyConversion conversion = new StudyConversion(); + conversion.setSpecies("Mus musculus"); + + Collection props = conversion.getStudyProperties(); + + assertEquals(1, props.size()); + assertTrue(containsProperty(props, "species", "Mus musculus", PropertyType.String)); + } + + @Test + void testGetStudyProperties_onlyNihAnvilUse() { + StudyConversion conversion = new StudyConversion(); + conversion.setNihAnvilUse("AnVIL_No"); + + Collection props = conversion.getStudyProperties(); + + assertEquals(1, props.size()); + assertTrue(containsProperty(props, "nihAnvilUse", "AnVIL_No", PropertyType.String)); + } + + @Test + void testGetStudyProperties_phenotypeAndSpeciesOnly() { + StudyConversion conversion = new StudyConversion(); + conversion.setPhenotype("Obesity"); + conversion.setSpecies("Rattus norvegicus"); + + Collection props = conversion.getStudyProperties(); + + assertEquals(2, props.size()); + assertTrue(containsProperty(props, "phenotypeIndication", "Obesity", PropertyType.String)); + assertTrue(containsProperty(props, "species", "Rattus norvegicus", PropertyType.String)); + } + + // ------------------------------------------------------------------------- + // Getters / setters (round-trip) + // ------------------------------------------------------------------------- + + @Test + void testSettersAndGetters_roundTrip() { + StudyConversion c = new StudyConversion(); + + String name = randomAlphabetic(8); + String description = randomAlphabetic(20); + List dataTypes = List.of(randomAlphabetic(5), randomAlphabetic(5)); + String phenotype = randomAlphabetic(10); + String species = randomAlphabetic(10); + String piName = randomAlphabetic(10); + String piEmail = "pi@test.example"; + String dataSubmitterEmail = "submitter@test.example"; + Boolean publicVisibility = true; + String nihAnvilUse = "AnVIL_Yes"; + String datasetName = randomAlphabetic(10); + DataUse dataUse = new DataUse(); + dataUse.setGeneralUse(true); + Integer dacId = 42; + String dataLocation = "gs://bucket/path"; + String url = "https://example.test/study"; + Integer numberOfParticipants = 500; + + c.setName(name); + c.setDescription(description); + c.setDataTypes(dataTypes); + c.setPhenotype(phenotype); + c.setSpecies(species); + c.setPiName(piName); + c.setPiEmail(piEmail); + c.setDataSubmitterEmail(dataSubmitterEmail); + c.setPublicVisibility(publicVisibility); + c.setNihAnvilUse(nihAnvilUse); + c.setDatasetName(datasetName); + c.setDataUse(dataUse); + c.setDacId(dacId); + c.setDataLocation(dataLocation); + c.setUrl(url); + c.setNumberOfParticipants(numberOfParticipants); + + assertEquals(name, c.getName()); + assertEquals(description, c.getDescription()); + assertEquals(dataTypes, c.getDataTypes()); + assertEquals(phenotype, c.getPhenotype()); + assertEquals(species, c.getSpecies()); + assertEquals(piName, c.getPiName()); + assertEquals(piEmail, c.getPiEmail()); + assertEquals(dataSubmitterEmail, c.getDataSubmitterEmail()); + assertEquals(publicVisibility, c.getPublicVisibility()); + assertEquals(nihAnvilUse, c.getNihAnvilUse()); + assertEquals(datasetName, c.getDatasetName()); + assertEquals(dataUse, c.getDataUse()); + assertEquals(dacId, c.getDacId()); + assertEquals(dataLocation, c.getDataLocation()); + assertEquals(url, c.getUrl()); + assertEquals(numberOfParticipants, c.getNumberOfParticipants()); + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + private boolean containsProperty( + Collection props, String key, Object value, PropertyType type) { + return props.stream() + .anyMatch(p -> key.equals(p.getKey()) && value.equals(p.getValue()) && type == p.getType()); + } +} diff --git a/src/test/java/org/broadinstitute/consent/http/models/StudyPatchTest.java b/src/test/java/org/broadinstitute/consent/http/models/StudyPatchTest.java index 110a6e8f0c..de11a1701d 100644 --- a/src/test/java/org/broadinstitute/consent/http/models/StudyPatchTest.java +++ b/src/test/java/org/broadinstitute/consent/http/models/StudyPatchTest.java @@ -43,34 +43,35 @@ void testFromJson(String json) { void testIsPatchableNoValues() { Study study = mockStudy(); StudyPatch patch = - new StudyPatch(null, null, null, null, null, null, null, null, null, null, null); + new StudyPatch(null, null, null, null, null, null, null, null, null, null, null, null); assertFalse(patch.isPatchable(study)); } @Test void testIsPatchableSameValues() { - Study study = mockStudy(); + Study mockStudy = mockStudy(); StudyPatch patch = new StudyPatch( - study.getName(), + mockStudy.getName(), StudyType.OBSERVATIONAL, - study.getDescription(), - study.getDataTypes(), + mockStudy.getDescription(), + mockStudy.getDataTypes(), "CANCER", "HUMAN", - study.getPiName(), + mockStudy.getPiName(), + mockStudy.getPiEmail(), List.of("EMAIL1", "EMAIL2"), "01/01/2020", "01/01/2020", - study.getPublicVisibility()); - assertFalse(patch.isPatchable(study)); + mockStudy.getPublicVisibility()); + assertFalse(patch.isPatchable(mockStudy)); } @Test void testIsPatchableName() { Study study = mockStudy(); StudyPatch patch = - new StudyPatch("Name", null, null, null, null, null, null, null, null, null, null); + new StudyPatch("Name", null, null, null, null, null, null, null, null, null, null, null); assertTrue(patch.isPatchable(study)); } @@ -79,7 +80,7 @@ void testIsPatchableStudyType() { Study study = mockStudy(); StudyPatch patch = new StudyPatch( - null, StudyType.ANALYTICAL, null, null, null, null, null, null, null, null, null); + null, StudyType.ANALYTICAL, null, null, null, null, null, null, null, null, null, null); assertTrue(patch.isPatchable(study)); } @@ -87,7 +88,8 @@ void testIsPatchableStudyType() { void testIsPatchableDescription() { Study study = mockStudy(); StudyPatch patch = - new StudyPatch(null, null, "Description", null, null, null, null, null, null, null, null); + new StudyPatch( + null, null, "Description", null, null, null, null, null, null, null, null, null); assertTrue(patch.isPatchable(study)); } @@ -96,7 +98,18 @@ void testIsPatchableDataTypes() { Study study = mockStudy(); StudyPatch patch = new StudyPatch( - null, null, null, List.of("type1", "type2"), null, null, null, null, null, null, null); + null, + null, + null, + List.of("type1", "type2"), + null, + null, + null, + null, + null, + null, + null, + null); assertTrue(patch.isPatchable(study)); } @@ -104,7 +117,8 @@ void testIsPatchableDataTypes() { void testIsPatchablePhenotype() { Study study = mockStudy(); StudyPatch patch = - new StudyPatch(null, null, null, null, "New Phenotype", null, null, null, null, null, null); + new StudyPatch( + null, null, null, null, "New Phenotype", null, null, null, null, null, null, null); assertTrue(patch.isPatchable(study)); } @@ -112,7 +126,8 @@ void testIsPatchablePhenotype() { void testIsPatchableSpecies() { Study study = mockStudy(); StudyPatch patch = - new StudyPatch(null, null, null, null, null, "New Species", null, null, null, null, null); + new StudyPatch( + null, null, null, null, null, "New Species", null, null, null, null, null, null); assertTrue(patch.isPatchable(study)); } @@ -120,7 +135,17 @@ void testIsPatchableSpecies() { void testIsPatchablePIName() { Study study = mockStudy(); StudyPatch patch = - new StudyPatch(null, null, null, null, null, null, "New PI Name", null, null, null, null); + new StudyPatch( + null, null, null, null, null, null, "New PI Name", null, null, null, null, null); + assertTrue(patch.isPatchable(study)); + } + + @Test + void testIsPatchablePIEmail() { + Study study = mockStudy(); + StudyPatch patch = + new StudyPatch( + null, null, null, null, null, null, null, "New PI Email", null, null, null, null); assertTrue(patch.isPatchable(study)); } @@ -136,6 +161,7 @@ void testIsPatchableDataCustodians() { null, null, null, + null, List.of("new_email1", "new_email2"), null, null, @@ -147,7 +173,8 @@ void testIsPatchableDataCustodians() { void testIsPatchableTargetDeliveryDate() { Study study = mockStudy(); StudyPatch patch = - new StudyPatch(null, null, null, null, null, null, null, null, "New Date", null, null); + new StudyPatch( + null, null, null, null, null, null, null, null, null, "New Date", null, null); assertTrue(patch.isPatchable(study)); } @@ -155,7 +182,8 @@ void testIsPatchableTargetDeliveryDate() { void testIsPatchableTargetReleaseDate() { Study study = mockStudy(); StudyPatch patch = - new StudyPatch(null, null, null, null, null, null, null, null, null, "New Date", null); + new StudyPatch( + null, null, null, null, null, null, null, null, null, null, "New Date", null); assertTrue(patch.isPatchable(study)); } @@ -163,7 +191,7 @@ void testIsPatchableTargetReleaseDate() { void testIsPatchablePublicVisibility() { Study study = mockStudy(); StudyPatch patch = - new StudyPatch(null, null, null, null, null, null, null, null, null, null, false); + new StudyPatch(null, null, null, null, null, null, null, null, null, null, null, false); assertTrue(patch.isPatchable(study)); } @@ -173,6 +201,7 @@ private Study mockStudy() { study.setDescription(randomAlphabetic(20)); study.setDataTypes(List.of(randomAlphabetic(20), randomAlphabetic(20))); study.setPiName(randomAlphabetic(20)); + study.setPiEmail(randomAlphabetic(20)); study.setPublicVisibility(true); study.addProperties( new StudyProperty(STUDY_TYPE, StudyType.OBSERVATIONAL.value(), PropertyType.String), diff --git a/src/test/java/org/broadinstitute/consent/http/service/DatasetRegistrationServiceTest.java b/src/test/java/org/broadinstitute/consent/http/service/DatasetRegistrationServiceTest.java index 6ca0ff7bd7..c042e26628 100644 --- a/src/test/java/org/broadinstitute/consent/http/service/DatasetRegistrationServiceTest.java +++ b/src/test/java/org/broadinstitute/consent/http/service/DatasetRegistrationServiceTest.java @@ -19,6 +19,7 @@ import com.google.cloud.storage.BlobId; import freemarker.template.TemplateException; import jakarta.ws.rs.BadRequestException; +import jakarta.ws.rs.InternalServerErrorException; import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.ServerErrorException; import jakarta.ws.rs.core.MediaType; @@ -51,6 +52,7 @@ import org.broadinstitute.consent.http.models.Dac; import org.broadinstitute.consent.http.models.DataUse; import org.broadinstitute.consent.http.models.Dataset; +import org.broadinstitute.consent.http.models.DatasetPatch; import org.broadinstitute.consent.http.models.DatasetProperty; import org.broadinstitute.consent.http.models.FileStorageObject; import org.broadinstitute.consent.http.models.Study; @@ -182,6 +184,7 @@ void testInsertCompleteDatasetRegistration() throws Exception { assertEquals(schema.getStudyName(), capturedStudyInsert.name()); assertEquals(schema.getPiName(), capturedStudyInsert.piName()); + assertEquals(schema.getPiEmail(), capturedStudyInsert.piEmail()); assertEquals(schema.getStudyDescription(), capturedStudyInsert.description()); assertEquals(schema.getDataTypes(), capturedStudyInsert.dataTypes()); assertEquals(schema.getPublicVisibility(), capturedStudyInsert.publicVisibility()); @@ -348,6 +351,7 @@ void testInsertMinimumDatasetRegistration() throws Exception { assertEquals(schema.getStudyName(), capturedStudyInsert.name()); assertEquals(schema.getPiName(), capturedStudyInsert.piName()); + assertEquals(schema.getPiEmail(), capturedStudyInsert.piEmail()); assertEquals(schema.getStudyDescription(), capturedStudyInsert.description()); assertEquals(schema.getDataTypes(), capturedStudyInsert.dataTypes()); assertEquals(schema.getPublicVisibility(), capturedStudyInsert.publicVisibility()); @@ -628,6 +632,7 @@ void testInsertMultipleDatasetRegistration() throws Exception { assertEquals(schema.getStudyName(), capturedStudyInsert.name()); assertEquals(schema.getPiName(), capturedStudyInsert.piName()); + assertEquals(schema.getPiEmail(), capturedStudyInsert.piEmail()); assertEquals(schema.getStudyDescription(), capturedStudyInsert.description()); assertEquals(schema.getDataTypes(), capturedStudyInsert.dataTypes()); assertEquals(schema.getPublicVisibility(), capturedStudyInsert.publicVisibility()); @@ -953,6 +958,123 @@ void testDeleteFile_No_User() { assertDoesNotThrow(() -> datasetRegistrationService.deleteFile(new FileStorageObject(), null)); } + @Test + void testPatchDatasetSuccess() throws Exception { + int datasetId = 1; + User user = new User(); + DatasetPatch patch = mock(); + Dataset dataset = new Dataset(); + dataset.setDatasetId(datasetId); + when(datasetDAO.findDatasetById(datasetId)).thenReturn(dataset); + + Dataset result = datasetRegistrationService.patchDataset(datasetId, user, patch); + + assertEquals(dataset, result); + verify(datasetServiceDAO, times(1)).patchDataset(datasetId, user, patch); + } + + @Test + void testPatchDatasetThrowsOnSQLException() throws Exception { + int datasetId = 1; + User user = new User(); + DatasetPatch patch = mock(); + doThrow(new SQLException("boom")).when(datasetServiceDAO).patchDataset(anyInt(), any(), any()); + + assertThrows( + InternalServerErrorException.class, + () -> datasetRegistrationService.patchDataset(datasetId, user, patch)); + verify(datasetDAO, never()).findDatasetById(anyInt()); + } + + @Test + void testFindStudyByIdFound() { + Study study = new Study(); + study.setStudyId(1); + when(studyDAO.findStudyById(1)).thenReturn(study); + + assertEquals(study, datasetRegistrationService.findStudyById(1)); + } + + @Test + void testFindStudyByIdNotFound() { + when(studyDAO.findStudyById(1)).thenReturn(null); + + assertThrows(NotFoundException.class, () -> datasetRegistrationService.findStudyById(1)); + } + + @Test + void testUpdateDatasetNullNameThrows() { + User user = mock(); + org.broadinstitute.consent.http.models.DatasetUpdate update = + new org.broadinstitute.consent.http.models.DatasetUpdate(null, 1, List.of()); + + assertThrows( + BadRequestException.class, + () -> datasetRegistrationService.updateDataset(1, user, update, Map.of())); + } + + @Test + void testUpdateDatasetNullDacIdThrows() { + User user = mock(); + org.broadinstitute.consent.http.models.DatasetUpdate update = + new org.broadinstitute.consent.http.models.DatasetUpdate( + randomAlphabetic(10), null, List.of()); + + assertThrows( + BadRequestException.class, + () -> datasetRegistrationService.updateDataset(1, user, update, Map.of())); + } + + @Test + void testUpdateDatasetDacIdMismatchThrows() { + User user = mock(); + Dataset dataset = new Dataset(); + dataset.setDatasetId(1); + dataset.setDacId(1); + when(datasetDAO.findDatasetById(1)).thenReturn(dataset); + org.broadinstitute.consent.http.models.DatasetUpdate update = + new org.broadinstitute.consent.http.models.DatasetUpdate( + randomAlphabetic(10), 2, List.of()); + + assertThrows( + BadRequestException.class, + () -> datasetRegistrationService.updateDataset(1, user, update, Map.of())); + } + + @Test + void testUpdateStudyFromRegistrationCapturesPiEmail() throws Exception { + User user = mock(); + DatasetRegistrationSchemaV1 schema = createRandomCompleteDatasetRegistration(user); + Study study = mock(); + + when(dacDAO.findById(any())).thenReturn(new Dac()); + ArgumentCaptor studyUpdateCaptor = + ArgumentCaptor.forClass(DatasetServiceDAO.StudyUpdate.class); + when(datasetServiceDAO.updateStudy(studyUpdateCaptor.capture(), any(), any())) + .thenReturn(study); + when(study.getDatasets()).thenReturn(Set.of()); + + datasetRegistrationService.updateStudyFromRegistration(1, schema, user, Map.of()); + + assertEquals(schema.getPiName(), studyUpdateCaptor.getValue().piName()); + assertEquals(schema.getPiEmail(), studyUpdateCaptor.getValue().piEmail()); + } + + @Test + void testSendDatasetSubmittedEmailsMembersButNoChairs() throws Exception { + Dac dac = mock(); + Dataset dataset = new Dataset(); + dataset.setDacId(1); + User member = new User(); + member.setMemberRole(); + + when(dacDAO.findById(any())).thenReturn(dac); + when(dacDAO.findMembersByDacId(any())).thenReturn(List.of(member)); + + datasetRegistrationService.sendDatasetSubmittedEmails(List.of(dataset)); + verify(emailService, never()).sendMessage(any(DatasetSubmittedMessage.class), any()); + } + private void assertDataUse(ConsentGroup consentGroup, DataUse dataUse) { assertEquals(consentGroup.getCol(), dataUse.getCollaboratorRequired()); assertEquals(consentGroup.getDiseaseSpecificUse(), dataUse.getDiseaseRestrictions()); @@ -1000,6 +1122,7 @@ private DatasetRegistrationSchemaV1 createRandomMinimumDatasetRegistration(User schemaV1.setPhenotypeIndication(randomAlphabetic(10)); schemaV1.setSpecies(randomAlphabetic(10)); schemaV1.setPiName(randomAlphabetic(10)); + schemaV1.setPiEmail(randomAlphabetic(10) + "@domain.org"); when(user.getUserId()).thenReturn(1); schemaV1.setDataSubmitterUserId(user.getUserId()); schemaV1.setDataCustodianEmail(List.of(randomAlphabetic(10) + "@domain.org")); @@ -1028,6 +1151,7 @@ private DatasetRegistrationSchemaV1 createAccessManagementRegistrationNoDacId(Us schemaV1.setPhenotypeIndication(randomAlphabetic(10)); schemaV1.setSpecies(randomAlphabetic(10)); schemaV1.setPiName(randomAlphabetic(10)); + schemaV1.setPiEmail(randomAlphabetic(10) + "@domain.org"); when(user.getUserId()).thenReturn(1); schemaV1.setDataSubmitterUserId(user.getUserId()); schemaV1.setDataCustodianEmail(List.of(randomAlphabetic(10) + "@domain.org")); @@ -1055,6 +1179,7 @@ private DatasetRegistrationSchemaV1 createRandomMultipleDatasetRegistration(User schemaV1.setPhenotypeIndication(randomAlphabetic(10)); schemaV1.setSpecies(randomAlphabetic(10)); schemaV1.setPiName(randomAlphabetic(10)); + schemaV1.setPiEmail(randomAlphabetic(10) + "@domain.org"); when(user.getUserId()).thenReturn(1); schemaV1.setDataSubmitterUserId(user.getUserId()); schemaV1.setDataCustodianEmail(List.of(randomAlphabetic(10) + "@domain.org")); @@ -1095,6 +1220,7 @@ private DatasetRegistrationSchemaV1 createRandomCompleteDatasetRegistration(User schemaV1.setPhenotypeIndication(randomAlphabetic(10)); schemaV1.setSpecies(randomAlphabetic(10)); schemaV1.setPiName(randomAlphabetic(10)); + schemaV1.setPiEmail(randomAlphabetic(10) + "@domain.org"); when(user.getUserId()).thenReturn(1); schemaV1.setDataSubmitterUserId(user.getUserId()); schemaV1.setDataCustodianEmail(List.of(randomAlphabetic(10) + "@domain.org")); diff --git a/src/test/java/org/broadinstitute/consent/http/service/DatasetServiceTest.java b/src/test/java/org/broadinstitute/consent/http/service/DatasetServiceTest.java index 66864984ed..926e969eaa 100644 --- a/src/test/java/org/broadinstitute/consent/http/service/DatasetServiceTest.java +++ b/src/test/java/org/broadinstitute/consent/http/service/DatasetServiceTest.java @@ -13,6 +13,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -23,6 +24,8 @@ import com.google.gson.Gson; import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.ForbiddenException; +import jakarta.ws.rs.InternalServerErrorException; +import jakarta.ws.rs.NotAuthorizedException; import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.core.Response; import java.sql.Timestamp; @@ -51,8 +54,11 @@ import org.broadinstitute.consent.http.models.DataUseBuilder; import org.broadinstitute.consent.http.models.Dataset; import org.broadinstitute.consent.http.models.DatasetAuthorizationReader; +import org.broadinstitute.consent.http.models.DatasetProperty; import org.broadinstitute.consent.http.models.DatasetStudySummary; +import org.broadinstitute.consent.http.models.Dictionary; import org.broadinstitute.consent.http.models.Study; +import org.broadinstitute.consent.http.models.StudyConversion; import org.broadinstitute.consent.http.models.StudyPatch; import org.broadinstitute.consent.http.models.StudyProperty; import org.broadinstitute.consent.http.models.User; @@ -1072,12 +1078,550 @@ void testPatchStudy() throws Exception { null, null, null, + null, true); when(datasetServiceDAO.patchStudy(study, user, patch)).thenReturn(study); when(studyDAO.findStudyById(study.getStudyId())).thenReturn(study); assertDoesNotThrow(() -> datasetService.patchStudy(study.getStudyId(), user, patch)); } + // ==================== convertDatasetToStudy ==================== + + @Test + void testConvertDatasetToStudy_NonAdminThrows() { + User nonAdmin = new User(); + nonAdmin.setUserId(2); + Dataset dataset = new Dataset(); + StudyConversion conversion = new StudyConversion(); + assertThrows( + NotAuthorizedException.class, + () -> datasetService.convertDatasetToStudy(nonAdmin, dataset, conversion)); + } + + @Test + void testConvertDatasetToStudy_AdminCreatesNewStudy() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + StudyConversion conversion = new StudyConversion(); + conversion.setName("New Study"); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + + when(studyDAO.findStudyByName("New Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + + Study result = datasetService.convertDatasetToStudy(admin, dataset, conversion); + + assertNotNull(result); + verify(studyDAO).insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any()); + verify(datasetDAO).updateStudyId(10, newStudyId); + } + + @Test + void testConvertDatasetToStudy_DacIdUpdated() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setDacId(99); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(datasetDAO).updateDatasetDacId(10, 99); + } + + @Test + void testConvertDatasetToStudy_DataUseUpdated() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + DataUse dataUse = new DataUseBuilder().setGeneralUse(true).build(); + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setDataUse(dataUse); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + when(ontologyService.translateDataUse(any(), any())).thenReturn("GRU"); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(datasetServiceDAO).updateDatasetDataUse(eq(admin), eq(dataset), eq(dataUse), any()); + } + + @Test + void testConvertDatasetToStudy_DatasetNameUpdated() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setDatasetName("New Dataset Name"); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(datasetDAO).updateDatasetName(10, "New Dataset Name"); + } + + @Test + void testConvertDatasetToStudy_LegacyPropsInserted() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setPhenotype("Cancer"); + conversion.setSpecies("Human"); + conversion.setNumberOfParticipants(200); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + + Dictionary phenotypeDict = new Dictionary(1, "Phenotype/Indication", false, 1, 1); + Dictionary speciesDict = new Dictionary(2, "Species", false, 2, 2); + Dictionary participantsDict = new Dictionary(3, "# of participants", false, 3, 3); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()) + .thenReturn(List.of(phenotypeDict, speciesDict, participantsDict)); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(datasetDAO, times(3)).insertDatasetProperties(any()); + } + + @Test + void testConvertDatasetToStudy_LegacyPropUpdated() { + User admin = getAdmin(); + DatasetProperty existingPhenotype = new DatasetProperty(); + existingPhenotype.setPropertyName("Phenotype/Indication"); + existingPhenotype.setPropertyKey(1); + Dataset dataset = buildDataset(10, 5); + dataset.setProperties(Set.of(existingPhenotype)); + + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setPhenotype("UpdatedPhenotype"); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + Dictionary phenotypeDict = new Dictionary(1, "Phenotype/Indication", false, 1, 1); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of(phenotypeDict)); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(datasetDAO).updateDatasetProperty(10, 1, "UpdatedPhenotype"); + verify(datasetDAO, never()).insertDatasetProperties(any()); + } + + @Test + void testConvertDatasetToStudy_NewPropsInserted() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setDataLocation("gs://bucket/data"); + conversion.setUrl("https://example.com/study"); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + Dictionary dataLocationDict = new Dictionary(4, "Data Location", false, 4, 4); + Dictionary urlDict = new Dictionary(5, "URL", false, 5, 5); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of(dataLocationDict, urlDict)); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(datasetDAO, times(2)).insertDatasetProperties(any()); + } + + @Test + void testConvertDatasetToStudy_NewPropUpdated() { + User admin = getAdmin(); + DatasetProperty existingDataLocation = new DatasetProperty(); + existingDataLocation.setSchemaProperty("dataLocation"); + existingDataLocation.setPropertyKey(4); + Dataset dataset = buildDataset(10, 5); + dataset.setProperties(Set.of(existingDataLocation)); + + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setDataLocation("gs://new-bucket/data"); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(datasetDAO).updateDatasetProperty(10, 4, "gs://new-bucket/data"); + verify(datasetDAO, never()).insertDatasetProperties(any()); + } + + @Test + void testConvertDatasetToStudy_DataSubmitterEmailFound() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setDataSubmitterEmail("submitter@test.com"); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + User submitter = new User(); + submitter.setUserId(99); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + when(userDAO.findUserByEmail("submitter@test.com")).thenReturn(submitter); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(datasetDAO).updateDatasetCreateUserId(anyInt(), anyInt()); + } + + @Test + void testConvertDatasetToStudy_DataSubmitterEmailNotFound() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setDataSubmitterEmail("unknown@test.com"); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + when(userDAO.findUserByEmail("unknown@test.com")).thenReturn(null); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(datasetDAO, never()).updateDatasetCreateUserId(any(), any()); + } + + @Test + void testConvertDatasetToStudy_AdminUpdatesExistingStudy() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + StudyConversion conversion = new StudyConversion(); + conversion.setName("Existing Study"); + + Study existingStudy = new Study(); + existingStudy.setStudyId(77); + + when(studyDAO.findStudyByName("Existing Study")).thenReturn(existingStudy); + when(studyDAO.findStudyById(77)).thenReturn(existingStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + + Study result = datasetService.convertDatasetToStudy(admin, dataset, conversion); + + assertNotNull(result); + verify(studyDAO).updateStudy(any(), any(), any(), any(), any(), any(), any(), any(), any()); + verify(studyDAO, never()) + .insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any()); + } + + @Test + void testConvertDatasetToStudy_StudyPropsInsertedWhenKeyMissing() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setNihAnvilUse("Yes"); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + // Existing prop has a different key — nihAnvilUse is absent + createdStudy.addProperties( + new StudyProperty("phenotypeIndication", "Cancer", PropertyType.String)); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(studyDAO).insertStudyProperty(eq(newStudyId), eq("nihAnvilUse"), any(), any()); + } + + @Test + void testConvertDatasetToStudy_StudyPropsUpdatedWhenMatching() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, 5); + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + conversion.setNihAnvilUse("Yes"); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + // Existing prop matches exactly — same key, value, and type as the conversion prop + createdStudy.addProperties(new StudyProperty("nihAnvilUse", "Yes", PropertyType.String)); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy(any(), any(), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + + datasetService.convertDatasetToStudy(admin, dataset, conversion); + + verify(studyDAO).updateStudyProperty(eq(newStudyId), eq("nihAnvilUse"), any(), any()); + } + + @Test + void testConvertDatasetToStudy_NullDatasetCreateUserIdFallsBackToAdminId() { + User admin = getAdmin(); + Dataset dataset = buildDataset(10, null); // null createUserId → use admin's userId + StudyConversion conversion = new StudyConversion(); + conversion.setName("Study"); + + int newStudyId = 42; + Study createdStudy = new Study(); + createdStudy.setStudyId(newStudyId); + + when(studyDAO.findStudyByName("Study")).thenReturn(null); + when(studyDAO.insertStudy( + any(), any(), any(), any(), any(), any(), eq(admin.getUserId()), any(), any())) + .thenReturn(newStudyId); + when(studyDAO.findStudyById(newStudyId)).thenReturn(createdStudy); + when(datasetDAO.getDictionaryTerms()).thenReturn(List.of()); + + Study result = datasetService.convertDatasetToStudy(admin, dataset, conversion); + + assertNotNull(result); + verify(studyDAO) + .insertStudy(any(), any(), any(), any(), any(), any(), eq(admin.getUserId()), any(), any()); + } + + // ==================== updateStudyCustodians ==================== + + @Test + void testUpdateStudyCustodians_StudyNotFound() { + when(studyDAO.findStudyById(anyInt())).thenReturn(null); + assertThrows( + NotFoundException.class, () -> datasetService.updateStudyCustodians(mockUser, 99, "[]")); + } + + // ==================== deleteDataset ==================== + + @Test + void testDeleteDataset_NullDataset() throws Exception { + when(datasetDAO.findDatasetById(99)).thenReturn(null); + assertDoesNotThrow(() -> datasetService.deleteDataset(99, 1)); + verify(datasetServiceDAO, never()).deleteDataset(any(), any()); + } + + // ==================== isCreatorCustodianOrAdmin ==================== + + @Test + void testIsCreatorCustodianOrAdmin_Admin() { + User admin = getAdmin(); + Study study = new Study(); + study.setStudyId(1); + study.setCreateUserId(2); + assertTrue(datasetService.isCreatorCustodianOrAdmin(admin, study)); + } + + @Test + void testIsCreatorCustodianOrAdmin_Creator() { + User creator = new User(); + creator.setUserId(5); + creator.setEmail("creator@test.com"); + Study study = new Study(); + study.setStudyId(1); + study.setCreateUserId(creator.getUserId()); + assertTrue(datasetService.isCreatorCustodianOrAdmin(creator, study)); + } + + @Test + void testIsCreatorCustodianOrAdmin_NeitherAdminNorCreatorNorCustodian() { + User user = new User(); + user.setUserId(5); + user.setEmail("user@test.com"); + Study study = new Study(); + study.setStudyId(1); + study.setCreateUserId(99); + assertFalse(datasetService.isCreatorCustodianOrAdmin(user, study)); + } + + // ==================== patchStudy – exception path ==================== + + @Test + void testPatchStudy_ExceptionThrowsInternalServerError() throws Exception { + Study study = new Study(); + study.setStudyId(1); + User user = new User(); + user.setUserId(1); + StudyPatch patch = + new StudyPatch(null, null, null, null, null, null, null, null, null, null, null, null); + + when(studyDAO.findStudyById(1)).thenReturn(study); + when(datasetServiceDAO.patchStudy(any(), any(), any())) + .thenThrow(new RuntimeException("DB error")); + + assertThrows( + InternalServerErrorException.class, () -> datasetService.patchStudy(1, user, patch)); + } + + // ==================== findDatasetsByIds ==================== + + @Test + void testFindDatasetsByIds_AdminSeesAll() { + User admin = getAdmin(); + Dataset d1 = new Dataset(); + d1.setDatasetId(1); + d1.setCreateUserId(99); + + when(datasetDAO.findDatasetsByIdList(anyList())).thenReturn(List.of(d1)); + + List result = datasetService.findDatasetsByIds(admin, List.of(1)); + + assertEquals(1, result.size()); + } + + @Test + void testFindDatasetsByIds_FilteredByVisibility() { + User user = new User(); + user.setUserId(1); + user.setEmail("user@test.com"); + + Study privateStudy = new Study(); + privateStudy.setStudyId(10); + privateStudy.setCreateUserId(99); + privateStudy.setPublicVisibility(false); + + Dataset d1 = new Dataset(); + d1.setDatasetId(1); + d1.setCreateUserId(99); + d1.setStudyId(10); + d1.setStudy(privateStudy); + + when(datasetDAO.findDatasetsByIdList(anyList())).thenReturn(List.of(d1)); + + List result = datasetService.findDatasetsByIds(user, List.of(1)); + + assertEquals(0, result.size()); + } + + // ==================== canReadStudy ==================== + + @Test + void testCanReadStudy_NullStudy() { + User user = new User(); + user.setUserId(1); + assertFalse(datasetService.canReadStudy(user, null)); + } + + @Test + void testCanReadStudy_Admin() { + User admin = getAdmin(); + Study study = new Study(); + study.setPublicVisibility(false); + assertTrue(datasetService.canReadStudy(admin, study)); + } + + @Test + void testCanReadStudy_PublicVisibilityNull() { + User user = new User(); + user.setUserId(1); + Study study = new Study(); + // publicVisibility null → !Boolean.FALSE.equals(null) is true → readable + assertTrue(datasetService.canReadStudy(user, study)); + } + + @Test + void testCanReadStudy_PrivateFalseNotCreatorNorCustodian() { + User user = new User(); + user.setUserId(1); + user.setEmail("user@test.com"); + Study study = new Study(); + study.setCreateUserId(99); + study.setPublicVisibility(false); + assertFalse(datasetService.canReadStudy(user, study)); + } + + // ==================== isCreatorOrCustodian(User, Study) – null study ==================== + + @Test + void testIsCreatorOrCustodian_NullStudy() { + User user = new User(); + user.setUserId(1); + assertFalse(datasetService.isCreatorOrCustodian(user, (Study) null)); + } + + // ==================== Helper ==================== // + + private Dataset buildDataset(int datasetId, Integer createUserId) { + Dataset dataset = new Dataset(); + dataset.setDatasetId(datasetId); + dataset.setCreateUserId(createUserId); + dataset.setProperties(Collections.emptySet()); + return dataset; + } + /* Helper functions */ private List getDatasets() { diff --git a/src/test/java/org/broadinstitute/consent/http/service/dao/DatasetServiceDAOTest.java b/src/test/java/org/broadinstitute/consent/http/service/dao/DatasetServiceDAOTest.java index a3660dbf96..0c05a1f128 100644 --- a/src/test/java/org/broadinstitute/consent/http/service/dao/DatasetServiceDAOTest.java +++ b/src/test/java/org/broadinstitute/consent/http/service/dao/DatasetServiceDAOTest.java @@ -223,6 +223,7 @@ void testInsertStudyWithDatasets() throws Exception { randomAlphabetic(10), List.of(randomAlphabetic(10)), randomAlphabetic(10), + randomAlphabetic(10), true, user.getUserId(), List.of(), @@ -252,6 +253,7 @@ void testInsertStudyWithDatasets() throws Exception { assertEquals(studyInsert.description(), s.getDescription()); assertEquals(studyInsert.dataTypes(), s.getDataTypes()); assertEquals(studyInsert.piName(), s.getPiName()); + assertEquals(studyInsert.piEmail(), s.getPiEmail()); assertEquals(studyInsert.publicVisibility(), s.getPublicVisibility()); assertEquals(studyInsert.userId(), s.getCreateUserId()); assertNotNull(s.getCreateDate()); @@ -281,6 +283,7 @@ void testInsertStudyWithProps() throws Exception { randomAlphabetic(10), List.of(randomAlphabetic(10)), randomAlphabetic(10), + randomAlphabetic(10), true, user.getUserId(), List.of(prop1, prop2), @@ -310,6 +313,7 @@ void testInsertStudyWithProps() throws Exception { assertEquals(studyInsert.description(), s.getDescription()); assertEquals(studyInsert.dataTypes(), s.getDataTypes()); assertEquals(studyInsert.piName(), s.getPiName()); + assertEquals(studyInsert.piEmail(), s.getPiEmail()); assertEquals(studyInsert.publicVisibility(), s.getPublicVisibility()); assertEquals(studyInsert.userId(), s.getCreateUserId()); assertNotNull(s.getCreateDate()); @@ -362,6 +366,7 @@ void testInsertStudyWithAlternativeDataSharingFile() throws Exception { randomAlphabetic(10), List.of(randomAlphabetic(10)), randomAlphabetic(10), + randomAlphabetic(10), true, user.getUserId(), List.of(prop1, prop2), @@ -391,6 +396,7 @@ void testInsertStudyWithAlternativeDataSharingFile() throws Exception { assertEquals(studyInsert.description(), s.getDescription()); assertEquals(studyInsert.dataTypes(), s.getDataTypes()); assertEquals(studyInsert.piName(), s.getPiName()); + assertEquals(studyInsert.piEmail(), s.getPiEmail()); assertEquals(studyInsert.publicVisibility(), s.getPublicVisibility()); assertEquals(studyInsert.userId(), s.getCreateUserId()); assertNotNull(s.getCreateDate()); @@ -544,6 +550,7 @@ void testUpdateStudyDetails() throws Exception { newStudyDescription, newDataTypes, newPIName, + null, !study.getPublicVisibility(), study.getCreateUserId(), List.copyOf(study.getProperties()), @@ -590,6 +597,7 @@ void testUpdateStudyWithPropUpdates() throws Exception { study.getDescription(), study.getDataTypes(), study.getPiName(), + study.getPiEmail(), !study.getPublicVisibility(), study.getCreateUserId(), List.of(newProp, prop1), @@ -636,6 +644,7 @@ void testUpdateStudyWithDatasetUpdates() throws Exception { study.getDescription(), study.getDataTypes(), study.getPiName(), + study.getPiEmail(), !study.getPublicVisibility(), study.getCreateUserId(), List.copyOf(study.getProperties()), @@ -726,6 +735,7 @@ void testUpdateStudyWithFileUpdates() throws Exception { study.getDescription(), study.getDataTypes(), study.getPiName(), + study.getPiEmail(), !study.getPublicVisibility(), study.getCreateUserId(), List.copyOf(study.getProperties()), @@ -823,6 +833,7 @@ void testUpdateStudyWithFileUpdatesOnSecondDataset() throws Exception { study.getDescription(), study.getDataTypes(), study.getPiName(), + study.getPiEmail(), !study.getPublicVisibility(), study.getCreateUserId(), List.copyOf(study.getProperties()), @@ -1154,6 +1165,7 @@ void testPatchStudyAllProperties() throws Exception { randomAlphabetic(10), randomAlphabetic(10), randomAlphabetic(10), + null, List.of("email1", "email2"), randomAlphabetic(10), randomAlphabetic(10), @@ -1215,7 +1227,7 @@ void testPatchStudyNoChanges() throws Exception { Study study = createStudy(null, null, null); User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = - new StudyPatch(null, null, null, null, null, null, null, null, null, null, null); + new StudyPatch(null, null, null, null, null, null, null, null, null, null, null, null); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(study.getName(), patched.getName()); assertEquals(study.getDescription(), patched.getDescription()); @@ -1243,7 +1255,7 @@ void testPatchStudyName() throws Exception { User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = new StudyPatch( - randomAlphabetic(10), null, null, null, null, null, null, null, null, null, null); + randomAlphabetic(10), null, null, null, null, null, null, null, null, null, null, null); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(patch.name(), patched.getName()); assertEquals(study.getDescription(), patched.getDescription()); @@ -1271,7 +1283,7 @@ void testPatchStudyDescription() throws Exception { User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = new StudyPatch( - null, null, randomAlphabetic(10), null, null, null, null, null, null, null, null); + null, null, randomAlphabetic(10), null, null, null, null, null, null, null, null, null); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(study.getName(), patched.getName()); assertEquals(patch.description(), patched.getDescription()); @@ -1299,7 +1311,18 @@ void testPatchStudyDataTypes() throws Exception { User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = new StudyPatch( - null, null, null, List.of("tag1", "tag2"), null, null, null, null, null, null, null); + null, + null, + null, + List.of("tag1", "tag2"), + null, + null, + null, + null, + null, + null, + null, + null); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(study.getName(), patched.getName()); assertEquals(study.getDescription(), patched.getDescription()); @@ -1327,7 +1350,7 @@ void testPatchStudyPIName() throws Exception { User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = new StudyPatch( - null, null, null, null, null, null, randomAlphabetic(10), null, null, null, null); + null, null, null, null, null, null, randomAlphabetic(10), null, null, null, null, null); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(study.getName(), patched.getName()); assertEquals(study.getDescription(), patched.getDescription()); @@ -1354,7 +1377,7 @@ void testPatchStudyPublicVisibility() throws Exception { Study study = createStudy(null, null, null); User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = - new StudyPatch(null, null, null, null, null, null, null, null, null, null, false); + new StudyPatch(null, null, null, null, null, null, null, null, null, null, null, false); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(study.getName(), patched.getName()); assertEquals(study.getDescription(), patched.getDescription()); @@ -1382,7 +1405,18 @@ void testPatchStudyStudyType() throws Exception { User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = new StudyPatch( - null, StudyType.COHORT_STUDY, null, null, null, null, null, null, null, null, null); + null, + StudyType.COHORT_STUDY, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(study.getName(), patched.getName()); assertEquals(study.getDescription(), patched.getDescription()); @@ -1416,7 +1450,7 @@ void testPatchStudyPhenotypeIndication() throws Exception { User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = new StudyPatch( - null, null, null, null, randomAlphabetic(10), null, null, null, null, null, null); + null, null, null, null, randomAlphabetic(10), null, null, null, null, null, null, null); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(study.getName(), patched.getName()); assertEquals(study.getDescription(), patched.getDescription()); @@ -1449,7 +1483,7 @@ void testPatchStudySpecies() throws Exception { User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = new StudyPatch( - null, null, null, null, null, randomAlphabetic(10), null, null, null, null, null); + null, null, null, null, null, randomAlphabetic(10), null, null, null, null, null, null); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(study.getName(), patched.getName()); assertEquals(study.getDescription(), patched.getDescription()); @@ -1490,6 +1524,7 @@ void testPatchStudyDataCustodianEmail() throws Exception { null, null, null, + null, List.of("email1", "email2"), null, null, @@ -1527,7 +1562,7 @@ void testPatchStudyAlternativeDataSharingPlanTargetDeliveryDate() throws Excepti User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = new StudyPatch( - null, null, null, null, null, null, null, null, randomAlphabetic(10), null, null); + null, null, null, null, null, null, null, null, null, randomAlphabetic(10), null, null); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(study.getName(), patched.getName()); assertEquals(study.getDescription(), patched.getDescription()); @@ -1559,7 +1594,7 @@ void testPatchStudyAlternativeDataSharingPlanTargetPublicReleaseDate() throws Ex User user = userDAO.findUserById(study.getCreateUserId()); StudyPatch patch = new StudyPatch( - null, null, null, null, null, null, null, null, null, randomAlphabetic(10), null); + null, null, null, null, null, null, null, null, null, null, randomAlphabetic(10), null); Study patched = serviceDAO.patchStudy(study, user, patch); assertEquals(study.getName(), patched.getName()); assertEquals(study.getDescription(), patched.getDescription()); @@ -1643,6 +1678,7 @@ private Study createStudy( randomAlphabetic(10), List.of(randomAlphabetic(10)), randomAlphabetic(10), + null, true, user.getUserId(), List.of(prop1, prop2),