Skip to content

Commit 4a89841

Browse files
authored
Merge pull request #1390 from WebFuzzing/phg/enumGene
EnumGene supported for DTO usage
2 parents 12da8a2 + a4ce2ff commit 4a89841

11 files changed

Lines changed: 188 additions & 32 deletions

File tree

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/dtoreflectiveassert/DtoReflectiveAssertRest.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,14 @@ class DtoReflectiveAssertRest {
4444
return ResponseEntity.ok("OK")
4545
}
4646

47+
@PostMapping(path = ["/enum-examples"], consumes = [MediaType.APPLICATION_JSON_VALUE])
48+
open fun enumExamples(@RequestBody body: EnumExamplesDto) : ResponseEntity<String>{
49+
return ResponseEntity.ok("OK")
50+
}
51+
52+
@PostMapping(path = ["/enum-type"], consumes = [MediaType.APPLICATION_JSON_VALUE])
53+
open fun enumType(@RequestBody body: EnumTypeDto) : ResponseEntity<String>{
54+
return ResponseEntity.ok("OK")
55+
}
56+
4757
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.foo.rest.examples.spring.openapi.v3.dtoreflectiveassert
2+
3+
class EnumExamplesDto {
4+
5+
var textValue: String = ""
6+
var flagValue: Boolean = false
7+
var countValue: Int = -1
8+
var scoreValue: Float = -3.14f
9+
var listValue: List<String> = emptyList()
10+
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.foo.rest.examples.spring.openapi.v3.dtoreflectiveassert
2+
3+
class EnumTypeDto {
4+
5+
var name: String = ""
6+
var status: String = ""
7+
var intStatus: Int = -10
8+
9+
}

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/resources/static/openapi-dto-reflective-assert.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,30 @@ paths:
166166
responses:
167167
'200':
168168
description: OK
169+
/enum-type:
170+
post:
171+
summary: Create an item using enums
172+
requestBody:
173+
required: true
174+
content:
175+
application/json:
176+
schema:
177+
$ref: '#/components/schemas/EnumType'
178+
responses:
179+
'200':
180+
description: OK
181+
/enum-examples:
182+
post:
183+
summary: Submit an object using examples
184+
requestBody:
185+
required: true
186+
content:
187+
application/json:
188+
schema:
189+
$ref: '#/components/schemas/EnumExample'
190+
responses:
191+
'200':
192+
description: OK
169193

170194
components:
171195
schemas:
@@ -211,3 +235,47 @@ components:
211235
required:
212236
- name
213237
- age
238+
239+
EnumType:
240+
type: object
241+
properties:
242+
name:
243+
type: string
244+
status:
245+
type: string
246+
enum: [ active, inactive, archived ]
247+
intStatus:
248+
type: integer
249+
enum: [ -1, 0, 1 ]
250+
required:
251+
- name
252+
- status
253+
- intStatus
254+
255+
EnumExample:
256+
type: object
257+
properties:
258+
textValue:
259+
type: string
260+
example: "hello world"
261+
flagValue:
262+
type: boolean
263+
example: true
264+
countValue:
265+
type: integer
266+
example: 33
267+
scoreValue:
268+
type: number
269+
format: float
270+
example: 3.14
271+
listValue:
272+
type: array
273+
items:
274+
type: string
275+
example: [ "s1", "s2", "s3" ]
276+
required:
277+
- textValue
278+
- flagValue
279+
- countValue
280+
- scoreValue
281+
- listValue

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/dtoreflectiveassert/DtoReflectiveAssertEMTest.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
4343
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/parent", "OK")
4444
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/items-inline", "OK")
4545
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/items-components", "OK")
46+
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/enum-type", "OK")
47+
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/enum-examples", "OK")
4648
}
4749

4850
assertPrimitiveTypeDtoCreated()
@@ -51,6 +53,8 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
5153
assertItemsInlineDtoCreated()
5254
assertAnyOfDtoCreated()
5355
assertOneOfDtoCreated()
56+
assertEnumTypeDtoCreated()
57+
assertEnumExampleDtoCreated()
5458
}
5559

5660
private fun assertPrimitiveTypeDtoCreated() {
@@ -107,6 +111,22 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
107111
assertProperty(klass, instance, "mouse", "Jerry")
108112
}
109113

