diff --git a/core/src/main/kotlin/org/evomaster/core/output/dto/DtoWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/dto/DtoWriter.kt index 7a0351a2eb..bce25ebc8a 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/dto/DtoWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/dto/DtoWriter.kt @@ -23,6 +23,7 @@ import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.numeric.LongGene import org.evomaster.core.search.gene.placeholder.CycleObjectGene import org.evomaster.core.search.gene.jsonpatch.JsonPatchDocumentGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchPathValueGene import org.evomaster.core.search.gene.regex.RegexGene import org.evomaster.core.search.gene.string.Base64StringGene import org.evomaster.core.search.gene.string.StringGene @@ -119,11 +120,7 @@ class DtoWriter( gene is ObjectGene -> calculateDtoFromObject(gene, actionName) gene is ArrayGene<*> -> calculateDtoFromArray(gene, actionName) gene is FixedMapGene<*, *> -> calculateDtoFromFixedMapGene(gene, actionName) - // TODO: a JsonPatchDocumentGene is currently skipped from DTO collection. Once we decide - // how a JSON Patch document should be rendered when a test case is written (it is not a - // regular object/array DTO but an RFC 6902 array of operations), this should build and - // emit the corresponding DTO instead of returning. - gene is JsonPatchDocumentGene -> return + gene is JsonPatchDocumentGene -> calculateDtoFromJsonPatch(gene) isPrimitiveGene(gene) -> return else -> { throw IllegalStateException("Gene $gene is not supported for DTO payloads for action: $actionName") @@ -131,6 +128,28 @@ class DtoWriter( } } + // Registers the shared JsonPatchOperation DTO and collects nested DTOs for object/array values. + private fun calculateDtoFromJsonPatch(gene: JsonPatchDocumentGene) { + val dtoName = GeneToDto.JSON_PATCH_OPERATION_DTO + val dtoClass = dtoCollector.computeIfAbsent(dtoName) { DtoClass(it) } + dtoClass.addField(GeneToDto.FIELD_OP, DtoField(GeneToDto.FIELD_OP, "String")) + dtoClass.addField(GeneToDto.FIELD_PATH, DtoField(GeneToDto.FIELD_PATH, "String")) + dtoClass.addField(GeneToDto.FIELD_FROM, DtoField(GeneToDto.FIELD_FROM, "String")) + dtoClass.addField(GeneToDto.FIELD_VALUE, DtoField(GeneToDto.FIELD_VALUE, anyType())) + dtoCollector[dtoName] = dtoClass + + gene.operations.filterIsInstance().forEach { operation -> + when (val valueGene = operation.pathValueChoice.activeGene().second.getLeafGene()) { + is ObjectGene -> calculateDtoFromObject(valueGene, GeneToDto.FIELD_VALUE) + is ArrayGene<*> -> calculateDtoFromArray(valueGene, GeneToDto.FIELD_VALUE) + } + } + } + + private fun anyType(): String { + return if (outputFormat.isJava()) "Object" else "Any" + } + private fun calculateDtoFromFixedMapGene(gene: FixedMapGene<*, *>, actionName: String) { val dtoName = TestWriterUtils.safeVariableName(actionName) val dtoClass = dtoCollector.computeIfAbsent(dtoName) { DtoClass(dtoName) } diff --git a/core/src/main/kotlin/org/evomaster/core/output/dto/GeneToDto.kt b/core/src/main/kotlin/org/evomaster/core/output/dto/GeneToDto.kt index bcd2c2299a..20bb119809 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/dto/GeneToDto.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/dto/GeneToDto.kt @@ -12,6 +12,11 @@ import org.evomaster.core.search.gene.collection.PairGene import org.evomaster.core.search.gene.datetime.DateGene import org.evomaster.core.search.gene.datetime.DateTimeGene import org.evomaster.core.search.gene.datetime.TimeGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchDocumentGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchFromPathGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchOperationGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchPathOnlyGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchPathValueGene import org.evomaster.core.search.gene.numeric.DoubleGene import org.evomaster.core.search.gene.numeric.FloatGene import org.evomaster.core.search.gene.numeric.IntegerGene @@ -36,6 +41,15 @@ class GeneToDto( val outputFormat: OutputFormat ) { + companion object { + // Shared DTO class name for all JSON Patch operations (RFC 6902). + const val JSON_PATCH_OPERATION_DTO = "JsonPatchOperation" + const val FIELD_OP = "op" + const val FIELD_PATH = "path" + const val FIELD_FROM = "from" + const val FIELD_VALUE = "value" + } + private val log: Logger = LoggerFactory.getLogger(GeneToDto::class.java) private var dtoOutput: DtoOutput = if (outputFormat.isJava()) { @@ -67,6 +81,7 @@ class GeneToDto( } is ChoiceGene<*> -> TestWriterUtils.safeVariableName(fallback) is FixedMapGene<*,*> -> TestWriterUtils.safeVariableName(fallback) + is JsonPatchDocumentGene -> JSON_PATCH_OPERATION_DTO else -> throw IllegalStateException("Gene $gene is not supported for DTO payloads for action: $fallback") } } @@ -85,10 +100,84 @@ class GeneToDto( is ArrayGene<*> -> getArrayDtoCall(gene, dtoName, counters, null, capitalize) is ChoiceGene<*> -> getDtoCall(gene.activeGene(), dtoName, counters, capitalize) is FixedMapGene<*,*> -> getFixedMapGeneDtoCall(gene, dtoName, counters) + is JsonPatchDocumentGene -> getJsonPatchDtoCall(gene, counters) else -> throw RuntimeException("BUG: Gene $gene (with type ${this::class.java.simpleName}) should not be creating DTOs") } } + // Renders a JSON Patch document as a List, one DTO per active operation. + private fun getJsonPatchDtoCall(gene: JsonPatchDocumentGene, counters: MutableList): DtoCall { + val listVarName = "list_${JSON_PATCH_OPERATION_DTO}_${counters.joinToString("_")}" + val result = mutableListOf() + result.add(dtoOutput.getNewListStatement(JSON_PATCH_OPERATION_DTO, listVarName)) + + var operationCounter = 1 + gene.operations.forEach { operation -> + val childCounter = mutableListOf().apply { + addAll(counters) + add(operationCounter++) + } + val operationCall = getJsonPatchOperationCall(operation, childCounter) + result.addAll(operationCall.objectCalls) + result.add(dtoOutput.getAddElementToListStatement(listVarName, operationCall.varName)) + } + + return DtoCall(listVarName, result) + } + + // Renders a single RFC 6902 operation as a JsonPatchOperation DTO with only its relevant fields set. + private fun getJsonPatchOperationCall(operation: JsonPatchOperationGene, counters: MutableList): DtoCall { + val varName = "dto_${JSON_PATCH_OPERATION_DTO}_${counters.joinToString("_")}" + val result = mutableListOf() + result.add(dtoOutput.getNewObjectStatement(JSON_PATCH_OPERATION_DTO, varName)) + result.add(dtoOutput.getSetterStatement(varName, FIELD_OP, "\"${operation.operationName}\"")) + + when (operation) { + is JsonPatchPathOnlyGene -> { + result.add(dtoOutput.getSetterStatement(varName, FIELD_PATH, renderLeafValue(operation.pathGene))) + } + is JsonPatchFromPathGene -> { + result.add(dtoOutput.getSetterStatement(varName, FIELD_FROM, renderLeafValue(operation.fromGene))) + result.add(dtoOutput.getSetterStatement(varName, FIELD_PATH, renderLeafValue(operation.pathGene))) + } + is JsonPatchPathValueGene -> { + val pair = operation.pathValueChoice.activeGene() + result.add(dtoOutput.getSetterStatement(varName, FIELD_PATH, renderLeafValue(pair.first))) + setJsonPatchValue(varName, pair.second.getLeafGene(), counters, result) + } + } + + return DtoCall(varName, result) + } + + // Sets the "value" field: primitives are inlined as literals, objects/arrays delegate to DTO generation. + private fun setJsonPatchValue( + varName: String, + valueGene: Gene, + counters: MutableList, + result: MutableList + ) { + when (valueGene) { + is ObjectGene -> { + val childCall = getDtoCall(valueGene, getDtoName(valueGene, FIELD_VALUE, true), counters, true) + result.addAll(childCall.objectCalls) + result.add(dtoOutput.getSetterStatement(varName, FIELD_VALUE, childCall.varName)) + } + is ArrayGene<*> -> { + val childCall = getArrayDtoCall(valueGene, getDtoName(valueGene, FIELD_VALUE, true), counters, FIELD_VALUE, true) + result.addAll(childCall.objectCalls) + result.add(dtoOutput.getSetterStatement(varName, FIELD_VALUE, childCall.varName)) + } + else -> result.add(dtoOutput.getSetterStatement(varName, FIELD_VALUE, renderLeafValue(valueGene))) + } + } + + // Returns the printable string representation of a gene's leaf value with its language-specific suffix. + private fun renderLeafValue(gene: Gene): String { + val leafGene = gene.getLeafGene() + return "${leafGene.getValueAsPrintableString(targetFormat = outputFormat)}${getValueSuffix(leafGene)}" + } + private fun getObjectDtoCall(gene: ObjectGene, dtoName: String, counters: MutableList): DtoCall { val dtoVarName = "dto_${dtoName}_${counters.joinToString("_")}" diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/HttpWsTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/HttpWsTestCaseWriter.kt index 44e268d9b4..e502bf255b 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/HttpWsTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/HttpWsTestCaseWriter.kt @@ -128,11 +128,13 @@ abstract class HttpWsTestCaseWriter : ApiTestCaseWriter() { val bodyParam = call.parameters.find { p -> p is BodyParam } as BodyParam? if (bodyParam != null && bodyParam.isJson() && payloadIsValidJson(bodyParam)) { val primaryGene = bodyParam.primaryGene() - if (primaryGene.getWrappedGene(JsonPatchDocumentGene::class.java) != null) { - return "" + val actionName = call.getName() + val jsonPatchGene = primaryGene.getWrappedGene(JsonPatchDocumentGene::class.java) + if (jsonPatchGene != null) { + // A JSON Patch document is rendered as a List DTO (RFC 6902). + return generateDtoCall(jsonPatchGene, actionName, lines).varName } val choiceGene = primaryGene.getWrappedGene(ChoiceGene::class.java) - val actionName = call.getName() if (choiceGene != null) { // We only generate DTOs for ChoiceGene objects that contain either an ObjectGene or ArrayGene in their // genes. This check is necessary since when using `example` and `default` entries, diff --git a/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt b/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt index d2454da456..81523a8733 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt @@ -27,8 +27,11 @@ import org.evomaster.core.search.gene.UUIDGene import org.evomaster.core.search.gene.collection.EnumGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.string.StringGene +import org.evomaster.core.output.dto.GeneToDto +import org.evomaster.core.search.gene.jsonpatch.JsonPatchDocumentGene import org.evomaster.core.search.gene.utils.GeneUtils import org.evomaster.core.search.gene.wrapper.OptionalGene +import org.evomaster.core.search.service.Randomness import org.evomaster.core.sql.schema.TableId import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test @@ -1641,6 +1644,40 @@ public void test() throws Exception { } } + @Test + fun testJsonPatchBodyRenderedAsDto() { + val format = OutputFormat.KOTLIN_JUNIT_5 + val baseUrlOfSut = "baseUrlOfSut" + + val schema = ObjectGene("body", listOf(StringGene("name"), IntegerGene("age"))) + val typeGene = EnumGene("contentType", listOf("application/json-patch+json")).apply { index = 0 } + val bodyParam = BodyParam(gene = JsonPatchDocumentGene("patch", schema), typeGene = typeGene) + + val action = RestCallAction("1", HttpVerb.PATCH, RestPath("/items/1"), mutableListOf(bodyParam)) + val individual = RestIndividual(mutableListOf(action), SampleType.RANDOM) + TestUtils.doInitializeIndividualForTesting(individual, Randomness().apply { updateSeed(42L) }) + + val fitnessVal = FitnessValue(0.0) + val result = RestCallResult(action.getLocalId()).apply { setStatusCode(200) } + val ei = EvaluatedIndividual(fitnessVal, individual, listOf(result)) + + val config = getConfig(format) + config.dtoForRequestPayload = true + config.problemType = EMConfig.ProblemType.REST + + val test = TestCase(test = ei, name = "test") + val writer = RestTestCaseWriter(config, PartialOracles()) + val output = writer.convertToCompilableTestCode(test, baseUrlOfSut).toString() + + // The JSON Patch body must be rendered as a DTO list, not as a raw JSON string. + assertTrue(output.contains("list_${GeneToDto.JSON_PATCH_OPERATION_DTO}_"), + "Expected DTO list variable in generated output") + assertTrue(output.contains(".body(list_${GeneToDto.JSON_PATCH_OPERATION_DTO}_"), + "Expected DTO variable passed as body argument") + assertFalse(output.contains("{\"op\":"), + "Body must not contain raw JSON string representation of a patch operation") + } + @Test fun testInActiveBodyParamInTest(){ val stringGene = StringGene("stringGene") diff --git a/core/src/test/kotlin/org/evomaster/core/output/dto/DtoWriterJsonPatchTest.kt b/core/src/test/kotlin/org/evomaster/core/output/dto/DtoWriterJsonPatchTest.kt new file mode 100644 index 0000000000..73594607ea --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/output/dto/DtoWriterJsonPatchTest.kt @@ -0,0 +1,107 @@ +package org.evomaster.core.output.dto + +import org.evomaster.core.TestUtils +import org.evomaster.core.output.OutputFormat +import org.evomaster.core.output.Termination +import org.evomaster.core.output.naming.RestActionTestCaseUtils.getEvaluatedIndividualWith +import org.evomaster.core.output.naming.RestActionTestCaseUtils.getRestCallAction +import org.evomaster.core.problem.api.param.Param +import org.evomaster.core.problem.enterprise.SampleType +import org.evomaster.core.problem.rest.data.HttpVerb +import org.evomaster.core.problem.rest.data.RestCallResult +import org.evomaster.core.problem.rest.data.RestIndividual +import org.evomaster.core.problem.rest.param.BodyParam +import org.evomaster.core.search.EvaluatedIndividual +import org.evomaster.core.search.FitnessValue +import org.evomaster.core.search.Solution +import org.evomaster.core.search.gene.ObjectGene +import org.evomaster.core.search.gene.collection.ArrayGene +import org.evomaster.core.search.gene.collection.EnumGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchDocumentGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchPathValueGene +import org.evomaster.core.search.gene.numeric.IntegerGene +import org.evomaster.core.search.gene.string.StringGene +import org.evomaster.core.search.service.Randomness +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import java.nio.file.Paths +import java.util.Collections.singletonList + +class DtoWriterJsonPatchTest { + + private val outputTestSuitePath = Paths.get("./target/dto-writer-json-patch-test") + private val testPackage = "test.package" + + private fun jsonPatchBodyParam(): Param { + val schema = ObjectGene("body", listOf(StringGene("name"), IntegerGene("age"))) + val typeGene = EnumGene("contentType", listOf("application/json-patch+json")).apply { index = 0 } + return BodyParam(gene = JsonPatchDocumentGene("patch", schema), typeGene = typeGene) + } + + private fun jsonPatchSolution(): Solution<*> { + val action = getRestCallAction("/items/{id}", HttpVerb.PATCH, mutableListOf(jsonPatchBodyParam())) + val eIndividual = getEvaluatedIndividualWith(action) + return Solution(singletonList(eIndividual), "", "", Termination.NONE, emptyList(), emptyList()) + } + + @Test + fun collectsJsonPatchOperationDtoWithAllFields() { + val dtoWriter = DtoWriter(OutputFormat.KOTLIN_JUNIT_5) + dtoWriter.write(outputTestSuitePath, testPackage, jsonPatchSolution()) + + val dtos = dtoWriter.getCollectedDtos() + val operationDto = dtos[GeneToDto.JSON_PATCH_OPERATION_DTO] + assertNotNull(operationDto, "Expected a ${GeneToDto.JSON_PATCH_OPERATION_DTO} DTO to be collected") + + val fields = operationDto!!.fieldsMap + assertEquals(DtoField(GeneToDto.FIELD_OP, "String"), fields[GeneToDto.FIELD_OP]) + assertEquals(DtoField(GeneToDto.FIELD_PATH, "String"), fields[GeneToDto.FIELD_PATH]) + assertEquals(DtoField(GeneToDto.FIELD_FROM, "String"), fields[GeneToDto.FIELD_FROM]) + // value is the generic object type since a JSON Patch value can be any JSON value + assertEquals(DtoField(GeneToDto.FIELD_VALUE, "Any"), fields[GeneToDto.FIELD_VALUE]) + } + + @Test + fun valueFieldIsObjectForJavaOutput() { + val dtoWriter = DtoWriter(OutputFormat.JAVA_JUNIT_5) + dtoWriter.write(outputTestSuitePath, testPackage, jsonPatchSolution()) + + val operationDto = dtoWriter.getCollectedDtos()[GeneToDto.JSON_PATCH_OPERATION_DTO] + assertNotNull(operationDto) + assertEquals(DtoField(GeneToDto.FIELD_VALUE, "Object"), operationDto!!.fieldsMap[GeneToDto.FIELD_VALUE]) + } + + @Test + fun collectsNestedObjectDtoWhenValueIsArrayOfObjects() { + // Schema where the only patchable field is an array of objects: + // every add/replace/test operation will have an ArrayGene as its value gene, + // so calculateDtoFromJsonPatch must visit calculateDtoFromArray for those operations. + val tagSchema = ObjectGene("Tag", listOf(StringGene("label")), refType = "Tag") + val schema = ObjectGene("body", listOf(ArrayGene("tags", tagSchema))) + val typeGene = EnumGene("contentType", listOf("application/json-patch+json")).apply { index = 0 } + val bodyParam = BodyParam(gene = JsonPatchDocumentGene("patch", schema), typeGene = typeGene) + + val action = getRestCallAction("/items/{id}", HttpVerb.PATCH, mutableListOf(bodyParam)) + val individual = RestIndividual(mutableListOf(action), SampleType.RANDOM) + TestUtils.doInitializeIndividualForTesting(individual, Randomness().apply { updateSeed(42L) }) + + val result = RestCallResult(action.getLocalId()).apply { setStatusCode(200) } + val ei = EvaluatedIndividual(FitnessValue(0.0), individual, listOf(result)) + val solution = Solution(singletonList(ei), "", "", Termination.NONE, emptyList(), emptyList()) + + val dtoWriter = DtoWriter(OutputFormat.KOTLIN_JUNIT_5) + dtoWriter.write(outputTestSuitePath, testPackage, solution) + val dtos = dtoWriter.getCollectedDtos() + + assertNotNull(dtos[GeneToDto.JSON_PATCH_OPERATION_DTO]) + + val patchGene = bodyParam.primaryGene() as JsonPatchDocumentGene + val pathValueOps = patchGene.operations.filterIsInstance() + assertTrue(pathValueOps.isNotEmpty(), "Seed 42L must produce at least one add/replace/test operation") + // All add/replace/test operations carry an ArrayGene value in this schema; + // the nested Tag DTO must therefore be collected. + assertNotNull(dtos["Tag"], "Nested Tag DTO must be collected for operations with array-of-objects value") + } +} diff --git a/core/src/test/kotlin/org/evomaster/core/output/dto/GeneToDtoJsonPatchTest.kt b/core/src/test/kotlin/org/evomaster/core/output/dto/GeneToDtoJsonPatchTest.kt new file mode 100644 index 0000000000..c09c75612e --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/output/dto/GeneToDtoJsonPatchTest.kt @@ -0,0 +1,88 @@ +package org.evomaster.core.output.dto + +import org.evomaster.core.output.OutputFormat +import org.evomaster.core.search.gene.ObjectGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchDocumentGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchFromPathGene +import org.evomaster.core.search.gene.jsonpatch.JsonPatchPathValueGene +import org.evomaster.core.search.gene.numeric.IntegerGene +import org.evomaster.core.search.gene.string.StringGene +import org.evomaster.core.search.service.Randomness +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +/** + * Verifies that a [JsonPatchDocumentGene] (RFC 6902) is rendered by [GeneToDto] as a + * List instead of stringified JSON. + */ +class GeneToDtoJsonPatchTest { + + private val dtoName = GeneToDto.JSON_PATCH_OPERATION_DTO + + private fun patchDoc(seed: Long = 42L): JsonPatchDocumentGene { + val schema = ObjectGene("body", listOf(StringGene("name"), IntegerGene("age"))) + val doc = JsonPatchDocumentGene("patch", schema) + doc.doInitialize(Randomness().apply { updateSeed(seed) }) + return doc + } + + private fun setOpValue(line: String): String = + Regex("""\.setOp\("(.*?)"\)""").find(line)!!.groupValues[1] + + @Test + fun rendersAsListOfJsonPatchOperationDtosKotlin() { + val doc = patchDoc() + val dtoCall = GeneToDto(OutputFormat.KOTLIN_JUNIT_5).getDtoCall(doc, dtoName, mutableListOf(0), false) + val calls = dtoCall.objectCalls + + assertEquals("list_${dtoName}_0", dtoCall.varName) + assertEquals("val list_${dtoName}_0 = mutableListOf<$dtoName>()", calls.first()) + + val ops = doc.operations + // One object instantiation and one add-to-list per operation + assertEquals(ops.size, calls.count { it.contains("= $dtoName()") }) + assertEquals(ops.size, calls.count { it.startsWith("list_${dtoName}_0.add(dto_${dtoName}_") }) + + // Every operation sets its "op", and the multiset of op names matches the document + val opNamesInCode = calls.filter { it.contains(".setOp(") }.map { setOpValue(it) } + assertEquals(ops.map { it.operationName }.sorted(), opNamesInCode.sorted()) + } + + @Test + fun setsOnlyTheFieldsRelevantToEachOperation() { + val doc = patchDoc() + val calls = GeneToDto(OutputFormat.KOTLIN_JUNIT_5).getDtoCall(doc, dtoName, mutableListOf(0), false).objectCalls + + val ops = doc.operations + // "from" only for move/copy, "value" only for add/replace/test + assertEquals(ops.count { it is JsonPatchFromPathGene }, calls.count { it.contains(".setFrom(") }) + assertEquals(ops.count { it is JsonPatchPathValueGene }, calls.count { it.contains(".setValue(") }) + // "op" and "path" are present in every operation + assertEquals(ops.size, calls.count { it.contains(".setOp(") }) + assertEquals(ops.size, calls.count { it.contains(".setPath(") }) + } + + @Test + fun primitiveValuesAreInlinedAsLiterals() { + // Force every operation type to render; assert string values are quoted and int values are bare + val doc = patchDoc(seed = 7L) + val calls = GeneToDto(OutputFormat.KOTLIN_JUNIT_5).getDtoCall(doc, dtoName, mutableListOf(0), false).objectCalls + + calls.filter { it.contains(".setValue(") }.forEach { line -> + val value = Regex("""\.setValue\((.*)\)""").find(line)!!.groupValues[1] + val isQuotedString = value.startsWith("\"") && value.endsWith("\"") + val isInt = value.toIntOrNull() != null + assertTrue(isQuotedString || isInt, "Unexpected non-literal value rendering: $line") + } + } + + @Test + fun rendersWithJavaSyntax() { + val doc = patchDoc() + val calls = GeneToDto(OutputFormat.JAVA_JUNIT_5).getDtoCall(doc, dtoName, mutableListOf(0), false).objectCalls + + assertEquals("List<$dtoName> list_${dtoName}_0 = new ArrayList<$dtoName>();", calls.first()) + assertTrue(calls.any { it.contains("$dtoName dto_${dtoName}_0_1 = new $dtoName();") }) + } +}