Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.foo.rest.examples.spring.openapi.v3.dtoreflectiveassert

class AdditionalPropsDto(
val value: String,
val source: String? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.foo.rest.examples.spring.openapi.v3.dtoreflectiveassert

import com.fasterxml.jackson.annotation.JsonAnySetter

class AdditionalPropsInlineDto(val stringProp: String) {

val additional: MutableMap<String, AdditionalPropsDto> = mutableMapOf()

@JsonAnySetter
fun addAdditional(key: String, value: AdditionalPropsDto) {
additional[key] = value
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.foo.rest.examples.spring.openapi.v3.dtoreflectiveassert

import com.fasterxml.jackson.annotation.JsonAnySetter

class AdditionalPropsNoRootDto() {

val additional: MutableMap<String, AdditionalPropsDto> = mutableMapOf()

@JsonAnySetter
fun addAdditional(key: String, value: AdditionalPropsDto) {
additional[key] = value
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.foo.rest.examples.spring.openapi.v3.dtoreflectiveassert

import com.fasterxml.jackson.annotation.JsonAnySetter

class AdditionalPropsRefDto(val stringProp: String) {

val additional: MutableMap<String, ChildSchemaDto> = mutableMapOf()

@JsonAnySetter
fun addAdditional(key: String, value: ChildSchemaDto) {
additional[key] = value
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,19 @@ class DtoReflectiveAssertRest {
return ResponseEntity.ok("OK")
}

@PostMapping(path = ["/additional-properties-inline"], consumes = [MediaType.APPLICATION_JSON_VALUE])
open fun additionalPropertiesInline(@RequestBody body: AdditionalPropsInlineDto) : ResponseEntity<String>{
return ResponseEntity.ok("OK")
}

@PostMapping(path = ["/additional-properties-ref"], consumes = [MediaType.APPLICATION_JSON_VALUE])
open fun additionalPropertiesRef(@RequestBody body: AdditionalPropsRefDto) : ResponseEntity<String>{
return ResponseEntity.ok("OK")
}

@PostMapping(path = ["/additional-properties-no-root"], consumes = [MediaType.APPLICATION_JSON_VALUE])
open fun additionalPropertiesNoRoot(@RequestBody body: AdditionalPropsNoRootDto) : ResponseEntity<String>{
return ResponseEntity.ok("OK")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,73 @@ paths:
responses:
'200':
description: OK
/additional-properties-inline:
post:
summary: Update inline additionalProperties
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
stringProp:
type: string
additionalProperties:
type: object
required:
- value
properties:
value:
type: string
source:
type: string
required:
- stringProp
responses:
'200':
description: OK
/additional-properties-ref:
post:
summary: Update ref additionalProperties
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
stringProp:
type: string
additionalProperties:
$ref: '#/components/schemas/ChildSchema'
required:
- stringProp
responses:
'204':
description: Attributes updated
/additional-properties-no-root:
post:
summary: Update ref additionalProperties
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties:
type: object
required:
- value
- source
properties:
value:
type: string
source:
type: string
responses:
'204':
description: Attributes updated

components:
schemas:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import org.junit.jupiter.api.Test
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaMethod

class DtoReflectiveAssertEMTest: SpringTestBase() {

Expand Down Expand Up @@ -45,6 +47,9 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/items-components", "OK")
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/enum-type", "OK")
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/enum-examples", "OK")
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/additional-properties-inline", "OK")
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/additional-properties-ref", "OK")
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/additional-properties-no-root", "OK")
}

assertPrimitiveTypeDtoCreated()
Expand All @@ -55,6 +60,9 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
assertOneOfDtoCreated()
assertEnumTypeDtoCreated()
assertEnumExampleDtoCreated()
assertAdditionalPropertiesInlineDtoCreated()
assertAdditionalPropertiesRefDtoCreated()
assertAdditionalPropertiesNoRootDtoCreated()
}

private fun assertPrimitiveTypeDtoCreated() {
Expand All @@ -74,9 +82,9 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {

private fun assertParentAndChildDtosCreated() {
val (parentKlass, parentInstance) = initDtoClass("ParentSchema")
val (childKass, childInstance) = initDtoClass("ChildSchema")
assertProperty(childKass, childInstance, "name", "Philip")
assertProperty(childKass, childInstance, "age", 31)
val (childKlass, childInstance) = initDtoClass("ChildSchema")
assertProperty(childKlass, childInstance, "name", "Philip")
assertProperty(childKlass, childInstance, "age", 31)
assertProperty(parentKlass, parentInstance, "label", "EM_TEST")
assertProperty(parentKlass, parentInstance, "child", childInstance)
}
Expand Down Expand Up @@ -127,6 +135,31 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
assertProperty(klass, instance, "listValue", mutableListOf("s1", "s2", "s3"))
}

private fun assertAdditionalPropertiesInlineDtoCreated() {
val (parentKlass, parentInstance) = initDtoClass("POST__additional_properties_inline")
assertProperty(parentKlass, parentInstance, "stringProp", "A text value")
val (childKlass, childInstance) = initDtoClass("POST__additional_properties_inline_ap")
assertProperty(childKlass, childInstance, "value", "My value")
assertAdditionalPropertiesFunction(parentKlass, parentInstance, "aRandomKey", childInstance)
}

private fun assertAdditionalPropertiesRefDtoCreated() {
val (parentKlass, parentInstance) = initDtoClass("POST__additional_properties_ref")
assertProperty(parentKlass, parentInstance, "stringProp", "A text value")
val (childKlass, childInstance) = initDtoClass("ChildSchema")
assertProperty(childKlass, childInstance, "name", "Philip")
assertProperty(childKlass, childInstance, "age", 31)
assertAdditionalPropertiesFunction(parentKlass, parentInstance, "anotherRandomKey", childInstance)
}

private fun assertAdditionalPropertiesNoRootDtoCreated() {
val (parentKlass, parentInstance) = initDtoClass("POST__additional_properties_no_root")
val (childKlass, childInstance) = initDtoClass("POST__additional_properties_no_root_ap")
assertProperty(childKlass, childInstance, "value", "WebFuzzing")
assertProperty(childKlass, childInstance, "source", "Dataset")
assertAdditionalPropertiesFunction(parentKlass, parentInstance, "no_root_key", childInstance)
}

private fun initDtoClass(name: String): Pair<KClass<out Any>, Any> {
val className = ClassName("org.foo.dto.$name")
val klass = loadClass(className).kotlin
Expand All @@ -146,4 +179,22 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
Assertions.assertEquals(propertyValue, property?.get(instance))
}

private fun assertAdditionalPropertiesFunction(klass: KClass<out Any>, instance: Any, propertyName: String, propertyValue: Any?) {
val setterFunction = klass.memberFunctions.firstOrNull {
it.name == "addAdditionalProperty"
}?.javaMethod
Assertions.assertNotNull(setterFunction)
setterFunction?.isAccessible = true
setterFunction?.invoke(instance, propertyName, propertyValue)

val getterFunction = klass.memberFunctions.firstOrNull {
it.name == "getAdditionalProperties"
}?.javaMethod
Assertions.assertNotNull(getterFunction)
getterFunction?.isAccessible = true
val map = getterFunction?.invoke(instance) as MutableMap<*, *>
Assertions.assertNotNull(map)
Assertions.assertEquals(propertyValue, map[propertyName])
}

}
14 changes: 11 additions & 3 deletions core/src/main/kotlin/org/evomaster/core/output/dto/DtoClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@ package org.evomaster.core.output.dto

class DtoClass(
val name: String,
val fields: MutableList<DtoField> = mutableListOf()) {
val fieldsMap: MutableMap<String, DtoField> = mutableMapOf(),
var additionalPropertiesDtoName: String? = null
) {

fun addField(field: DtoField) {
if (field !in fields) fields.add(field)
fun addField(fieldName: String, field: DtoField) {
if (!fieldsMap.containsKey(fieldName)) {
fieldsMap[fieldName] = field
}
}

fun hasAdditionalProperties(): Boolean {
return !additionalPropertiesDtoName.isNullOrEmpty()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,13 @@ interface DtoOutput {
*/
fun getAddElementToListStatement(listVarName: String, value: String): String

/**
* @param additionalPropertiesVarName variable in which the additionalProperties will be added
* @param key for the additional property to be added to the DTO
* @param value variable name representing the additional properties
*
* @return the add additional properties statement
*/
fun getAddElementToAdditionalPropertiesStatement(additionalPropertiesVarName: String, key: String, value: String): String

}
Loading