114+
private fun assertEnumTypeDtoCreated() {
115+
val (klass, instance) = initDtoClass("EnumType")
116+
assertProperty(klass, instance, "name", "EvoMaster")
117+
assertProperty(klass, instance, "status", "active")
118+
assertProperty(klass, instance, "intStatus", 1)
119+
}
120+
121+
private fun assertEnumExampleDtoCreated() {
122+
val (klass, instance) = initDtoClass("EnumExample")
123+
assertProperty(klass, instance, "textValue", "A text value")
124+
assertProperty(klass, instance, "flagValue", true)
125+
assertProperty(klass, instance, "countValue", 33)
126+
assertProperty(klass, instance, "scoreValue", 3.14f)
127+
assertProperty(klass, instance, "listValue", mutableListOf("s1", "s2", "s3"))
128+
}
129+
110130
private fun initDtoClass(name: String): Pair<KClass<out Any>, Any> {
111131
val className = ClassName("org.foo.dto.$name")
112132
val klass = loadClass(className).kotlin

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/oracledisable/SSRFBaseDisableEMTest.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ class SSRFBaseDisableEMTest : SpringTestBase() {
3939
setOption(args, "schemaOracles", "false")
4040
setOption(args, "disabledOracleCodes", DefinedFaultCategory.SSRF.code.toString())
4141

42-
// TODO: Remove once EnumGene is supported for DTOs
43-
setOption(args, "dtoForRequestPayload","false")
44-
4542
val solution = initAndRun(args)
4643

4744
assertTrue(solution.individuals.isNotEmpty())

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/security/ssrf/base/SSRFBaseEMTest.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ class SSRFBaseEMTest : SpringTestBase() {
3737
setOption(args, "languageModelConnector", "false")
3838
setOption(args, "schemaOracles", "false")
3939

40-
// TODO: Remove once EnumGene is supported for DTOs
41-
setOption(args, "dtoForRequestPayload","false")
42-
4340
val solution = initAndRun(args)
4441

4542
assertTrue(solution.individuals.isNotEmpty())

core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/swagger/SwaggerDescriptionEMTest.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ class SwaggerDescriptionEMTest : SpringTestBase() {
2626
500
2727
) { args: MutableList<String> ->
2828

29-
// TODO: Remove once EnumGene is supported for DTOs
30-
setOption(args, "dtoForRequestPayload","false")
31-
3229
val solution = initAndRun(args)
3330

3431
Assertions.assertTrue(solution.individuals.isNotEmpty())

core/src/main/kotlin/org/evomaster/core/output/dto/DtoWriter.kt

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.evomaster.core.search.gene.BooleanGene
1111
import org.evomaster.core.search.gene.Gene
1212
import org.evomaster.core.search.gene.ObjectGene
1313
import org.evomaster.core.search.gene.collection.ArrayGene
14+
import org.evomaster.core.search.gene.collection.EnumGene
1415
import org.evomaster.core.search.gene.datetime.DateGene
1516
import org.evomaster.core.search.gene.datetime.DateTimeGene
1617
import org.evomaster.core.search.gene.datetime.TimeGene
@@ -93,24 +94,25 @@ class DtoWriter(
9394

9495

9596
private fun calculateDtoFromChoice(gene: ChoiceGene<*>, actionName: String) {
96-
// TODO: should we handle EnumGene?
9797
if (hasObjectOrArrayGene(gene)) {
9898
val dtoName = TestWriterUtils.safeVariableName(actionName)
99-
val dtoClass = DtoClass(dtoName)
100-
val children = gene.getViewOfChildren()
101-
// merge options into a single DTO
102-
children.forEach { childGene ->
103-
when (childGene) {
104-
is ObjectGene -> populateDtoClass(dtoClass, childGene)
105-
is ArrayGene<*> -> {
106-
val template = childGene.template
107-
if (template is ObjectGene) {
108-
populateDtoClass(dtoClass, template)
99+
if (!dtoCollector.contains(dtoName)) {
100+
val dtoClass = DtoClass(dtoName)
101+
val children = gene.getViewOfChildren()
102+
// merge options into a single DTO
103+
children.forEach { childGene ->
104+
when (childGene) {
105+
is ObjectGene -> populateDtoClass(dtoClass, childGene)
106+
is ArrayGene<*> -> {
107+
val template = childGene.template
108+
if (template is ObjectGene) {
109+
populateDtoClass(dtoClass, template)
110+
}
109111
}
110112
}
111113
}
114+
dtoCollector.put(dtoName, dtoClass)
112115
}
113-
dtoCollector.put(dtoName, dtoClass)
114116
}
115117
}
116118

@@ -139,16 +141,17 @@ class DtoWriter(
139141
private fun calculateDtoFromObject(gene: ObjectGene, actionName: String) {
140142
// TODO: Determine strategy for objects that are not defined as a component and do not have a name
141143
val dtoName = TestWriterUtils.safeVariableName(gene.refType?:actionName)
142-
val dtoClass = DtoClass(dtoName)
143-
// TODO: add support for additionalFields
144-
populateDtoClass(dtoClass, gene)
145-
dtoCollector.put(dtoName, dtoClass)
144+
if (!dtoCollector.contains(dtoName)) {
145+
val dtoClass = DtoClass(dtoName)
146+
populateDtoClass(dtoClass, gene)
147+
dtoCollector.put(dtoName, dtoClass)
148+
}
146149
}
147150

148151
private fun calculateDtoFromArray(gene: ArrayGene<*>, actionName: String) {
149152
val template = gene.template
150-
// TODO consider ChoiceGene. Primitive types won't be considered, an array of strings should not be wrapped
151-
// into a DTO but just use List<String> for setting the payload.
153+
// Primitive types won't be considered, an array of strings should not be wrapped
154+
// into a DTO but just use List<String> for setting the payload.
152155
if (template is ObjectGene) {
153156
calculateDtoFromObject(template, actionName)
154157
} else {
@@ -184,7 +187,6 @@ class DtoWriter(
184187

185188
private fun getDtoType(fieldName: String, field: Gene?): String {
186189
return when (field) {
187-
// TODO: handle nested arrays, objects and extend type system for dto fields
188190
is StringGene -> "String"
189191
is IntegerGene -> if (outputFormat.isJava()) "Integer" else "Int"
190192
is LongGene -> "Long"
@@ -199,6 +201,7 @@ class DtoWriter(
199201
is BooleanGene -> "Boolean"
200202
is ObjectGene -> field.refType?:StringUtils.capitalization(fieldName)
201203
is ArrayGene<*> -> if (outputFormat.isJava()) "List<${getDtoType(field.name, field.template)}>" else "MutableList<${getDtoType(field.name, field.template)}>"
204+
is EnumGene<*> -> field.getValueType(outputFormat.isKotlin())
202205
else -> throw Exception("Not supported gene at the moment: ${field?.javaClass?.simpleName} for field $fieldName")
203206
}
204207
}

core/src/main/kotlin/org/evomaster/core/output/dto/GeneToDto.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.evomaster.core.search.gene.BooleanGene
66
import org.evomaster.core.search.gene.Gene
77
import org.evomaster.core.search.gene.ObjectGene
88
import org.evomaster.core.search.gene.collection.ArrayGene
9+
import org.evomaster.core.search.gene.collection.EnumGene
910
import org.evomaster.core.search.gene.datetime.DateGene
1011
import org.evomaster.core.search.gene.datetime.DateTimeGene
1112
import org.evomaster.core.search.gene.datetime.TimeGene
@@ -105,7 +106,13 @@ class GeneToDto(
105106
result.add(dtoOutput.getSetterStatement(dtoVarName, attributeName, childDtoCall.varName))
106107
}
107108
else -> {
108-
result.add(dtoOutput.getSetterStatement(dtoVarName, attributeName, "${leafGene.getValueAsPrintableString(targetFormat = null)}${getValueSuffix(leafGene)}"))
109+
if (leafGene is EnumGene<*> && it is ChoiceGene<*>) {
110+
val children = it.getViewOfChildren()
111+
val otherChoice = children.find { child -> child != leafGene }
112+
result.add(dtoOutput.getSetterStatement(dtoVarName, attributeName, "${leafGene.getValueAsPrintableString(targetFormat = outputFormat)}${getValueSuffix(otherChoice)}"))
113+
} else {
114+
result.add(dtoOutput.getSetterStatement(dtoVarName, attributeName, "${leafGene.getValueAsPrintableString(targetFormat = outputFormat)}${getValueSuffix(leafGene)}"))
115+
}
109116
}
110117
}
111118
}
@@ -135,7 +142,7 @@ class GeneToDto(
135142
} else {
136143
gene.getViewOfElements().forEach {
137144
val leafGene = it.getLeafGene()
138-
result.add(dtoOutput.getAddElementToListStatement(listVarName, "${leafGene.getValueAsPrintableString(targetFormat = null)}${getValueSuffix(leafGene)}"))
145+
result.add(dtoOutput.getAddElementToListStatement(listVarName, "${leafGene.getValueAsPrintableString(targetFormat = outputFormat)}${getValueSuffix(leafGene)}"))
139146
}
140147
}
141148

@@ -164,7 +171,7 @@ class GeneToDto(
164171

165172
// According to documentation, a trailing constant is only needed for Long, Hexadecimal and Float
166173
// https://kotlinlang.org/docs/numbers.html#literal-constants-for-numbers
167-
private fun getValueSuffix(gene: Gene): String {
174+
private fun getValueSuffix(gene: Gene?): String {
168175
return when (gene) {
169176
is LongGene -> "L"
170177
is FloatGene -> "f"

0 commit comments

Comments
 (0)