-
Notifications
You must be signed in to change notification settings - Fork 115
json patch output in tests still string #1630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
c28c6a5
1f4c997
5e1b9ad
efadc17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| package com.foo.rest.examples.bb.jsonpatchdto | ||
|
|
||
| import com.fasterxml.jackson.databind.JsonNode | ||
| import com.fasterxml.jackson.databind.ObjectMapper | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponse | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponses | ||
| import org.evomaster.e2etests.utils.CoveredTargets | ||
| import org.springframework.boot.SpringApplication | ||
| import org.springframework.boot.autoconfigure.SpringBootApplication | ||
| import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration | ||
| import org.springframework.http.HttpStatus | ||
| import org.springframework.http.ResponseEntity | ||
| import org.springframework.web.bind.annotation.PatchMapping | ||
| import org.springframework.web.bind.annotation.PathVariable | ||
| import org.springframework.web.bind.annotation.PostMapping | ||
| import org.springframework.web.bind.annotation.RequestBody | ||
| import org.springframework.web.bind.annotation.RequestMapping | ||
| import org.springframework.web.bind.annotation.RestController | ||
|
|
||
| @SpringBootApplication(exclude = [SecurityAutoConfiguration::class]) | ||
| @RestController | ||
| @RequestMapping("/api/jsonpatchdto/resources") | ||
| open class BBJsonPatchDtoApplication { | ||
|
|
||
| companion object { | ||
| @JvmStatic | ||
| fun main(args: Array<String>) { | ||
| SpringApplication.run(BBJsonPatchDtoApplication::class.java, *args) | ||
| } | ||
| } | ||
|
|
||
| private val createdId = 1L | ||
| private val mapper = ObjectMapper() | ||
|
|
||
| @ApiResponses(value = [ | ||
| ApiResponse(responseCode = "201", description = "Created") | ||
| ]) | ||
| @PostMapping(consumes = ["application/json"], produces = ["application/json"]) | ||
| fun create(@RequestBody dto: BBJsonPatchResourceDto): ResponseEntity<BBJsonPatchCreatedDto> { | ||
| if (dto.name.isNotBlank()) | ||
| CoveredTargets.cover("JSON_PATCH_DTO_CREATED_WITH_NAME") | ||
|
|
||
| CoveredTargets.cover("JSON_PATCH_DTO_CREATED") | ||
| return ResponseEntity.status(HttpStatus.CREATED).body(BBJsonPatchCreatedDto(createdId)) | ||
| } | ||
|
|
||
| @ApiResponses(value = [ | ||
| ApiResponse(responseCode = "400", description = "Invalid patch document"), | ||
| ApiResponse(responseCode = "404", description = "Missing resource") | ||
| ]) | ||
| @PatchMapping("/{id}", consumes = ["application/json-patch+json"], produces = ["text/plain"]) | ||
| fun patch(@PathVariable id: Long, @RequestBody body: String): ResponseEntity<String> { | ||
| if (id != createdId) | ||
| return ResponseEntity.status(HttpStatus.NOT_FOUND).body("missing") | ||
|
|
||
| val patch = parsePatchDocument(body) | ||
| if (patch == null || patch.none { it.path("op").asText() == "replace" }) | ||
| return ResponseEntity.badRequest().body("invalid patch") | ||
|
|
||
| CoveredTargets.cover("JSON_PATCH_DTO_PATCHED") | ||
| return ResponseEntity.ok("patched") | ||
| } | ||
|
|
||
| private fun parsePatchDocument(body: String): JsonNode? = | ||
| try { | ||
| mapper.readTree(body).takeIf { it.isArray } | ||
| } catch (e: Exception) { | ||
| null | ||
| } | ||
| } | ||
|
|
||
| data class BBJsonPatchResourceDto( | ||
| val name: String = "", | ||
| val age: Int = 0 | ||
| ) | ||
|
|
||
| data class BBJsonPatchCreatedDto( | ||
| val id: Long = 0 | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package com.foo.rest.examples.bb.jsonpatchdto | ||
|
|
||
| import com.foo.rest.examples.bb.SpringController | ||
|
|
||
| class BBJsonPatchDtoController : SpringController(BBJsonPatchDtoApplication::class.java) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| package org.evomaster.e2etests.spring.rest.bb.jsonpatchdto | ||
|
|
||
| import com.foo.rest.examples.bb.jsonpatchdto.BBJsonPatchDtoController | ||
| import org.evomaster.core.EMConfig | ||
| import org.evomaster.core.output.OutputFormat | ||
| 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.data.RestPath | ||
| import org.evomaster.core.search.Solution | ||
| import org.evomaster.e2etests.spring.rest.bb.SpringTestBase | ||
| import org.evomaster.e2etests.utils.BlackBoxUtils | ||
| import org.junit.jupiter.api.Assertions.assertFalse | ||
| import org.junit.jupiter.api.Assertions.assertTrue | ||
| import org.junit.jupiter.api.BeforeAll | ||
| import org.junit.jupiter.params.ParameterizedTest | ||
| import org.junit.jupiter.params.provider.EnumSource | ||
| import java.nio.file.Files | ||
| import java.nio.file.Paths | ||
|
|
||
| class BBJsonPatchDtoTest : SpringTestBase() { | ||
|
|
||
| companion object { | ||
| @BeforeAll | ||
| @JvmStatic | ||
| fun init() { | ||
| val config = EMConfig() | ||
| initClass(BBJsonPatchDtoController(), config) | ||
| } | ||
| } | ||
|
|
||
| @ParameterizedTest | ||
| @EnumSource(value = OutputFormat::class, names = ["JAVA_JUNIT_5", "KOTLIN_JUNIT_5"]) | ||
| fun testJsonPatchDoesNotUseDtoWhenDtoPayloadsAreEnabled(outputFormat: OutputFormat) { | ||
| val outputFolderName = "BBJsonPatchDtoEM" | ||
|
|
||
| executeAndEvaluateBBTest( | ||
| outputFormat, | ||
| outputFolderName, | ||
| 1000, | ||
| 3, | ||
| listOf( | ||
| "JSON_PATCH_DTO_CREATED", | ||
| "JSON_PATCH_DTO_CREATED_WITH_NAME", | ||
| "JSON_PATCH_DTO_PATCHED" | ||
| ) | ||
| ) { args: MutableList<String> -> | ||
|
|
||
| setOption(args, "dtoForRequestPayload", "true") | ||
|
|
||
| val solution = initAndRun(args) | ||
|
|
||
| assertTrue(solution.individuals.size >= 1) | ||
| assertHasAtLeastOne(solution, HttpVerb.POST, 201, "/api/jsonpatchdto/resources", null) | ||
| assertHasAtLeastOne(solution, HttpVerb.PATCH, 200, "/api/jsonpatchdto/resources/{id}", "patched") | ||
| assertHasAtLeastOne(solution, HttpVerb.PATCH, 404, "/api/jsonpatchdto/resources/{id}", "missing") | ||
|
|
||
| assertPostThenPatchInSameTest(solution) | ||
| } | ||
|
|
||
| val generatedCode = readGeneratedCode(outputFormat, outputFolderName) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the inherited class
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or into |
||
| assertTrue( | ||
| generatedCode.contains("@JsonProperty(\"name\")") | ||
| && generatedCode.contains("@JsonProperty(\"age\")"), | ||
| "POST request body should be emitted as a DTO with the Resource request fields" | ||
| ) | ||
| assertFalse( | ||
| generatedCode.contains("JsonPatchOperation"), | ||
| "JSON Patch request body should keep using raw string JSON instead of a DTO" | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * Verifies that the solution contains at least one generated test (individual) in which a successful POST | ||
| * that creates the resource (201) is followed, later in the same test, by a successful PATCH (200) on the | ||
| * created resource. This is stronger than [assertHasAtLeastOne], which only checks each call exists somewhere | ||
| * in the solution independently and in any order. | ||
| */ | ||
| private fun assertPostThenPatchInSameTest(solution: Solution<RestIndividual>) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this could be generalized, with 2 input verbs, and moved up to |
||
| val postPath = RestPath("/api/jsonpatchdto/resources") | ||
| val patchPath = RestPath("/api/jsonpatchdto/resources/{id}") | ||
|
|
||
| val found = solution.individuals.any { ind -> | ||
| val actions = ind.individual.seeMainExecutableActions() | ||
| val results = ind.seeResults(actions) | ||
|
|
||
| var createdIndex = -1 | ||
| for (i in actions.indices) { | ||
| val action = actions[i] | ||
| val result = results[i] as RestCallResult | ||
|
|
||
| if (createdIndex < 0) { | ||
| if (action.verb == HttpVerb.POST | ||
| && result.getStatusCode() == 201 | ||
| && action.path.isEquivalent(postPath) | ||
| ) { | ||
| createdIndex = i | ||
| } | ||
| } else if (action.verb == HttpVerb.PATCH | ||
| && result.getStatusCode() == 200 | ||
| && action.path.isEquivalent(patchPath) | ||
| ) { | ||
| return@any true | ||
| } | ||
| } | ||
| false | ||
| } | ||
|
|
||
| assertTrue( | ||
| found, | ||
| "Expected a single generated test where a POST (201) creating the resource is followed by a PATCH (200) on it" | ||
| ) | ||
| } | ||
|
|
||
| private fun readGeneratedCode(outputFormat: OutputFormat, outputFolderName: String): String { | ||
| val baseLocation = if (outputFormat.isJava()) { | ||
| BlackBoxUtils.baseLocationForJava | ||
| } else { | ||
| BlackBoxUtils.baseLocationForKotlin | ||
| } | ||
|
|
||
| val basePath = listOf( | ||
| Paths.get(baseLocation), | ||
| Paths.get("core-tests/e2e-tests/spring/spring-rest-bb", baseLocation) | ||
| ).first { Files.exists(it) } | ||
|
|
||
| return Files.walk(basePath) | ||
| .filter { Files.isRegularFile(it) } | ||
| .filter { it.toString().contains(outputFolderName) } | ||
| .map { Files.readString(it) } | ||
| .toList() | ||
| .joinToString("\n") | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a comment to specify why test is only run for those formats