From 3f2fbbfe308d1da4f1263a9cd4b7e157c2facf63 Mon Sep 17 00:00:00 2001 From: Keerthi Humsika Kattamudi Date: Tue, 28 Jul 2026 11:29:38 -0400 Subject: [PATCH 1/3] BI-2973: Committing initial changes. --- .../geno/impl/GigwaGenotypeServiceImpl.java | 66 +++++++++++++++- ...gwaGenotypeServiceImplIntegrationTest.java | 79 ++++++++++++++++++- 2 files changed, 142 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImpl.java b/src/main/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImpl.java index 534043dc1..bf77d17a5 100644 --- a/src/main/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImpl.java +++ b/src/main/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImpl.java @@ -65,12 +65,14 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.*; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; import java.util.stream.Collectors; @Singleton @@ -81,6 +83,10 @@ public class GigwaGenotypeServiceImpl implements GenotypeService { private static final String BEARER = "Bearer "; private static final String GIGWA_REST_BASE_PATH = "gigwa/rest"; private static final String GIGWA_BRAPI_BASE_PATH = GIGWA_REST_BASE_PATH + BrapiVersion.BRAPI_V2; + private static final String INVALID_REF_ALT_MESSAGE = "VCF validation failed: the file contains unsupported REF or ALT values. Use '.' for missing data, do not use '-' or 'NA', and ensure ALT values follow the supported VCF allele format."; + private static final String DUPLICATE_POSITIONAL_KEY_MESSAGE = "VCF validation failed: the file contains duplicate chromosome-position values. Each variant must have a unique chromosome-position combination before import."; + private static final Pattern REF_PATTERN = Pattern.compile("[ACGTN.]"); + private static final Pattern ALT_PATTERN = Pattern.compile("[ACGT.]"); private static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json"); @@ -211,7 +217,8 @@ public ImportResponse submitGenotypeData(UUID userId, UUID programId, UUID submi try { byte[] fileContents = uploadedFile.getBytes(); - if(validateSamples(program, submissionId, fileContents, upload)) { + if (validateSamples(program, submissionId, fileContents, upload) + && validateVariantRecords(fileContents, upload)) { executor.execute(() -> { try { processSubmission(gigwaAuthToken, program, submissionId, fileContents, uploadedFile.getFilename(), upload, progress); @@ -391,6 +398,63 @@ private boolean validateSamples(Program program, UUID submissionId, byte[] fileC return true; } + + private boolean validateVariantRecords(byte[] fileContents, ImportUpload upload) { + Set positionalKeys = new HashSet<>(); + boolean foundHeader = false; + + Scanner sc = new Scanner(new ByteArrayInputStream(fileContents), StandardCharsets.UTF_8); + while (sc.hasNextLine()) { + String line = sc.nextLine(); + + if (!foundHeader) { + foundHeader = line.startsWith("#CHROM"); + continue; + } + + if (line.isBlank() || line.startsWith("#")) { + continue; + } + + String[] recordParts = line.split("\t", -1); + if (recordParts.length < 8) { + upload.getProgress().setStatuscode((short) HttpStatus.BAD_REQUEST.getCode()); + upload.getProgress().setMessage("VCF validation failed: variant rows are missing required columns"); + importDAO.updateProgress(upload.getProgress()); + return false; + } + + String chrom = recordParts[0].trim(); + String pos = recordParts[1].trim(); + String ref = recordParts[3].trim(); + String alt = recordParts[4].trim(); + + if (!REF_PATTERN.matcher(ref).matches()) { + upload.getProgress().setStatuscode((short) HttpStatus.BAD_REQUEST.getCode()); + upload.getProgress().setMessage(INVALID_REF_ALT_MESSAGE); + importDAO.updateProgress(upload.getProgress()); + return false; + } + + if (!ALT_PATTERN.matcher(alt).matches()) { + upload.getProgress().setStatuscode((short) HttpStatus.BAD_REQUEST.getCode()); + upload.getProgress().setMessage(INVALID_REF_ALT_MESSAGE); + importDAO.updateProgress(upload.getProgress()); + return false; + } + + String positionalKey = chrom + ":" + pos; + if (!positionalKeys.add(positionalKey)) { + upload.getProgress().setStatuscode((short) HttpStatus.BAD_REQUEST.getCode()); + upload.getProgress().setMessage(DUPLICATE_POSITIONAL_KEY_MESSAGE); + importDAO.updateProgress(upload.getProgress()); + return false; + } + } + + return true; + } + private boolean validateVcfHeader(String[] headerParts) { if(headerParts.length < 8) { return false; diff --git a/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java b/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java index d3c7c7415..ac9eccbab 100644 --- a/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java +++ b/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java @@ -579,6 +579,77 @@ public void testSubmitMissingSubmissionSamples() throws ApiException { assertEquals("There are samples that are not linked to the selected submission", response.getProgress().getMessage()); } + @Test + public void testSubmitDuplicatePositionalKeysShowsSingleMessage() throws Exception { + UUID programId = UUID.fromString("29162e85-e739-4f19-9fd0-0c377ed59956"); + String programKey = "TESTDUPKEY"; + UUID submissionId = UUID.randomUUID(); + + setupMocksForSubmitGenoData(programId, submissionId, buildSamplesFromVcf("sample_duplicate_positional_key.vcf")); + + AtomicReference importResponse = new AtomicReference<>(); + assertTimeout(Duration.of(2, ChronoUnit.MINUTES), () -> importResponse.set(submitGenoData(programId, programKey, submissionId, "sample_duplicate_positional_key.vcf")), "Upload did not complete within the time period"); + + ImportResponse response = importResponse.get(); + assertNotNull(response); + assertNotNull(response.getProgress()); + assertEquals((short) HttpStatus.BAD_REQUEST.getCode(), response.getProgress().getStatuscode()); + assertEquals("VCF validation failed: the file contains duplicate chromosome-position values. Each variant must have a unique chromosome-position combination before import.", response.getProgress().getMessage()); + } + + @Test + public void testSubmitInvalidRefShowsSingleMessage() throws Exception { + UUID programId = UUID.fromString("29162e85-e739-4f19-9fd0-0c377ed59956"); + String programKey = "TESTBADREF"; + UUID submissionId = UUID.randomUUID(); + + setupMocksForSubmitGenoData(programId, submissionId, buildSamplesFromVcf("sample_invalid_ref.vcf")); + + AtomicReference importResponse = new AtomicReference<>(); + assertTimeout(Duration.of(2, ChronoUnit.MINUTES), () -> importResponse.set(submitGenoData(programId, programKey, submissionId, "sample_invalid_ref.vcf")), "Upload did not complete within the time period"); + + ImportResponse response = importResponse.get(); + assertNotNull(response); + assertNotNull(response.getProgress()); + assertEquals((short) HttpStatus.BAD_REQUEST.getCode(), response.getProgress().getStatuscode()); + assertEquals("VCF validation failed: the file contains unsupported REF or ALT values. Use '.' for missing data, do not use '-' or 'NA', and ensure ALT values follow the supported VCF allele format.", response.getProgress().getMessage()); + } + + @Test + public void testSubmitInvalidAltShowsSingleMessage() throws Exception { + UUID programId = UUID.fromString("29162e85-e739-4f19-9fd0-0c377ed59956"); + String programKey = "TESTBADALT"; + UUID submissionId = UUID.randomUUID(); + + setupMocksForSubmitGenoData(programId, submissionId, buildSamplesFromVcf("sample_invalid_alt.vcf")); + + AtomicReference importResponse = new AtomicReference<>(); + assertTimeout(Duration.of(2, ChronoUnit.MINUTES), () -> importResponse.set(submitGenoData(programId, programKey, submissionId, "sample_invalid_alt.vcf")), "Upload did not complete within the time period"); + + ImportResponse response = importResponse.get(); + assertNotNull(response); + assertNotNull(response.getProgress()); + assertEquals((short) HttpStatus.BAD_REQUEST.getCode(), response.getProgress().getStatuscode()); + assertEquals("VCF validation failed: the file contains unsupported REF or ALT values. Use '.' for missing data, do not use '-' or 'NA', and ensure ALT values follow the supported VCF allele format.", response.getProgress().getMessage()); + } + + @Test + public void testSubmitMissingRefAndAltDotAccepted() throws Exception { + UUID programId = UUID.fromString("29162e85-e739-4f19-9fd0-0c377ed59956"); + String programKey = "TESTDOTREFALT"; + UUID submissionId = UUID.randomUUID(); + + setupMocksForSubmitGenoData(programId, submissionId, buildSamplesFromVcf("sample_valid_missing_ref_alt.vcf")); + + AtomicReference importResponse = new AtomicReference<>(); + assertTimeout(Duration.of(2, ChronoUnit.MINUTES), () -> importResponse.set(submitGenoData(programId, programKey, submissionId, "sample_valid_missing_ref_alt.vcf")), "Upload did not complete within the time period"); + + ImportResponse response = importResponse.get(); + assertNotNull(response); + assertNotNull(response.getProgress()); + assertEquals((short) HttpStatus.ACCEPTED.getCode(), response.getProgress().getStatuscode(), "Error importing geno file: " + response.getProgress().getMessage()); + } + private void setupMocksForSubmitGenoData(UUID programId, UUID submissionId, List samples) throws ApiException { SampleSubmission submission = new SampleSubmission(); submission.setId(submissionId); @@ -685,7 +756,11 @@ private ImportResponse submitGenoData(UUID programId, String programKey, UUID su } private List buildSamplesFromValidVcf() throws IOException { - try (Scanner sc = new Scanner(new FileInputStream("src/test/resources/files/geno/sample.vcf"), "UTF-8")) { + return buildSamplesFromVcf("sample.vcf"); + } + + private List buildSamplesFromVcf(String fileName) throws IOException { + try (Scanner sc = new Scanner(new FileInputStream("src/test/resources/files/geno/" + fileName), "UTF-8")) { String[] headerParts = null; boolean foundHeader = false; while (sc.hasNextLine() && !foundHeader) { @@ -696,7 +771,7 @@ private List buildSamplesFromValidVcf() throws IOException { } } - assertTrue(foundHeader, "Could not find sample.vcf header file"); + assertTrue(foundHeader, "Could not find " + fileName + " header file"); List samples = new ArrayList<>(); for (int i = 9; i < headerParts.length; i++) { From e6d4c437a66fd3630985f85a32284d465402e652 Mon Sep 17 00:00:00 2001 From: Keerthi Humsika Kattamudi Date: Mon, 3 Aug 2026 11:23:00 -0400 Subject: [PATCH 2/3] BI-2973: Committing latest changes. --- pom.xml | 5 + .../geno/impl/GigwaGenotypeServiceImpl.java | 122 +++++++++++------- ...gwaGenotypeServiceImplIntegrationTest.java | 57 +++++++- 3 files changed, 129 insertions(+), 55 deletions(-) diff --git a/pom.xml b/pom.xml index 674e7ffea..3afa66cab 100644 --- a/pom.xml +++ b/pom.xml @@ -464,6 +464,11 @@ micronaut-amazon-awssdk-s3 2.0.5-micronaut-2.0 + + com.github.samtools + htsjdk + 2.24.1 + diff --git a/src/main/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImpl.java b/src/main/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImpl.java index bf77d17a5..dab8cb7a2 100644 --- a/src/main/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImpl.java +++ b/src/main/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImpl.java @@ -1,9 +1,30 @@ +/* + * See the NOTICE file distributed with this work for additional information + * regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.breedinginsight.services.geno.impl; import com.agorapulse.micronaut.amazon.awssdk.s3.SimpleStorageService; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; +import htsjdk.samtools.util.CloseableIterator; +import htsjdk.tribble.TribbleException; +import htsjdk.variant.variantcontext.VariantContext; +import htsjdk.variant.vcf.VCFFileReader; import io.micronaut.context.annotation.Property; import io.micronaut.http.HttpStatus; import io.micronaut.http.multipart.CompletedFileUpload; @@ -65,14 +86,14 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.*; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Pattern; import java.util.stream.Collectors; @Singleton @@ -83,10 +104,8 @@ public class GigwaGenotypeServiceImpl implements GenotypeService { private static final String BEARER = "Bearer "; private static final String GIGWA_REST_BASE_PATH = "gigwa/rest"; private static final String GIGWA_BRAPI_BASE_PATH = GIGWA_REST_BASE_PATH + BrapiVersion.BRAPI_V2; - private static final String INVALID_REF_ALT_MESSAGE = "VCF validation failed: the file contains unsupported REF or ALT values. Use '.' for missing data, do not use '-' or 'NA', and ensure ALT values follow the supported VCF allele format."; - private static final String DUPLICATE_POSITIONAL_KEY_MESSAGE = "VCF validation failed: the file contains duplicate chromosome-position values. Each variant must have a unique chromosome-position combination before import."; - private static final Pattern REF_PATTERN = Pattern.compile("[ACGTN.]"); - private static final Pattern ALT_PATTERN = Pattern.compile("[ACGT.]"); + private static final String INVALID_REF_ALT_MESSAGE = "The file is not a valid VCF or contains unsupported REF/ALT allele values."; + private static final String DUPLICATE_POSITIONAL_KEY_MESSAGE = "Duplicate chromosomal position(s) detected. CHROM:POS key must be unique for variant type."; private static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json"); @@ -218,7 +237,7 @@ public ImportResponse submitGenotypeData(UUID userId, UUID programId, UUID submi try { byte[] fileContents = uploadedFile.getBytes(); if (validateSamples(program, submissionId, fileContents, upload) - && validateVariantRecords(fileContents, upload)) { + && validateVariantRecords(fileContents, uploadedFile.getFilename(), upload)) { executor.execute(() -> { try { processSubmission(gigwaAuthToken, program, submissionId, fileContents, uploadedFile.getFilename(), upload, progress); @@ -398,57 +417,64 @@ private boolean validateSamples(Program program, UUID submissionId, byte[] fileC return true; } - - private boolean validateVariantRecords(byte[] fileContents, ImportUpload upload) { + private boolean validateVariantRecords(byte[] fileContents, String filename, ImportUpload upload) { Set positionalKeys = new HashSet<>(); - boolean foundHeader = false; + Path tempVcfFile = null; + int parsedVariantCount = 0; - Scanner sc = new Scanner(new ByteArrayInputStream(fileContents), StandardCharsets.UTF_8); - while (sc.hasNextLine()) { - String line = sc.nextLine(); + try { + tempVcfFile = Files.createTempFile("bi-vcf-validation-", ".vcf"); + Files.write(tempVcfFile, fileContents); - if (!foundHeader) { - foundHeader = line.startsWith("#CHROM"); - continue; - } + try (VCFFileReader reader = new VCFFileReader(tempVcfFile.toFile(), false); + CloseableIterator variants = reader.iterator()) { - if (line.isBlank() || line.startsWith("#")) { - continue; - } + while (variants.hasNext()) { + VariantContext variant = variants.next(); + parsedVariantCount++; - String[] recordParts = line.split("\t", -1); - if (recordParts.length < 8) { - upload.getProgress().setStatuscode((short) HttpStatus.BAD_REQUEST.getCode()); - upload.getProgress().setMessage("VCF validation failed: variant rows are missing required columns"); - importDAO.updateProgress(upload.getProgress()); - return false; - } + String positionalKey = + variant.getType() + ":" + + variant.getContig() + ":" + + variant.getStart(); - String chrom = recordParts[0].trim(); - String pos = recordParts[1].trim(); - String ref = recordParts[3].trim(); - String alt = recordParts[4].trim(); + if (!positionalKeys.add(positionalKey)) { + log.error("Duplicate Gigwa positional key detected during VCF validation for file '{}'. Parsed records: {}. Key: {}", + filename, parsedVariantCount, positionalKey); - if (!REF_PATTERN.matcher(ref).matches()) { - upload.getProgress().setStatuscode((short) HttpStatus.BAD_REQUEST.getCode()); - upload.getProgress().setMessage(INVALID_REF_ALT_MESSAGE); - importDAO.updateProgress(upload.getProgress()); - return false; + upload.getProgress().setStatuscode((short) HttpStatus.BAD_REQUEST.getCode()); + upload.getProgress().setMessage(DUPLICATE_POSITIONAL_KEY_MESSAGE); + importDAO.updateProgress(upload.getProgress()); + return false; + } + } } - if (!ALT_PATTERN.matcher(alt).matches()) { - upload.getProgress().setStatuscode((short) HttpStatus.BAD_REQUEST.getCode()); - upload.getProgress().setMessage(INVALID_REF_ALT_MESSAGE); - importDAO.updateProgress(upload.getProgress()); - return false; - } + log.info("Completed HTSJDK VCF validation for file '{}'. Parsed {} variant record(s) with no validation errors", + filename, parsedVariantCount); + } catch (TribbleException | IllegalArgumentException e) { + log.error("HTSJDK VCF validation failed for file '{}'. Parsed {} variant record(s) before failure. Error: {}", + filename, parsedVariantCount, e.getMessage(), e); - String positionalKey = chrom + ":" + pos; - if (!positionalKeys.add(positionalKey)) { - upload.getProgress().setStatuscode((short) HttpStatus.BAD_REQUEST.getCode()); - upload.getProgress().setMessage(DUPLICATE_POSITIONAL_KEY_MESSAGE); - importDAO.updateProgress(upload.getProgress()); - return false; + upload.getProgress().setStatuscode((short) HttpStatus.BAD_REQUEST.getCode()); + upload.getProgress().setMessage(INVALID_REF_ALT_MESSAGE); + importDAO.updateProgress(upload.getProgress()); + return false; + } catch (IOException e) { + log.error("I/O failure during VCF validation setup for file '{}'. Parsed {} variant record(s) before failure. Error: {}", + filename, parsedVariantCount, e.getMessage(), e); + + upload.getProgress().setStatuscode((short) HttpStatus.INTERNAL_SERVER_ERROR.getCode()); + upload.getProgress().setMessage("An error occurred while trying to validate VCF variant information"); + importDAO.updateProgress(upload.getProgress()); + return false; + } finally { + if (tempVcfFile != null) { + try { + Files.deleteIfExists(tempVcfFile); + } catch (IOException e) { + log.warn("Unable to delete temporary VCF validation file {}", tempVcfFile, e); + } } } diff --git a/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java b/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java index ac9eccbab..e0cf269ac 100644 --- a/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java +++ b/src/test/java/org/breedinginsight/services/geno/impl/GigwaGenotypeServiceImplIntegrationTest.java @@ -594,7 +594,32 @@ public void testSubmitDuplicatePositionalKeysShowsSingleMessage() throws Excepti assertNotNull(response); assertNotNull(response.getProgress()); assertEquals((short) HttpStatus.BAD_REQUEST.getCode(), response.getProgress().getStatuscode()); - assertEquals("VCF validation failed: the file contains duplicate chromosome-position values. Each variant must have a unique chromosome-position combination before import.", response.getProgress().getMessage()); + assertEquals("Duplicate chromosomal position(s) detected. CHROM:POS key must be unique for variant type.", response.getProgress().getMessage()); + } + + @Test + public void testSubmitSameChromPosDifferentVariantTypesAccepted() throws Exception { + UUID programId = UUID.fromString("29162e85-e739-4f19-9fd0-0c377ed59956"); + String programKey = "TESTSAMEPOSDIFFTYPE"; + UUID submissionId = UUID.randomUUID(); + + setupMocksForSubmitGenoData(programId, submissionId, buildSamplesFromVcf("sample_same_pos_different_variant_type.vcf")); + + AtomicReference importResponse = new AtomicReference<>(); + assertTimeout( + Duration.of(2, ChronoUnit.MINUTES), + () -> importResponse.set(submitGenoData(programId, programKey, submissionId, "sample_same_pos_different_variant_type.vcf")), + "Upload did not complete within the time period" + ); + + ImportResponse response = importResponse.get(); + assertNotNull(response); + assertNotNull(response.getProgress()); + assertEquals( + (short) HttpStatus.ACCEPTED.getCode(), + response.getProgress().getStatuscode(), + "Error importing geno file: " + response.getProgress().getMessage() + ); } @Test @@ -612,7 +637,7 @@ public void testSubmitInvalidRefShowsSingleMessage() throws Exception { assertNotNull(response); assertNotNull(response.getProgress()); assertEquals((short) HttpStatus.BAD_REQUEST.getCode(), response.getProgress().getStatuscode()); - assertEquals("VCF validation failed: the file contains unsupported REF or ALT values. Use '.' for missing data, do not use '-' or 'NA', and ensure ALT values follow the supported VCF allele format.", response.getProgress().getMessage()); + assertEquals("The file is not a valid VCF or contains unsupported REF/ALT allele values.", response.getProgress().getMessage()); } @Test @@ -630,19 +655,37 @@ public void testSubmitInvalidAltShowsSingleMessage() throws Exception { assertNotNull(response); assertNotNull(response.getProgress()); assertEquals((short) HttpStatus.BAD_REQUEST.getCode(), response.getProgress().getStatuscode()); - assertEquals("VCF validation failed: the file contains unsupported REF or ALT values. Use '.' for missing data, do not use '-' or 'NA', and ensure ALT values follow the supported VCF allele format.", response.getProgress().getMessage()); + assertEquals("The file is not a valid VCF or contains unsupported REF/ALT allele values.", response.getProgress().getMessage()); + } + + @Test + public void testSubmitRefDotShowsSingleMessage() throws Exception { + UUID programId = UUID.fromString("29162e85-e739-4f19-9fd0-0c377ed59956"); + String programKey = "TESTDOTREF"; + UUID submissionId = UUID.randomUUID(); + + setupMocksForSubmitGenoData(programId, submissionId, buildSamplesFromVcf("sample_invalid_ref_dot.vcf")); + + AtomicReference importResponse = new AtomicReference<>(); + assertTimeout(Duration.of(2, ChronoUnit.MINUTES), () -> importResponse.set(submitGenoData(programId, programKey, submissionId, "sample_invalid_ref_dot.vcf")), "Upload did not complete within the time period"); + + ImportResponse response = importResponse.get(); + assertNotNull(response); + assertNotNull(response.getProgress()); + assertEquals((short) HttpStatus.BAD_REQUEST.getCode(), response.getProgress().getStatuscode()); + assertEquals("The file is not a valid VCF or contains unsupported REF/ALT allele values.", response.getProgress().getMessage()); } @Test - public void testSubmitMissingRefAndAltDotAccepted() throws Exception { + public void testSubmitMultiAllelicAltAccepted() throws Exception { UUID programId = UUID.fromString("29162e85-e739-4f19-9fd0-0c377ed59956"); - String programKey = "TESTDOTREFALT"; + String programKey = "TESTMULTIALT"; UUID submissionId = UUID.randomUUID(); - setupMocksForSubmitGenoData(programId, submissionId, buildSamplesFromVcf("sample_valid_missing_ref_alt.vcf")); + setupMocksForSubmitGenoData(programId, submissionId, buildSamplesFromVcf("sample_valid_multi_alt.vcf")); AtomicReference importResponse = new AtomicReference<>(); - assertTimeout(Duration.of(2, ChronoUnit.MINUTES), () -> importResponse.set(submitGenoData(programId, programKey, submissionId, "sample_valid_missing_ref_alt.vcf")), "Upload did not complete within the time period"); + assertTimeout(Duration.of(2, ChronoUnit.MINUTES), () -> importResponse.set(submitGenoData(programId, programKey, submissionId, "sample_valid_multi_alt.vcf")), "Upload did not complete within the time period"); ImportResponse response = importResponse.get(); assertNotNull(response); From 6cd9b148afe94a6f24f79a4f179d8741ca620041 Mon Sep 17 00:00:00 2001 From: Keerthi Humsika Kattamudi Date: Mon, 3 Aug 2026 11:23:34 -0400 Subject: [PATCH 3/3] BI-2973: Committing latest changes. --- .../resources/files/geno/sample_duplicate_positional_key.vcf | 4 ++++ src/test/resources/files/geno/sample_invalid_alt.vcf | 3 +++ src/test/resources/files/geno/sample_invalid_ref.vcf | 3 +++ src/test/resources/files/geno/sample_invalid_ref_dot.vcf | 3 +++ .../files/geno/sample_same_pos_different_variant_type.vcf | 4 ++++ .../resources/files/geno/sample_valid_missing_ref_alt.vcf | 3 +++ src/test/resources/files/geno/sample_valid_multi_alt.vcf | 3 +++ 7 files changed, 23 insertions(+) create mode 100644 src/test/resources/files/geno/sample_duplicate_positional_key.vcf create mode 100644 src/test/resources/files/geno/sample_invalid_alt.vcf create mode 100644 src/test/resources/files/geno/sample_invalid_ref.vcf create mode 100644 src/test/resources/files/geno/sample_invalid_ref_dot.vcf create mode 100644 src/test/resources/files/geno/sample_same_pos_different_variant_type.vcf create mode 100644 src/test/resources/files/geno/sample_valid_missing_ref_alt.vcf create mode 100644 src/test/resources/files/geno/sample_valid_multi_alt.vcf diff --git a/src/test/resources/files/geno/sample_duplicate_positional_key.vcf b/src/test/resources/files/geno/sample_duplicate_positional_key.vcf new file mode 100644 index 000000000..a538b8624 --- /dev/null +++ b/src/test/resources/files/geno/sample_duplicate_positional_key.vcf @@ -0,0 +1,4 @@ +##fileformat=VCFv4.2 +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT USDAMSP1_A01 +1 100 var1 A T . PASS . GT 0/1 +1 100 var2 G C . PASS . GT 0/1 diff --git a/src/test/resources/files/geno/sample_invalid_alt.vcf b/src/test/resources/files/geno/sample_invalid_alt.vcf new file mode 100644 index 000000000..3563d66c5 --- /dev/null +++ b/src/test/resources/files/geno/sample_invalid_alt.vcf @@ -0,0 +1,3 @@ +##fileformat=VCFv4.2 +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT USDAMSP1_A01 +1 100 var1 A - . PASS . GT 0/1 diff --git a/src/test/resources/files/geno/sample_invalid_ref.vcf b/src/test/resources/files/geno/sample_invalid_ref.vcf new file mode 100644 index 000000000..22e7fb0e1 --- /dev/null +++ b/src/test/resources/files/geno/sample_invalid_ref.vcf @@ -0,0 +1,3 @@ +##fileformat=VCFv4.2 +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT USDAMSP1_A01 +1 100 var1 X T . PASS . GT 0/1 diff --git a/src/test/resources/files/geno/sample_invalid_ref_dot.vcf b/src/test/resources/files/geno/sample_invalid_ref_dot.vcf new file mode 100644 index 000000000..97d4db7dc --- /dev/null +++ b/src/test/resources/files/geno/sample_invalid_ref_dot.vcf @@ -0,0 +1,3 @@ +##fileformat=VCFv4.2 +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT USDAMSP1_A01 +1 100 var1 . T . PASS . GT 0/1 diff --git a/src/test/resources/files/geno/sample_same_pos_different_variant_type.vcf b/src/test/resources/files/geno/sample_same_pos_different_variant_type.vcf new file mode 100644 index 000000000..a3743e676 --- /dev/null +++ b/src/test/resources/files/geno/sample_same_pos_different_variant_type.vcf @@ -0,0 +1,4 @@ +##fileformat=VCFv4.2 +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT USDAMSP1_A01 +1 100 var1 A T . PASS . GT 0/1 +1 100 var2 A AG . PASS . GT 0/1 \ No newline at end of file diff --git a/src/test/resources/files/geno/sample_valid_missing_ref_alt.vcf b/src/test/resources/files/geno/sample_valid_missing_ref_alt.vcf new file mode 100644 index 000000000..df0ebb00f --- /dev/null +++ b/src/test/resources/files/geno/sample_valid_missing_ref_alt.vcf @@ -0,0 +1,3 @@ +##fileformat=VCFv4.2 +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT USDAMSP1_A01 +1 100 var1 . . . PASS . GT 0/0 diff --git a/src/test/resources/files/geno/sample_valid_multi_alt.vcf b/src/test/resources/files/geno/sample_valid_multi_alt.vcf new file mode 100644 index 000000000..2f0b66b04 --- /dev/null +++ b/src/test/resources/files/geno/sample_valid_multi_alt.vcf @@ -0,0 +1,3 @@ +##fileformat=VCFv4.2 +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT USDAMSP1_A01 +1 100 var1 C CAG,T,CGG . PASS . GT 1/2