diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/controller/seqcol/SeqColController.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/controller/seqcol/SeqColController.java index dd0f0e4..44de295 100644 --- a/src/main/java/uk/ac/ebi/eva/evaseqcol/controller/seqcol/SeqColController.java +++ b/src/main/java/uk/ac/ebi/eva/evaseqcol/controller/seqcol/SeqColController.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; @RestController @RequestMapping("/") @@ -98,17 +97,26 @@ public ResponseEntity getSeqColByDigestAndLevel( return new ResponseEntity<>(HttpStatus.NOT_FOUND); } + @Operation(summary = "List sequence collection digests", + description = "Returns a paginated list of sequence collection level 0 digests. " + + "Supports filtering by level 1 attribute digests (e.g., names, sequences, lengths).") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "List retrieved successfully"), + @ApiResponse(responseCode = "400", description = "Invalid pagination parameters") + }) @GetMapping("/list/collection") - public ResponseEntity getList(@RequestParam Map queryParams) { - - int page = Integer.parseInt(queryParams.getOrDefault("page", "0")); - int pageSize = Integer.parseInt(queryParams.getOrDefault("page_size", "10")); + public ResponseEntity> getList( + @Parameter(description = "Page number (0-indexed)", example = "0") + @RequestParam(defaultValue = "0") int page, + @Parameter(description = "Number of results per page", example = "10") + @RequestParam(name = "page_size", defaultValue = "10") int pageSize, + @Parameter(description = "Additional filter parameters (attribute name = level 1 digest)") + @RequestParam Map allParams) { - Map filters = queryParams.entrySet().stream() - .filter(entry -> !entry.getKey().equals("page") && !entry.getKey().equals("page_size")) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + allParams.remove("page"); + allParams.remove("page_size"); - PaginatedResponse results = seqColService.getSeqColList(page, pageSize, filters); + PaginatedResponse results = seqColService.getSeqColList(page, pageSize, allParams); return ResponseEntity.ok(results); } diff --git a/src/main/java/uk/ac/ebi/eva/evaseqcol/dto/PaginatedResponse.java b/src/main/java/uk/ac/ebi/eva/evaseqcol/dto/PaginatedResponse.java index f0511ae..b6b5663 100644 --- a/src/main/java/uk/ac/ebi/eva/evaseqcol/dto/PaginatedResponse.java +++ b/src/main/java/uk/ac/ebi/eva/evaseqcol/dto/PaginatedResponse.java @@ -1,5 +1,6 @@ package uk.ac.ebi.eva.evaseqcol.dto; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Data; import org.springframework.data.domain.Page; @@ -16,6 +17,7 @@ public class PaginatedResponse { @AllArgsConstructor public static class PaginationInfo { private int page; + @JsonProperty("page_size") private int pageSize; private long total; } diff --git a/src/test/java/uk/ac/ebi/eva/evaseqcol/dto/PaginatedResponseTest.java b/src/test/java/uk/ac/ebi/eva/evaseqcol/dto/PaginatedResponseTest.java new file mode 100644 index 0000000..16cf57c --- /dev/null +++ b/src/test/java/uk/ac/ebi/eva/evaseqcol/dto/PaginatedResponseTest.java @@ -0,0 +1,164 @@ +package uk.ac.ebi.eva.evaseqcol.dto; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class PaginatedResponseTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void testConstructorAndGetters() { + List results = Arrays.asList("digest1", "digest2", "digest3"); + PaginatedResponse.PaginationInfo pagination = new PaginatedResponse.PaginationInfo(0, 10, 100); + + PaginatedResponse response = new PaginatedResponse<>(results, pagination); + + assertEquals(results, response.getResults()); + assertEquals(pagination, response.getPagination()); + assertEquals(3, response.getResults().size()); + } + + @Test + void testPaginationInfo() { + PaginatedResponse.PaginationInfo pagination = new PaginatedResponse.PaginationInfo(2, 25, 150); + + assertEquals(2, pagination.getPage()); + assertEquals(25, pagination.getPageSize()); + assertEquals(150, pagination.getTotal()); + } + + @Test + void testFromPageWithPageData() { + List content = Arrays.asList("item1", "item2", "item3"); + Page page = new PageImpl<>(content, PageRequest.of(1, 10), 50); + + PaginatedResponse response = PaginatedResponse.fromPage(page); + + assertEquals(content, response.getResults()); + assertEquals(1, response.getPagination().getPage()); + assertEquals(10, response.getPagination().getPageSize()); + assertEquals(50, response.getPagination().getTotal()); + } + + @Test + void testFromPageWithCustomContent() { + List originalContent = Arrays.asList("a", "b", "c"); + List customContent = Arrays.asList("x", "y"); + Page page = new PageImpl<>(originalContent, PageRequest.of(0, 5), 20); + + PaginatedResponse response = PaginatedResponse.fromPage(customContent, page); + + assertEquals(customContent, response.getResults()); + assertEquals(2, response.getResults().size()); + assertEquals(0, response.getPagination().getPage()); + assertEquals(5, response.getPagination().getPageSize()); + assertEquals(20, response.getPagination().getTotal()); + } + + @Test + void testFromPageWithEmptyPage() { + List emptyContent = Collections.emptyList(); + Page page = new PageImpl<>(emptyContent, PageRequest.of(0, 10), 0); + + PaginatedResponse response = PaginatedResponse.fromPage(page); + + assertTrue(response.getResults().isEmpty()); + assertEquals(0, response.getPagination().getTotal()); + } + + @Test + void testFromPageFirstPage() { + List content = Arrays.asList("first", "second"); + Page page = new PageImpl<>(content, PageRequest.of(0, 2), 10); + + PaginatedResponse response = PaginatedResponse.fromPage(page); + + assertEquals(0, response.getPagination().getPage()); + assertEquals(2, response.getPagination().getPageSize()); + assertEquals(10, response.getPagination().getTotal()); + } + + @Test + void testJsonSerializationPageSizeSnakeCase() throws JsonProcessingException { + List results = Arrays.asList("digest1"); + PaginatedResponse.PaginationInfo pagination = new PaginatedResponse.PaginationInfo(0, 10, 100); + PaginatedResponse response = new PaginatedResponse<>(results, pagination); + + String json = objectMapper.writeValueAsString(response); + + // Verify page_size is serialized with snake_case (not pageSize) + assertTrue(json.contains("\"page_size\"")); + assertFalse(json.contains("\"pageSize\"")); + } + + @Test + void testJsonSerializationStructure() throws JsonProcessingException { + List results = Arrays.asList("abc123", "def456"); + PaginatedResponse.PaginationInfo pagination = new PaginatedResponse.PaginationInfo(1, 20, 50); + PaginatedResponse response = new PaginatedResponse<>(results, pagination); + + String json = objectMapper.writeValueAsString(response); + + // Verify overall structure + assertTrue(json.contains("\"results\"")); + assertTrue(json.contains("\"pagination\"")); + assertTrue(json.contains("\"page\"")); + assertTrue(json.contains("\"page_size\"")); + assertTrue(json.contains("\"total\"")); + assertTrue(json.contains("\"abc123\"")); + assertTrue(json.contains("\"def456\"")); + } + + @Test + void testWithDifferentGenericTypes() { + // Test with Integer type + List intResults = Arrays.asList(1, 2, 3); + PaginatedResponse intResponse = new PaginatedResponse<>( + intResults, new PaginatedResponse.PaginationInfo(0, 10, 3)); + + assertEquals(3, intResponse.getResults().size()); + assertEquals(Integer.valueOf(1), intResponse.getResults().get(0)); + + // Test with custom object type + List objectResults = Arrays.asList("string", 123, true); + PaginatedResponse objectResponse = new PaginatedResponse<>( + objectResults, new PaginatedResponse.PaginationInfo(0, 10, 3)); + + assertEquals(3, objectResponse.getResults().size()); + } + + @Test + void testEqualsAndHashCode() { + List results = Arrays.asList("a", "b"); + PaginatedResponse.PaginationInfo pagination1 = new PaginatedResponse.PaginationInfo(0, 10, 100); + PaginatedResponse.PaginationInfo pagination2 = new PaginatedResponse.PaginationInfo(0, 10, 100); + + PaginatedResponse response1 = new PaginatedResponse<>(results, pagination1); + PaginatedResponse response2 = new PaginatedResponse<>(results, pagination2); + + assertEquals(response1, response2); + assertEquals(response1.hashCode(), response2.hashCode()); + } + + @Test + void testPaginationInfoEqualsAndHashCode() { + PaginatedResponse.PaginationInfo info1 = new PaginatedResponse.PaginationInfo(1, 20, 50); + PaginatedResponse.PaginationInfo info2 = new PaginatedResponse.PaginationInfo(1, 20, 50); + PaginatedResponse.PaginationInfo info3 = new PaginatedResponse.PaginationInfo(2, 20, 50); + + assertEquals(info1, info2); + assertEquals(info1.hashCode(), info2.hashCode()); + assertNotEquals(info1, info3); + } +} diff --git a/src/test/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColComparisonResultEntityTest.java b/src/test/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColComparisonResultEntityTest.java new file mode 100644 index 0000000..873fea8 --- /dev/null +++ b/src/test/java/uk/ac/ebi/eva/evaseqcol/entities/SeqColComparisonResultEntityTest.java @@ -0,0 +1,225 @@ +package uk.ac.ebi.eva.evaseqcol.entities; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class SeqColComparisonResultEntityTest { + + private SeqColComparisonResultEntity entity; + + @BeforeEach + void setUp() { + entity = new SeqColComparisonResultEntity(); + } + + @Test + void testDefaultConstructorInitializesCollections() { + assertNotNull(entity.getDigests()); + assertNotNull(entity.getAttributes()); + assertNotNull(entity.getArray_elements()); + + // Verify array_elements has all required sub-maps + assertTrue(entity.getArray_elements().containsKey("a_count")); + assertTrue(entity.getArray_elements().containsKey("b_count")); + assertTrue(entity.getArray_elements().containsKey("a_and_b_count")); + assertTrue(entity.getArray_elements().containsKey("a_and_b_same_order")); + } + + @Test + void testPutIntoDigests() { + entity.putIntoDigests("a", "digest_a_123"); + entity.putIntoDigests("b", "digest_b_456"); + + assertEquals("digest_a_123", entity.getDigests().get("a")); + assertEquals("digest_b_456", entity.getDigests().get("b")); + assertEquals(2, entity.getDigests().size()); + } + + @Test + void testDigestsAreSorted() { + // Add in reverse order + entity.putIntoDigests("b", "digest_b"); + entity.putIntoDigests("a", "digest_a"); + + // TreeMap should maintain sorted order + String[] keys = entity.getDigests().keySet().toArray(new String[0]); + assertEquals("a", keys[0]); + assertEquals("b", keys[1]); + } + + @Test + void testPutIntoArrays() { + List aOnly = Arrays.asList("attr1", "attr2"); + List bOnly = Arrays.asList("attr3"); + List aAndB = Arrays.asList("sequences", "names", "lengths"); + + entity.putIntoArrays("a_only", aOnly); + entity.putIntoArrays("b_only", bOnly); + entity.putIntoArrays("a_and_b", aAndB); + + assertEquals(aOnly, entity.getAttributes().get("a_only")); + assertEquals(bOnly, entity.getAttributes().get("b_only")); + assertEquals(aAndB, entity.getAttributes().get("a_and_b")); + } + + @Test + void testAttributesAreSorted() { + entity.putIntoArrays("b_only", Arrays.asList("x")); + entity.putIntoArrays("a_only", Arrays.asList("y")); + entity.putIntoArrays("a_and_b", Arrays.asList("z")); + + String[] keys = entity.getAttributes().keySet().toArray(new String[0]); + // TreeMap maintains alphabetical order + assertEquals("a_and_b", keys[0]); + assertEquals("a_only", keys[1]); + assertEquals("b_only", keys[2]); + } + + @Test + void testPutIntoArrayElementsWithIntegerValue() { + entity.putIntoArrayElements("a_count", "sequences", 10); + entity.putIntoArrayElements("a_count", "names", 10); + entity.putIntoArrayElements("b_count", "sequences", 8); + + assertEquals(10, entity.getArray_elements().get("a_count").get("sequences")); + assertEquals(10, entity.getArray_elements().get("a_count").get("names")); + assertEquals(8, entity.getArray_elements().get("b_count").get("sequences")); + } + + @Test + void testPutIntoArrayElementsWithBooleanValue() { + entity.putIntoArrayElements("a_and_b_same_order", "sequences", true); + entity.putIntoArrayElements("a_and_b_same_order", "names", false); + entity.putIntoArrayElements("a_and_b_same_order", "lengths", null); + + assertEquals(true, entity.getArray_elements().get("a_and_b_same_order").get("sequences")); + assertEquals(false, entity.getArray_elements().get("a_and_b_same_order").get("names")); + assertNull(entity.getArray_elements().get("a_and_b_same_order").get("lengths")); + } + + @Test + void testArrayElementsSubMapsAreSorted() { + entity.putIntoArrayElements("a_count", "names", 5); + entity.putIntoArrayElements("a_count", "lengths", 5); + entity.putIntoArrayElements("a_count", "sequences", 5); + + String[] keys = entity.getArray_elements().get("a_count").keySet().toArray(new String[0]); + // TreeMap maintains alphabetical order + assertEquals("lengths", keys[0]); + assertEquals("names", keys[1]); + assertEquals("sequences", keys[2]); + } + + @Test + void testCompleteComparisonScenario() { + // Simulate a complete comparison result + String digestA = "3mTg0tAA3PS-R1TzelLVWJ2ilUzoWfVq"; + String digestB = "rkTW1yZ0e22IN8K-0frqoGOMT8dynNyE"; + + // Set digests + entity.putIntoDigests("a", digestA); + entity.putIntoDigests("b", digestB); + + // Set attributes + entity.putIntoArrays("a_only", Arrays.asList("custom_attr")); + entity.putIntoArrays("b_only", Arrays.asList()); + entity.putIntoArrays("a_and_b", Arrays.asList("sequences", "names", "lengths")); + + // Set array element counts + entity.putIntoArrayElements("a_count", "sequences", 25); + entity.putIntoArrayElements("a_count", "names", 25); + entity.putIntoArrayElements("a_count", "lengths", 25); + + entity.putIntoArrayElements("b_count", "sequences", 24); + entity.putIntoArrayElements("b_count", "names", 24); + entity.putIntoArrayElements("b_count", "lengths", 24); + + entity.putIntoArrayElements("a_and_b_count", "sequences", 24); + entity.putIntoArrayElements("a_and_b_count", "names", 24); + entity.putIntoArrayElements("a_and_b_count", "lengths", 24); + + entity.putIntoArrayElements("a_and_b_same_order", "sequences", true); + entity.putIntoArrayElements("a_and_b_same_order", "names", true); + entity.putIntoArrayElements("a_and_b_same_order", "lengths", true); + + // Verify complete structure + assertEquals(digestA, entity.getDigests().get("a")); + assertEquals(digestB, entity.getDigests().get("b")); + + assertEquals(1, entity.getAttributes().get("a_only").size()); + assertEquals(0, entity.getAttributes().get("b_only").size()); + assertEquals(3, entity.getAttributes().get("a_and_b").size()); + + assertEquals(25, entity.getArray_elements().get("a_count").get("sequences")); + assertEquals(24, entity.getArray_elements().get("b_count").get("sequences")); + assertEquals(24, entity.getArray_elements().get("a_and_b_count").get("sequences")); + assertEquals(true, entity.getArray_elements().get("a_and_b_same_order").get("sequences")); + } + + @Test + void testIdenticalSeqColsComparison() { + // When comparing identical seqCols + String sameDigest = "identicalDigest123"; + + entity.putIntoDigests("a", sameDigest); + entity.putIntoDigests("b", sameDigest); + + entity.putIntoArrays("a_only", Arrays.asList()); + entity.putIntoArrays("b_only", Arrays.asList()); + entity.putIntoArrays("a_and_b", Arrays.asList("sequences", "names", "lengths")); + + entity.putIntoArrayElements("a_count", "sequences", 10); + entity.putIntoArrayElements("b_count", "sequences", 10); + entity.putIntoArrayElements("a_and_b_count", "sequences", 10); + entity.putIntoArrayElements("a_and_b_same_order", "sequences", true); + + // All elements match + assertEquals(entity.getArray_elements().get("a_count").get("sequences"), + entity.getArray_elements().get("b_count").get("sequences")); + assertEquals(entity.getArray_elements().get("a_count").get("sequences"), + entity.getArray_elements().get("a_and_b_count").get("sequences")); + } + + @Test + void testNullSameOrderValue() { + // When less than 2 overlapping elements or unbalanced duplicates + entity.putIntoArrayElements("a_and_b_same_order", "sequences", null); + + assertNull(entity.getArray_elements().get("a_and_b_same_order").get("sequences")); + } + + @Test + void testOverwriteDigestValue() { + entity.putIntoDigests("a", "original_digest"); + entity.putIntoDigests("a", "new_digest"); + + assertEquals("new_digest", entity.getDigests().get("a")); + assertEquals(1, entity.getDigests().size()); + } + + @Test + void testEqualsAndHashCode() { + SeqColComparisonResultEntity entity1 = new SeqColComparisonResultEntity(); + entity1.putIntoDigests("a", "digest1"); + + SeqColComparisonResultEntity entity2 = new SeqColComparisonResultEntity(); + entity2.putIntoDigests("a", "digest1"); + + assertEquals(entity1, entity2); + assertEquals(entity1.hashCode(), entity2.hashCode()); + } + + @Test + void testEmptyArraysInAttributes() { + entity.putIntoArrays("a_only", Arrays.asList()); + entity.putIntoArrays("b_only", Arrays.asList()); + + assertTrue(entity.getAttributes().get("a_only").isEmpty()); + assertTrue(entity.getAttributes().get("b_only").isEmpty()); + } +} diff --git a/src/test/java/uk/ac/ebi/eva/evaseqcol/model/IngestionResultEntityTest.java b/src/test/java/uk/ac/ebi/eva/evaseqcol/model/IngestionResultEntityTest.java new file mode 100644 index 0000000..3135c80 --- /dev/null +++ b/src/test/java/uk/ac/ebi/eva/evaseqcol/model/IngestionResultEntityTest.java @@ -0,0 +1,147 @@ +package uk.ac.ebi.eva.evaseqcol.model; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class IngestionResultEntityTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + private IngestionResultEntity entity; + + @BeforeEach + void setUp() { + entity = new IngestionResultEntity(); + } + + @Test + void testDefaultConstructor() { + assertNull(entity.getAssemblyAccession()); + assertEquals(0, entity.getNumberOfInsertedSeqcols()); + assertNotNull(entity.getInsertedSeqcols()); + assertTrue(entity.getInsertedSeqcols().isEmpty()); + assertNull(entity.getErrorMessage()); + } + + @Test + void testAllArgsConstructor() { + InsertedSeqColEntity inserted = new InsertedSeqColEntity("digest123", "GENBANK"); + IngestionResultEntity fullEntity = new IngestionResultEntity( + "GCA_000001405.28", + 1, + java.util.Collections.singletonList(inserted), + null + ); + + assertEquals("GCA_000001405.28", fullEntity.getAssemblyAccession()); + assertEquals(1, fullEntity.getNumberOfInsertedSeqcols()); + assertEquals(1, fullEntity.getInsertedSeqcols().size()); + assertNull(fullEntity.getErrorMessage()); + } + + @Test + void testSetAssemblyAccession() { + entity.setAssemblyAccession("GCA_000001405.28"); + assertEquals("GCA_000001405.28", entity.getAssemblyAccession()); + } + + @Test + void testAddInsertedSeqCol() { + InsertedSeqColEntity inserted1 = new InsertedSeqColEntity("digest1", "GENBANK"); + InsertedSeqColEntity inserted2 = new InsertedSeqColEntity("digest2", "ENA"); + + entity.addInsertedSeqCol(inserted1); + assertEquals(1, entity.getInsertedSeqcols().size()); + + entity.addInsertedSeqCol(inserted2); + assertEquals(2, entity.getInsertedSeqcols().size()); + assertEquals("digest1", entity.getInsertedSeqcols().get(0).getDigest()); + assertEquals("digest2", entity.getInsertedSeqcols().get(1).getDigest()); + } + + @Test + void testIncrementNumberOfInsertedSeqCols() { + assertEquals(0, entity.getNumberOfInsertedSeqcols()); + + entity.incrementNumberOfInsertedSeqCols(); + assertEquals(1, entity.getNumberOfInsertedSeqcols()); + + entity.incrementNumberOfInsertedSeqCols(); + entity.incrementNumberOfInsertedSeqCols(); + assertEquals(3, entity.getNumberOfInsertedSeqcols()); + } + + @Test + void testSetErrorMessage() { + entity.setErrorMessage("Assembly not found"); + assertEquals("Assembly not found", entity.getErrorMessage()); + } + + @Test + void testJsonSerializationSnakeCaseProperties() throws JsonProcessingException { + entity.setAssemblyAccession("GCA_000001405.28"); + entity.addInsertedSeqCol(new InsertedSeqColEntity("abc123", "GENBANK")); + entity.incrementNumberOfInsertedSeqCols(); + + String json = objectMapper.writeValueAsString(entity); + + // Verify snake_case property names + assertTrue(json.contains("\"assembly_accession\"")); + assertTrue(json.contains("\"num_inserted_seqcols\"")); + assertTrue(json.contains("\"inserted_seqcols\"")); + assertTrue(json.contains("\"error_message\"")); + + // Verify values + assertTrue(json.contains("GCA_000001405.28")); + assertTrue(json.contains("abc123")); + } + + @Test + void testJsonDeserialization() throws JsonProcessingException { + String json = "{\"assembly_accession\":\"GCA_123\",\"num_inserted_seqcols\":2," + + "\"inserted_seqcols\":[{\"digest\":\"d1\",\"naming_convention\":\"ENA\"}]," + + "\"error_message\":null}"; + + IngestionResultEntity deserialized = objectMapper.readValue(json, IngestionResultEntity.class); + + assertEquals("GCA_123", deserialized.getAssemblyAccession()); + assertEquals(2, deserialized.getNumberOfInsertedSeqcols()); + assertEquals(1, deserialized.getInsertedSeqcols().size()); + assertNull(deserialized.getErrorMessage()); + } + + @Test + void testEqualsAndHashCode() { + IngestionResultEntity entity1 = new IngestionResultEntity(); + entity1.setAssemblyAccession("GCA_000001405.28"); + entity1.setNumberOfInsertedSeqcols(1); + + IngestionResultEntity entity2 = new IngestionResultEntity(); + entity2.setAssemblyAccession("GCA_000001405.28"); + entity2.setNumberOfInsertedSeqcols(1); + + assertEquals(entity1, entity2); + assertEquals(entity1.hashCode(), entity2.hashCode()); + } + + @Test + void testTypicalIngestionWorkflow() { + // Simulate a typical ingestion workflow + entity.setAssemblyAccession("GCA_000001405.28"); + + // Add multiple seqcols for different naming conventions + String[] conventions = {"GENBANK", "ENA", "UCSC"}; + for (int i = 0; i < conventions.length; i++) { + InsertedSeqColEntity inserted = new InsertedSeqColEntity("digest" + i, conventions[i]); + entity.addInsertedSeqCol(inserted); + entity.incrementNumberOfInsertedSeqCols(); + } + + assertEquals(3, entity.getNumberOfInsertedSeqcols()); + assertEquals(3, entity.getInsertedSeqcols().size()); + assertNull(entity.getErrorMessage()); + } +} diff --git a/src/test/java/uk/ac/ebi/eva/evaseqcol/model/InsertedSeqColEntityTest.java b/src/test/java/uk/ac/ebi/eva/evaseqcol/model/InsertedSeqColEntityTest.java new file mode 100644 index 0000000..c8babe7 --- /dev/null +++ b/src/test/java/uk/ac/ebi/eva/evaseqcol/model/InsertedSeqColEntityTest.java @@ -0,0 +1,96 @@ +package uk.ac.ebi.eva.evaseqcol.model; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class InsertedSeqColEntityTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void testNoArgsConstructor() { + InsertedSeqColEntity entity = new InsertedSeqColEntity(); + + assertNull(entity.getDigest()); + assertNull(entity.getNamingConvention()); + } + + @Test + void testAllArgsConstructor() { + InsertedSeqColEntity entity = new InsertedSeqColEntity("3mTg0tAA3PS-R1TzelLVWJ2ilUzoWfVq", "GENBANK"); + + assertEquals("3mTg0tAA3PS-R1TzelLVWJ2ilUzoWfVq", entity.getDigest()); + assertEquals("GENBANK", entity.getNamingConvention()); + } + + @Test + void testSettersAndGetters() { + InsertedSeqColEntity entity = new InsertedSeqColEntity(); + + entity.setDigest("abc123def456"); + entity.setNamingConvention("ENA"); + + assertEquals("abc123def456", entity.getDigest()); + assertEquals("ENA", entity.getNamingConvention()); + } + + @Test + void testJsonSerializationSnakeCase() throws JsonProcessingException { + InsertedSeqColEntity entity = new InsertedSeqColEntity("digest123", "UCSC"); + + String json = objectMapper.writeValueAsString(entity); + + // Verify snake_case for naming_convention + assertTrue(json.contains("\"naming_convention\"")); + assertFalse(json.contains("\"namingConvention\"")); + + // Verify values + assertTrue(json.contains("\"digest123\"")); + assertTrue(json.contains("\"UCSC\"")); + } + + @Test + void testJsonDeserialization() throws JsonProcessingException { + String json = "{\"digest\":\"myDigest\",\"naming_convention\":\"GENBANK\"}"; + + InsertedSeqColEntity entity = objectMapper.readValue(json, InsertedSeqColEntity.class); + + assertEquals("myDigest", entity.getDigest()); + assertEquals("GENBANK", entity.getNamingConvention()); + } + + @Test + void testEqualsAndHashCode() { + InsertedSeqColEntity entity1 = new InsertedSeqColEntity("digest1", "ENA"); + InsertedSeqColEntity entity2 = new InsertedSeqColEntity("digest1", "ENA"); + InsertedSeqColEntity entity3 = new InsertedSeqColEntity("digest2", "ENA"); + + assertEquals(entity1, entity2); + assertEquals(entity1.hashCode(), entity2.hashCode()); + assertNotEquals(entity1, entity3); + } + + @Test + void testDifferentNamingConventions() { + String[] conventions = {"ENA", "GENBANK", "UCSC", "TEST"}; + + for (String convention : conventions) { + InsertedSeqColEntity entity = new InsertedSeqColEntity("digest", convention); + assertEquals(convention, entity.getNamingConvention()); + } + } + + @Test + void testToString() { + InsertedSeqColEntity entity = new InsertedSeqColEntity("testDigest", "GENBANK"); + + String toString = entity.toString(); + + // Lombok @Data generates toString + assertTrue(toString.contains("testDigest")); + assertTrue(toString.contains("GENBANK")); + } +} diff --git a/src/test/java/uk/ac/ebi/eva/evaseqcol/model/NameLengthPairEntityTest.java b/src/test/java/uk/ac/ebi/eva/evaseqcol/model/NameLengthPairEntityTest.java new file mode 100644 index 0000000..1462c57 --- /dev/null +++ b/src/test/java/uk/ac/ebi/eva/evaseqcol/model/NameLengthPairEntityTest.java @@ -0,0 +1,132 @@ +package uk.ac.ebi.eva.evaseqcol.model; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class NameLengthPairEntityTest { + + @Test + void testNoArgsConstructor() { + NameLengthPairEntity entity = new NameLengthPairEntity(); + + assertNull(entity.getName()); + assertNull(entity.getLength()); + } + + @Test + void testAllArgsConstructor() { + NameLengthPairEntity entity = new NameLengthPairEntity("chr1", 248956422); + + assertEquals("chr1", entity.getName()); + assertEquals(248956422, entity.getLength()); + } + + @Test + void testSettersAndGetters() { + NameLengthPairEntity entity = new NameLengthPairEntity(); + + entity.setName("chrX"); + entity.setLength(156040895); + + assertEquals("chrX", entity.getName()); + assertEquals(156040895, entity.getLength()); + } + + @Test + void testToStringFormat() { + NameLengthPairEntity entity = new NameLengthPairEntity("chr1", 248956422); + + String result = entity.toString(); + + // Verify the custom toString format + assertEquals("{ \"length\":\"248956422\", \"name\":\"chr1\"}", result); + } + + @Test + void testToStringWithDifferentValues() { + // Test with small values + NameLengthPairEntity smallEntity = new NameLengthPairEntity("chrM", 16569); + assertTrue(smallEntity.toString().contains("\"length\":\"16569\"")); + assertTrue(smallEntity.toString().contains("\"name\":\"chrM\"")); + + // Test with large chromosome length + NameLengthPairEntity largeEntity = new NameLengthPairEntity("chr1", 248956422); + assertTrue(largeEntity.toString().contains("\"length\":\"248956422\"")); + } + + @Test + void testToStringJsonStructure() { + NameLengthPairEntity entity = new NameLengthPairEntity("test", 100); + String result = entity.toString(); + + // Verify it starts and ends correctly + assertTrue(result.startsWith("{")); + assertTrue(result.endsWith("}")); + + // Verify it contains length before name (as per implementation) + int lengthIndex = result.indexOf("\"length\""); + int nameIndex = result.indexOf("\"name\""); + assertTrue(lengthIndex < nameIndex, "length should appear before name in toString"); + } + + @Test + void testEqualsAndHashCode() { + NameLengthPairEntity entity1 = new NameLengthPairEntity("chr1", 1000); + NameLengthPairEntity entity2 = new NameLengthPairEntity("chr1", 1000); + NameLengthPairEntity entity3 = new NameLengthPairEntity("chr2", 1000); + NameLengthPairEntity entity4 = new NameLengthPairEntity("chr1", 2000); + + assertEquals(entity1, entity2); + assertEquals(entity1.hashCode(), entity2.hashCode()); + assertNotEquals(entity1, entity3); + assertNotEquals(entity1, entity4); + } + + @Test + void testWithNullValues() { + NameLengthPairEntity entity = new NameLengthPairEntity(); + entity.setName(null); + entity.setLength(null); + + assertNull(entity.getName()); + assertNull(entity.getLength()); + } + + @Test + void testWithSpecialCharactersInName() { + NameLengthPairEntity entity = new NameLengthPairEntity("chr1_random", 50000); + + assertEquals("chr1_random", entity.getName()); + assertTrue(entity.toString().contains("\"name\":\"chr1_random\"")); + } + + @Test + void testWithZeroLength() { + NameLengthPairEntity entity = new NameLengthPairEntity("empty", 0); + + assertEquals(0, entity.getLength()); + assertTrue(entity.toString().contains("\"length\":\"0\"")); + } + + @Test + void testTypicalChromosomeData() { + // Test with realistic human chromosome data + String[][] chromosomes = { + {"chr1", "248956422"}, + {"chr2", "242193529"}, + {"chrX", "156040895"}, + {"chrY", "57227415"}, + {"chrM", "16569"} + }; + + for (String[] chrData : chromosomes) { + NameLengthPairEntity entity = new NameLengthPairEntity( + chrData[0], + Integer.parseInt(chrData[1]) + ); + assertEquals(chrData[0], entity.getName()); + assertEquals(Integer.parseInt(chrData[1]), entity.getLength()); + } + } +} diff --git a/src/test/java/uk/ac/ebi/eva/evaseqcol/utils/JSONExtDataTest.java b/src/test/java/uk/ac/ebi/eva/evaseqcol/utils/JSONExtDataTest.java index eb76cf5..469366f 100644 --- a/src/test/java/uk/ac/ebi/eva/evaseqcol/utils/JSONExtDataTest.java +++ b/src/test/java/uk/ac/ebi/eva/evaseqcol/utils/JSONExtDataTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; +import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -11,6 +12,7 @@ class JSONExtDataTest { private JSONExtData> JSONStringListExtDataObj; + private JSONExtData> JSONIntegerListExtDataObj; @BeforeEach void setUp() { @@ -18,10 +20,118 @@ void setUp() { List arrElements = Arrays.asList("A", "B", "C"); JSONStringListExtDataObj.setObject(arrElements); + JSONIntegerListExtDataObj = new JSONIntegerListExtData(); + List intElements = Arrays.asList(100, 200, 300); + JSONIntegerListExtDataObj.setObject(intElements); } @Test - void testToString() { + void testStringListToString() { assertEquals("[\"A\",\"B\",\"C\"]", JSONStringListExtDataObj.toString()); } + + @Test + void testIntegerListToString() { + assertEquals("[100, 200, 300]", JSONIntegerListExtDataObj.toString()); + } + + @Test + void testBaseClassNoArgsConstructor() { + JSONExtData data = new JSONExtData<>(); + assertNull(data.getObject()); + } + + @Test + void testBaseClassConstructorWithObject() { + JSONExtData data = new JSONExtData<>("test"); + assertEquals("test", data.getObject()); + } + + @Test + void testSetAndGetObject() { + JSONExtData data = new JSONExtData<>(); + data.setObject(42); + assertEquals(42, data.getObject()); + } + + @Test + void testStringListWithSpecialCharacters() { + JSONStringListExtData data = new JSONStringListExtData( + Arrays.asList("chr1_random", "chrUn_gl000220", "chr1") + ); + String result = data.toString(); + + assertTrue(result.contains("\"chr1_random\"")); + assertTrue(result.contains("\"chrUn_gl000220\"")); + } + + @Test + void testStringListWithSingleElement() { + JSONStringListExtData data = new JSONStringListExtData( + Collections.singletonList("single") + ); + assertEquals("[\"single\"]", data.toString()); + } + + @Test + void testIntegerListWithLargeNumbers() { + JSONIntegerListExtData data = new JSONIntegerListExtData( + Arrays.asList(248956422, 242193529) + ); + String result = data.toString(); + + assertTrue(result.contains("248956422")); + assertTrue(result.contains("242193529")); + } + + @Test + void testSerializable() { + assertTrue(JSONStringListExtDataObj instanceof java.io.Serializable); + assertTrue(JSONIntegerListExtDataObj instanceof java.io.Serializable); + } + + @Test + void testEqualsAndHashCode() { + JSONStringListExtData data1 = new JSONStringListExtData(Arrays.asList("A", "B")); + JSONStringListExtData data2 = new JSONStringListExtData(Arrays.asList("A", "B")); + + assertEquals(data1, data2); + assertEquals(data1.hashCode(), data2.hashCode()); + } + + @Test + void testNotEqualsDifferentContent() { + JSONStringListExtData data1 = new JSONStringListExtData(Arrays.asList("A", "B")); + JSONStringListExtData data2 = new JSONStringListExtData(Arrays.asList("A", "C")); + + // Verify the objects contain different data + assertNotEquals(data1.getObject(), data2.getObject()); + assertNotEquals(data1.toString(), data2.toString()); + } + + @Test + void testStringListEmptyList() { + JSONStringListExtData data = new JSONStringListExtData(Collections.emptyList()); + // Empty list would cause ArrayIndexOutOfBoundsException with current implementation + // This documents the behavior - might need fixing in production + assertNotNull(data.getObject()); + assertTrue(data.getObject().isEmpty()); + } + + @Test + void testIntegerListEmptyList() { + JSONIntegerListExtData data = new JSONIntegerListExtData(Collections.emptyList()); + assertEquals("[]", data.toString()); + } + + @Test + void testPolymorphicBehavior() { + // Both extend JSONExtData + JSONExtData> stringData = new JSONStringListExtData(Arrays.asList("X")); + JSONExtData> intData = new JSONIntegerListExtData(Arrays.asList(1)); + + // Different toString behaviors + assertEquals("[\"X\"]", stringData.toString()); + assertEquals("[1]", intData.toString()); + } } \ No newline at end of file diff --git a/src/test/java/uk/ac/ebi/eva/evaseqcol/utils/JSONIntegerListExtDataTest.java b/src/test/java/uk/ac/ebi/eva/evaseqcol/utils/JSONIntegerListExtDataTest.java new file mode 100644 index 0000000..87ac40d --- /dev/null +++ b/src/test/java/uk/ac/ebi/eva/evaseqcol/utils/JSONIntegerListExtDataTest.java @@ -0,0 +1,152 @@ +package uk.ac.ebi.eva.evaseqcol.utils; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class JSONIntegerListExtDataTest { + + @Test + void testNoArgsConstructor() { + JSONIntegerListExtData data = new JSONIntegerListExtData(); + + assertNull(data.getObject()); + } + + @Test + void testConstructorWithList() { + List lengths = Arrays.asList(248956422, 242193529, 198295559); + JSONIntegerListExtData data = new JSONIntegerListExtData(lengths); + + assertEquals(lengths, data.getObject()); + assertEquals(3, data.getObject().size()); + } + + @Test + void testSetAndGetObject() { + JSONIntegerListExtData data = new JSONIntegerListExtData(); + List lengths = Arrays.asList(100, 200, 300); + + data.setObject(lengths); + + assertEquals(lengths, data.getObject()); + } + + @Test + void testToString() { + List lengths = Arrays.asList(1000, 2000, 3000); + JSONIntegerListExtData data = new JSONIntegerListExtData(lengths); + + String result = data.toString(); + + // Integer list toString uses standard Java format + assertEquals("[1000, 2000, 3000]", result); + } + + @Test + void testToStringWithSingleElement() { + List lengths = Collections.singletonList(16569); + JSONIntegerListExtData data = new JSONIntegerListExtData(lengths); + + assertEquals("[16569]", data.toString()); + } + + @Test + void testToStringWithLargeNumbers() { + // Test with realistic chromosome lengths + List lengths = Arrays.asList(248956422, 242193529); + JSONIntegerListExtData data = new JSONIntegerListExtData(lengths); + + String result = data.toString(); + + assertTrue(result.contains("248956422")); + assertTrue(result.contains("242193529")); + } + + @Test + void testToStringEmptyList() { + List emptyList = Collections.emptyList(); + JSONIntegerListExtData data = new JSONIntegerListExtData(emptyList); + + assertEquals("[]", data.toString()); + } + + @Test + void testEqualsAndHashCode() { + List lengths = Arrays.asList(100, 200, 300); + + JSONIntegerListExtData data1 = new JSONIntegerListExtData(lengths); + JSONIntegerListExtData data2 = new JSONIntegerListExtData(lengths); + + assertEquals(data1, data2); + assertEquals(data1.hashCode(), data2.hashCode()); + } + + @Test + void testNotEqualsDifferentContent() { + JSONIntegerListExtData data1 = new JSONIntegerListExtData(Arrays.asList(100, 200)); + JSONIntegerListExtData data2 = new JSONIntegerListExtData(Arrays.asList(100, 300)); + + // Verify the objects contain different data + assertNotEquals(data1.getObject(), data2.getObject()); + assertNotEquals(data1.toString(), data2.toString()); + } + + @Test + void testInheritanceFromJSONExtData() { + JSONIntegerListExtData data = new JSONIntegerListExtData(Arrays.asList(1, 2, 3)); + + // Verify it's an instance of the parent class + assertTrue(data instanceof JSONExtData); + } + + @Test + void testWithNegativeNumbers() { + // Edge case: negative numbers (though unlikely in real usage) + List lengths = Arrays.asList(-1, 0, 1); + JSONIntegerListExtData data = new JSONIntegerListExtData(lengths); + + assertEquals("[-1, 0, 1]", data.toString()); + } + + @Test + void testWithZeroValues() { + List lengths = Arrays.asList(0, 0, 0); + JSONIntegerListExtData data = new JSONIntegerListExtData(lengths); + + assertEquals("[0, 0, 0]", data.toString()); + } + + @Test + void testWithManyElements() { + // Test with many elements like a real genome would have + List lengths = Arrays.asList( + 248956422, 242193529, 198295559, 190214555, 181538259, + 170805979, 159345973, 145138636, 138394717, 133797422 + ); + JSONIntegerListExtData data = new JSONIntegerListExtData(lengths); + + assertEquals(10, data.getObject().size()); + String result = data.toString(); + assertTrue(result.startsWith("[")); + assertTrue(result.endsWith("]")); + } + + @Test + void testModifyingObjectAfterConstruction() { + List originalLengths = Arrays.asList(100, 200); + JSONIntegerListExtData data = new JSONIntegerListExtData(originalLengths); + + // The list is not defensively copied, so modifications affect the data + // This test documents the current behavior + List newLengths = Arrays.asList(300, 400, 500); + data.setObject(newLengths); + + assertEquals(newLengths, data.getObject()); + assertEquals(3, data.getObject().size()); + } +} diff --git a/src/test/java/uk/ac/ebi/eva/evaseqcol/utils/JSONLevelOneTest.java b/src/test/java/uk/ac/ebi/eva/evaseqcol/utils/JSONLevelOneTest.java new file mode 100644 index 0000000..6b5506a --- /dev/null +++ b/src/test/java/uk/ac/ebi/eva/evaseqcol/utils/JSONLevelOneTest.java @@ -0,0 +1,221 @@ +package uk.ac.ebi.eva.evaseqcol.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class JSONLevelOneTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void testNoArgsConstructor() { + JSONLevelOne levelOne = new JSONLevelOne(); + + assertNull(levelOne.getSequences()); + assertNull(levelOne.getMd5DigestsOfSequences()); + assertNull(levelOne.getNames()); + assertNull(levelOne.getLengths()); + assertNull(levelOne.getSortedNameLengthPairs()); + } + + @Test + void testAllArgsConstructor() { + JSONLevelOne levelOne = new JSONLevelOne( + "seq_digest", + "md5_digest", + "names_digest", + "lengths_digest", + "sorted_pairs_digest" + ); + + assertEquals("seq_digest", levelOne.getSequences()); + assertEquals("md5_digest", levelOne.getMd5DigestsOfSequences()); + assertEquals("names_digest", levelOne.getNames()); + assertEquals("lengths_digest", levelOne.getLengths()); + assertEquals("sorted_pairs_digest", levelOne.getSortedNameLengthPairs()); + } + + @Test + void testFluentSetters() { + JSONLevelOne levelOne = new JSONLevelOne() + .setSequences("sequences_abc") + .setMd5DigestsOfSequences("md5_xyz") + .setNames("names_123") + .setLengths("lengths_456"); + + assertEquals("sequences_abc", levelOne.getSequences()); + assertEquals("md5_xyz", levelOne.getMd5DigestsOfSequences()); + assertEquals("names_123", levelOne.getNames()); + assertEquals("lengths_456", levelOne.getLengths()); + } + + @Test + void testFluentSettersReturnSameInstance() { + JSONLevelOne levelOne = new JSONLevelOne(); + + JSONLevelOne returnedFromSequences = levelOne.setSequences("test"); + JSONLevelOne returnedFromMd5 = returnedFromSequences.setMd5DigestsOfSequences("test2"); + JSONLevelOne returnedFromNames = returnedFromMd5.setNames("test3"); + JSONLevelOne returnedFromLengths = returnedFromNames.setLengths("test4"); + + // All should be the same instance + assertSame(levelOne, returnedFromSequences); + assertSame(levelOne, returnedFromMd5); + assertSame(levelOne, returnedFromNames); + assertSame(levelOne, returnedFromLengths); + } + + @Test + void testJsonSerializationSnakeCase() throws JsonProcessingException { + JSONLevelOne levelOne = new JSONLevelOne( + "seq_digest", + "md5_digest", + "names_digest", + "lengths_digest", + "sorted_pairs_digest" + ); + + String json = objectMapper.writeValueAsString(levelOne); + + // Verify snake_case property names + assertTrue(json.contains("\"md5_sequences\"")); + assertTrue(json.contains("\"sorted_name_length_pairs\"")); + + // Verify camelCase is NOT used + assertFalse(json.contains("\"md5DigestsOfSequences\"")); + assertFalse(json.contains("\"sortedNameLengthPairs\"")); + + // Verify standard property names + assertTrue(json.contains("\"sequences\"")); + assertTrue(json.contains("\"names\"")); + assertTrue(json.contains("\"lengths\"")); + } + + @Test + void testJsonDeserialization() throws JsonProcessingException { + String json = "{" + + "\"sequences\":\"seq_abc\"," + + "\"md5_sequences\":\"md5_def\"," + + "\"names\":\"names_ghi\"," + + "\"lengths\":\"lengths_jkl\"," + + "\"sorted_name_length_pairs\":\"sorted_mno\"" + + "}"; + + JSONLevelOne levelOne = objectMapper.readValue(json, JSONLevelOne.class); + + assertEquals("seq_abc", levelOne.getSequences()); + assertEquals("md5_def", levelOne.getMd5DigestsOfSequences()); + assertEquals("names_ghi", levelOne.getNames()); + assertEquals("lengths_jkl", levelOne.getLengths()); + assertEquals("sorted_mno", levelOne.getSortedNameLengthPairs()); + } + + @Test + void testJsonSerializationRoundTrip() throws JsonProcessingException { + JSONLevelOne original = new JSONLevelOne( + "digest1", + "digest2", + "digest3", + "digest4", + "digest5" + ); + + String json = objectMapper.writeValueAsString(original); + JSONLevelOne deserialized = objectMapper.readValue(json, JSONLevelOne.class); + + assertEquals(original, deserialized); + } + + @Test + void testEqualsAndHashCode() { + JSONLevelOne level1 = new JSONLevelOne("a", "b", "c", "d", "e"); + JSONLevelOne level2 = new JSONLevelOne("a", "b", "c", "d", "e"); + JSONLevelOne level3 = new JSONLevelOne("x", "b", "c", "d", "e"); + + assertEquals(level1, level2); + assertEquals(level1.hashCode(), level2.hashCode()); + assertNotEquals(level1, level3); + } + + @Test + void testSerializable() { + JSONLevelOne levelOne = new JSONLevelOne("a", "b", "c", "d", "e"); + + // Verify it implements Serializable + assertTrue(levelOne instanceof java.io.Serializable); + } + + @Test + void testWithRealDigestValues() { + // Test with realistic SHA-512 digest values (base64url encoded) + String seqDigest = "3mTg0tAA3PS-R1TzelLVWJ2ilUzoWfVq"; + String md5Digest = "rkTW1yZ0e22IN8K-0frqoGOMT8dynNyE"; + String namesDigest = "XYZ123abc-def456_ghi789"; + String lengthsDigest = "ABC789xyz-mno012_pqr345"; + String sortedPairsDigest = "QRS456uvw-xyz789_abc012"; + + JSONLevelOne levelOne = new JSONLevelOne( + seqDigest, md5Digest, namesDigest, lengthsDigest, sortedPairsDigest + ); + + assertEquals(seqDigest, levelOne.getSequences()); + assertEquals(md5Digest, levelOne.getMd5DigestsOfSequences()); + } + + @Test + void testPartiallyPopulated() { + // Only required fields set (sequences, names, lengths) + JSONLevelOne levelOne = new JSONLevelOne() + .setSequences("seq") + .setNames("names") + .setLengths("lengths"); + + assertNotNull(levelOne.getSequences()); + assertNotNull(levelOne.getNames()); + assertNotNull(levelOne.getLengths()); + assertNull(levelOne.getMd5DigestsOfSequences()); + assertNull(levelOne.getSortedNameLengthPairs()); + } + + @Test + void testJsonWithNullValues() throws JsonProcessingException { + JSONLevelOne levelOne = new JSONLevelOne(); + levelOne.setSequences("seq_only"); + + String json = objectMapper.writeValueAsString(levelOne); + + // Null values should be serialized + assertTrue(json.contains("\"sequences\":\"seq_only\"")); + } + + @Test + void testLombokGeneratedSettersOverridden() { + // The class has custom fluent setters that override Lombok + // Verify they work as expected + JSONLevelOne levelOne = new JSONLevelOne(); + + // Custom fluent setters return this + assertSame(levelOne, levelOne.setSequences("test")); + + // Lombok @Data also generates standard setters via reflection + // but our custom ones take precedence + levelOne.setSequences("updated"); + assertEquals("updated", levelOne.getSequences()); + } + + @Test + void testToString() { + JSONLevelOne levelOne = new JSONLevelOne("s", "m", "n", "l", "p"); + + String toString = levelOne.toString(); + + // Lombok @Data generates toString + assertNotNull(toString); + assertTrue(toString.contains("sequences")); + assertTrue(toString.contains("names")); + assertTrue(toString.contains("lengths")); + } +}