diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/deletefp/HttpOracleDeleteFPApplication.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/deletefp/HttpOracleDeleteFPApplication.kt new file mode 100644 index 0000000000..e76db40bd7 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/deletefp/HttpOracleDeleteFPApplication.kt @@ -0,0 +1,58 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.deletefp + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* + + +@SpringBootApplication(exclude = [SecurityAutoConfiguration::class]) +@RequestMapping(path = ["/api/resources"]) +@RestController +open class HttpOracleDeleteFPApplication { + + companion object { + @JvmStatic + fun main(args: Array) { + SpringApplication.run(HttpOracleDeleteFPApplication::class.java, *args) + } + + private val data = mutableMapOf() + + fun reset(){ + data.clear() + } + } + + + @PutMapping(path = ["/{id}"]) + open fun put( + @PathVariable("id") id: Int + ): ResponseEntity { + + data[id] = "Data for $id" + return ResponseEntity.status(200).build() + } + + @GetMapping(path = ["/{id}"]) + open fun get(@PathVariable("id") id: Int): ResponseEntity { + + if(!data.containsKey(id)){ + return ResponseEntity.status(404).build() + } + + return ResponseEntity.status(200).body(data[id]) + } + + @DeleteMapping(path = ["/{id}"]) + open fun delete(@PathVariable("id") id: Int): ResponseEntity { + + if(!data.containsKey(id)){ + return ResponseEntity.status(404).build() + } + + //not wrong, as not stating we are done + return ResponseEntity.status(202).build() + } +} \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/failmodificationfp/FailModificationFPApplication.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/failmodificationfp/FailModificationFPApplication.kt new file mode 100644 index 0000000000..9ec2226ef1 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/failmodificationfp/FailModificationFPApplication.kt @@ -0,0 +1,35 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.failmodificationfp + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PutMapping +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]) +@RequestMapping(path = ["/api/resources"]) +@RestController +open class FailModificationFPApplication { + + + @PutMapping + open fun put( + @RequestBody body: FailModificationFPDto, + ): ResponseEntity { + + return ResponseEntity.status(400).build() + } + + + @GetMapping + open fun get(): ResponseEntity { + + return ResponseEntity.status(200).body( + FailModificationFPDto(timestamp = System.currentTimeMillis(), message = null) + ) + } +} \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/failmodificationfp/FailModificationFPDto.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/failmodificationfp/FailModificationFPDto.kt new file mode 100644 index 0000000000..6a31816231 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/failmodificationfp/FailModificationFPDto.kt @@ -0,0 +1,6 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.failmodificationfp + +class FailModificationFPDto( + var timestamp: Long? = null, + var message: String? = null, +) \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidallowfp/InvalidAllowFPApplication.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidallowfp/InvalidAllowFPApplication.kt new file mode 100644 index 0000000000..6ee584f5c6 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidallowfp/InvalidAllowFPApplication.kt @@ -0,0 +1,29 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.invalidallowfp + +import io.swagger.v3.oas.annotations.Hidden +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@SpringBootApplication(exclude = [SecurityAutoConfiguration::class]) +@RequestMapping(path = ["/api/invalid-allow-fp"]) +@RestController +open class InvalidAllowFPApplication { + + + @GetMapping + open fun get(): ResponseEntity { + + return ResponseEntity.status(200).body("Hello") + } + + @Hidden + @DeleteMapping + open fun deleteProduct(): ResponseEntity { + return ResponseEntity.status(204).build() + } +} \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidallowfp/RemoveAllowHeaderFilter.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidallowfp/RemoveAllowHeaderFilter.kt new file mode 100644 index 0000000000..e128dd3f04 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidallowfp/RemoveAllowHeaderFilter.kt @@ -0,0 +1,38 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.invalidallowfp + +import javax.servlet.Filter +import javax.servlet.FilterChain +import javax.servlet.FilterConfig +import javax.servlet.ServletRequest +import javax.servlet.ServletResponse +import javax.servlet.http.HttpServletResponse +import javax.servlet.http.HttpServletResponseWrapper +import org.springframework.stereotype.Component + +@Component +class RemoveAllowHeaderFilter : Filter { + + override fun init(filterConfig: FilterConfig?) { + // no-op + } + + override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { + chain.doFilter(request, object : HttpServletResponseWrapper(response as HttpServletResponse) { + override fun setHeader(name: String, value: String) { + if (!name.equals("Allow", ignoreCase = true)) { + super.setHeader(name, value) + } + } + + override fun addHeader(name: String, value: String) { + if (!name.equals("Allow", ignoreCase = true)) { + super.addHeader(name, value) + } + } + }) + } + + override fun destroy() { + // no-op + } +} \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidlocationfp/InvalidLocationFPApplication.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidlocationfp/InvalidLocationFPApplication.kt new file mode 100644 index 0000000000..5a735fe789 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidlocationfp/InvalidLocationFPApplication.kt @@ -0,0 +1,55 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.invalidlocationfp + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@SpringBootApplication(exclude = [SecurityAutoConfiguration::class]) +@RequestMapping(path = ["/api/invalid-location-fp"]) +@RestController +open class InvalidLocationFPApplication { + + companion object { + @JvmStatic + fun main(args: Array) { + SpringApplication.run(InvalidLocationFPApplication::class.java, *args) + } + + private val data = mutableMapOf() + + fun reset(){ + data.clear() + } + } + + + @DeleteMapping(path = ["/{id}"]) + open fun delete(@PathVariable("id") id: Int): ResponseEntity { + if(! data.containsKey(id)){ + return ResponseEntity.status(404).build() + } + data.remove(id) + return ResponseEntity.status(204).build() + } + + + @PutMapping(path = ["/{id}"]) + open fun put( + @PathVariable("id") id: Int + ): ResponseEntity { + + val isNew = !data.containsKey(id) + data[id] = "$id" + + val status = if (isNew) 201 else 200 + return ResponseEntity.status(status) + .header("Location", "/api/invalid-location-fp/$id") + .build() + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidmergepatchfp/InvalidMergePatchFPApplication.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidmergepatchfp/InvalidMergePatchFPApplication.kt new file mode 100644 index 0000000000..2f06cb5d34 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidmergepatchfp/InvalidMergePatchFPApplication.kt @@ -0,0 +1,79 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.invalidmergepatchfp + +import java.net.URI +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +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]) +@RequestMapping(path = ["/api/merge-patch-fp"]) +@RestController +open class InvalidMergePatchFPApplication { + + companion object { + + const val MERGE_PATCH = "application/merge-patch+json" + + @JvmStatic + fun main(args: Array) { + SpringApplication.run(InvalidMergePatchFPApplication::class.java, *args) + } + + private val data = mutableMapOf() + + fun reset() { + data.clear() + } + } + + data class MergePatchResourceFP( + var name: String? = null, + var value: Int? = null + ) + + class MergeRequestFP( + var name: String? = null, + var value: Int? = null + ) + + + @PostMapping + open fun create(@RequestBody body: MergePatchResourceFP): ResponseEntity { + val id = data.size + 1 + val stored = body.copy() + data[id] = stored + return ResponseEntity.created(URI.create("/api/merge-patch-fp/$id")).body(stored) + } + + @GetMapping("/{id}") + open fun get(@PathVariable("id") id: Int): ResponseEntity { + val resource = data[id] ?: return ResponseEntity.status(404).build() + return ResponseEntity.status(200).body(resource) + } + + @PatchMapping("/{id}", consumes = [MERGE_PATCH]) + open fun patch( + @PathVariable("id") id: Int, + @RequestBody body: MergeRequestFP + ): ResponseEntity { + + val resource = data[id] ?: return ResponseEntity.status(404).build() + + // BUG: overwrites every field unconditionally. A body of {"name":"x"} makes + // 'value' arrive as null and wipes the stored value -> PATCH acts like PUT. + resource.name = body.name + resource.value = body.value + + //but, it returns 202, which means it has not completed yet + return ResponseEntity.status(202).body(resource) + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/nonidempotentputfp/NonIdempotentPutFPApplication.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/nonidempotentputfp/NonIdempotentPutFPApplication.kt new file mode 100644 index 0000000000..2cbb2a699f --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/nonidempotentputfp/NonIdempotentPutFPApplication.kt @@ -0,0 +1,73 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.nonidempotentputfp + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import java.net.URI + + +@SpringBootApplication(exclude = [SecurityAutoConfiguration::class]) +@RequestMapping(path = ["/api/accounts"]) +@RestController +open class NonIdempotentPutFPApplication { + + companion object { + @JvmStatic + fun main(args: Array) { + SpringApplication.run(NonIdempotentPutFPApplication::class.java, *args) + } + + private val data = mutableMapOf() + + fun reset(){ + data.clear() + } + } + + data class AccountData( + var balance: Int + ) + + data class DepositRequest( + val amount: Int + ) + + + @PostMapping() + open fun create(@RequestBody body: AccountData): ResponseEntity { + val id = data.size + 1 + data[id] = body.copy() + return ResponseEntity.created(URI("/api/accounts/$id")).body(data[id]) + } + + @GetMapping(path = ["/{id}"]) + open fun get(@PathVariable("id") id: Int): ResponseEntity { + val resource = data[id] + ?: return ResponseEntity.status(404).build() + return ResponseEntity.status(200).body(resource) + } + + @PutMapping(path = ["/{id}/deposit"]) + open fun deposit( + @PathVariable("id") id: Int, + @RequestBody body: DepositRequest + ): ResponseEntity { + + val resource = data[id] + ?: return ResponseEntity.status(404).build() + + // wrong: PUT must be idempotent, but each call accumulates the deposit + resource.balance += body.amount + + //but not a bug, as returning 202, meaning not finished yet + return ResponseEntity.status(202).body(resource) + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/partialupdateputfp/PartialUpdatePutFPApplication.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/partialupdateputfp/PartialUpdatePutFPApplication.kt new file mode 100644 index 0000000000..62e9b26e94 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/partialupdateputfp/PartialUpdatePutFPApplication.kt @@ -0,0 +1,93 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.partialupdateputfp + +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import java.net.URI + +@SpringBootApplication(exclude = [SecurityAutoConfiguration::class]) +@RequestMapping(path = ["/api/resources"]) +@RestController +open class PartialUpdatePutFPApplication { + + companion object { + @JvmStatic + fun main(args: Array) { + SpringApplication.run(PartialUpdatePutFPApplication::class.java, *args) + } + + private val data = mutableMapOf() + + fun reset(){ + data.clear() + } + } + + //the actual stored data has 4 fields + data class ResourceData( + var name: String, + var value: Int, + var timestamp: Long, + var hidden: String + ) + + // GET does not allow to read hidden + data class GetRequest( + var name: String, + var value: Int, + var timestamp: Long + ) + + // creation/update does not allow to set the timestamp + data class CreateUpdateRequest( + val name: String, + val value: Int, + val hidden: String + ) + + + @PostMapping() + open fun create(@RequestBody body: CreateUpdateRequest): ResponseEntity { + val id = data.size + 1 + data[id] = ResourceData(name = body.name, value = body.value, hidden = body.hidden, timestamp = System.currentTimeMillis()) + return ResponseEntity.created(URI("/api/resources/$id")).body(data[id]) + } + + @GetMapping(path = ["/{id}"]) + open fun get(@PathVariable("id") id: Int): ResponseEntity { + val resource = data[id] + ?: return ResponseEntity.status(404).build() + + //here "hidden" cannot be returned + return ResponseEntity.status(200).body( + GetRequest(name = resource.name, value = resource.value, timestamp = System.currentTimeMillis()) + ) + } + + @PutMapping(path = ["/{id}"]) + open fun put( + @PathVariable("id") id: Int, + @RequestBody body: CreateUpdateRequest + ): ResponseEntity { + + val resource = data[id] + ?: return ResponseEntity.status(404).build() + + //here doing a full replacement + resource.value = body.value + resource.name = body.name + resource.hidden = body.hidden + //this is server-side generated + resource.timestamp = System.currentTimeMillis() + + return ResponseEntity.status(200).build() + } +} \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/deletefp/HttpOracleDeleteFPController.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/deletefp/HttpOracleDeleteFPController.kt new file mode 100644 index 0000000000..da402e4c97 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/deletefp/HttpOracleDeleteFPController.kt @@ -0,0 +1,11 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.deletefp + +import com.foo.rest.examples.spring.openapi.v3.SpringController + + +class HttpOracleDeleteFPController: SpringController(HttpOracleDeleteFPApplication::class.java){ + + override fun resetStateOfSUT() { + HttpOracleDeleteFPApplication.reset() + } +} \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/failmodificationfp/HttpOracleFailModificationFPController.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/failmodificationfp/HttpOracleFailModificationFPController.kt new file mode 100644 index 0000000000..65f6b0811b --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/failmodificationfp/HttpOracleFailModificationFPController.kt @@ -0,0 +1,7 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.failmodificationfp + +import com.foo.rest.examples.spring.openapi.v3.SpringController +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.deletefp.HttpOracleDeleteFPApplication + + +class HttpOracleFailModificationFPController: SpringController(FailModificationFPApplication::class.java) \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidallowfp/HttpOracleInvalidAllowFPController.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidallowfp/HttpOracleInvalidAllowFPController.kt new file mode 100644 index 0000000000..cd72a755af --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidallowfp/HttpOracleInvalidAllowFPController.kt @@ -0,0 +1,5 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.invalidallowfp + +import com.foo.rest.examples.spring.openapi.v3.SpringController + +class HttpOracleInvalidAllowFPController: SpringController(InvalidAllowFPApplication::class.java) \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidlocationfp/HttpInvalidLocationFPController.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidlocationfp/HttpInvalidLocationFPController.kt new file mode 100644 index 0000000000..211a3668dd --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidlocationfp/HttpInvalidLocationFPController.kt @@ -0,0 +1,11 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.invalidlocationfp + +import com.foo.rest.examples.spring.openapi.v3.SpringController + + +class HttpInvalidLocationFPController: SpringController(InvalidLocationFPApplication::class.java){ + + override fun resetStateOfSUT() { + InvalidLocationFPApplication.reset() + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidmergepatchfp/HttpInvalidMergePatchFPController.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidmergepatchfp/HttpInvalidMergePatchFPController.kt new file mode 100644 index 0000000000..ed7f2714af --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/invalidmergepatchfp/HttpInvalidMergePatchFPController.kt @@ -0,0 +1,11 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.invalidmergepatchfp + +import com.foo.rest.examples.spring.openapi.v3.SpringController + + +class HttpInvalidMergePatchFPController: SpringController(InvalidMergePatchFPApplication::class.java){ + + override fun resetStateOfSUT() { + InvalidMergePatchFPApplication.reset() + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/nonidempotentputfp/HttpNonIdempotentPutFPController.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/nonidempotentputfp/HttpNonIdempotentPutFPController.kt new file mode 100644 index 0000000000..347f07af57 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/nonidempotentputfp/HttpNonIdempotentPutFPController.kt @@ -0,0 +1,11 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.nonidempotentputfp + +import com.foo.rest.examples.spring.openapi.v3.SpringController + + +class HttpNonIdempotentPutFPController: SpringController(NonIdempotentPutFPApplication::class.java){ + + override fun resetStateOfSUT() { + NonIdempotentPutFPApplication.reset() + } +} \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/partialupdateputfp/HttpPartialUpdatePutFPController.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/partialupdateputfp/HttpPartialUpdatePutFPController.kt new file mode 100644 index 0000000000..e46c157f9a --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/com/foo/rest/examples/spring/openapi/v3/httporaclefp/partialupdateputfp/HttpPartialUpdatePutFPController.kt @@ -0,0 +1,11 @@ +package com.foo.rest.examples.spring.openapi.v3.httporaclefp.partialupdateputfp + +import com.foo.rest.examples.spring.openapi.v3.SpringController + + +class HttpPartialUpdatePutFPController: SpringController(PartialUpdatePutFPApplication::class.java){ + + override fun resetStateOfSUT() { + PartialUpdatePutFPApplication.reset() + } +} \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/deletefp/HttpOracleDeleteFPEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/deletefp/HttpOracleDeleteFPEMTest.kt new file mode 100644 index 0000000000..30f1c65da8 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/deletefp/HttpOracleDeleteFPEMTest.kt @@ -0,0 +1,50 @@ +package org.evomaster.e2etests.spring.openapi.v3.httporaclefp.deletefp + +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.deletefp.HttpOracleDeleteFPController +import com.webfuzzing.commons.faults.DefinedFaultCategory +import org.evomaster.core.problem.enterprise.DetectedFaultUtils +import org.evomaster.core.problem.rest.data.HttpVerb +import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test + +class HttpOracleDeleteFPEMTest : SpringTestBase(){ + + companion object { + @BeforeAll + @JvmStatic + fun init() { + initClass(HttpOracleDeleteFPController()) + } + } + + + @Test + fun testRunEM() { + + runTestHandlingFlakyAndCompilation( + "HttpOracleDeleteFPEM", + 200 + ) { args: MutableList -> + + setOption(args, "httpOracles", "true") + + val solution = initAndRun(args) + + assertTrue(solution.individuals.size >= 1) + + assertHasAtLeastOne(solution, HttpVerb.PUT, 200, "/api/resources/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/api/resources/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.GET, 404, "/api/resources/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.DELETE, 404, "/api/resources/{id}", null) + + //202 does not mean it is completed... so should not say found fault + assertHasAtLeastOne(solution, HttpVerb.DELETE, 202, "/api/resources/{id}", null) + + val faults = DetectedFaultUtils.getDetectedFaultCategories(solution) + assertFalse( DefinedFaultCategory.HTTP_NONWORKING_DELETE in faults ) + } + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/failmodification/HttpOracleFailModificationFPEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/failmodification/HttpOracleFailModificationFPEMTest.kt new file mode 100644 index 0000000000..ba29b2125e --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/failmodification/HttpOracleFailModificationFPEMTest.kt @@ -0,0 +1,46 @@ +package org.evomaster.e2etests.spring.openapi.v3.httporaclefp.failmodification + +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.deletefp.HttpOracleDeleteFPController +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.failmodificationfp.HttpOracleFailModificationFPController +import com.webfuzzing.commons.faults.DefinedFaultCategory +import org.evomaster.core.problem.enterprise.DetectedFaultUtils +import org.evomaster.core.problem.rest.data.HttpVerb +import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test + +class HttpOracleFailModificationFPEMTest : SpringTestBase(){ + + companion object { + @BeforeAll + @JvmStatic + fun init() { + initClass(HttpOracleFailModificationFPController()) + } + } + + + @Test + fun testRunEM() { + + runTestHandlingFlakyAndCompilation( + "HttpOracleFailModificationFPEM", + 200 + ) { args: MutableList -> + + setOption(args, "httpOracles", "true") + + val solution = initAndRun(args) + + assertTrue(solution.individuals.size >= 1) + + assertHasAtLeastOne(solution, HttpVerb.PUT, 400, "/api/resources", null) + assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/api/resources", null) + + val faults = DetectedFaultUtils.getDetectedFaultCategories(solution) + assertFalse( DefinedFaultCategory.HTTP_SIDE_EFFECTS_FAILED_MODIFICATION in faults ) + } + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/invalidallowfp/HttpOracleInvalidAllowFPEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/invalidallowfp/HttpOracleInvalidAllowFPEMTest.kt new file mode 100644 index 0000000000..7e280f0475 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/invalidallowfp/HttpOracleInvalidAllowFPEMTest.kt @@ -0,0 +1,47 @@ +package org.evomaster.e2etests.spring.openapi.v3.httporaclefp.invalidallowfp + +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.deletefp.HttpOracleDeleteFPController +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.failmodificationfp.HttpOracleFailModificationFPController +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.invalidallowfp.HttpOracleInvalidAllowFPController +import com.webfuzzing.commons.faults.DefinedFaultCategory +import org.evomaster.core.problem.enterprise.DetectedFaultUtils +import org.evomaster.core.problem.rest.data.HttpVerb +import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test + +class HttpOracleInvalidAllowFPEMTest : SpringTestBase(){ + + companion object { + @BeforeAll + @JvmStatic + fun init() { + initClass(HttpOracleInvalidAllowFPController()) + } + } + + + @Test + fun testRunEM() { + + runTestHandlingFlakyAndCompilation( + "HttpOracleInvalidAllowFPEM", + 200 + ) { args: MutableList -> + + setOption(args, "httpOracles", "true") + + val solution = initAndRun(args) + + assertTrue(solution.individuals.size >= 1) + + assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/api/invalid-allow-fp", null) + assertNone(solution, HttpVerb.DELETE, 204, "/api/invalid-allow-fp", null) + + val faults = DetectedFaultUtils.getDetectedFaultCategories(solution) + assertFalse( DefinedFaultCategory.SCHEMA_INVALID_ALLOW in faults ) + } + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/invalidlocationfp/HttpOracleInvalidLocationFPEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/invalidlocationfp/HttpOracleInvalidLocationFPEMTest.kt new file mode 100644 index 0000000000..1cb41b9354 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/invalidlocationfp/HttpOracleInvalidLocationFPEMTest.kt @@ -0,0 +1,47 @@ +package org.evomaster.e2etests.spring.openapi.v3.httporaclefp.invalidlocationfp + +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.invalidlocationfp.HttpInvalidLocationFPController +import com.webfuzzing.commons.faults.DefinedFaultCategory +import org.evomaster.core.problem.enterprise.DetectedFaultUtils +import org.evomaster.core.problem.rest.data.HttpVerb +import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test + +class HttpOracleInvalidLocationFPEMTest : SpringTestBase(){ + + companion object { + @BeforeAll + @JvmStatic + fun init() { + initClass(HttpInvalidLocationFPController()) + } + } + + + @Test + fun testRunEM() { + + runTestHandlingFlakyAndCompilation( + "HttpOracleInvalidLocationFPEM", + 200 + ) { args: MutableList -> + + setOption(args, "httpOracles", "true") + + val solution = initAndRun(args) + + assertTrue(solution.individuals.size >= 1) + + assertHasAtLeastOne(solution, HttpVerb.PUT, 200, "/api/invalid-location-fp/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.PUT, 201, "/api/invalid-location-fp/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.DELETE, 404, "/api/invalid-location-fp/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.DELETE, 204, "/api/invalid-location-fp/{id}", null) + + val faults = DetectedFaultUtils.getDetectedFaultCategories(solution) + assertFalse( DefinedFaultCategory.HTTP_INVALID_LOCATION in faults ) + } + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/invalidmergepatchfp/HttpOracleInvalidMergePatchFPEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/invalidmergepatchfp/HttpOracleInvalidMergePatchFPEMTest.kt new file mode 100644 index 0000000000..6fc4b05f62 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/invalidmergepatchfp/HttpOracleInvalidMergePatchFPEMTest.kt @@ -0,0 +1,49 @@ +package org.evomaster.e2etests.spring.openapi.v3.httporaclefp.invalidmergepatchfp + +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.invalidmergepatchfp.HttpInvalidMergePatchFPController +import com.webfuzzing.commons.faults.DefinedFaultCategory +import org.evomaster.core.problem.enterprise.DetectedFaultUtils +import org.evomaster.core.problem.rest.data.HttpVerb +import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test + +class HttpOracleInvalidMergePatchFPEMTest : SpringTestBase(){ + + companion object { + @BeforeAll + @JvmStatic + fun init() { + initClass(HttpInvalidMergePatchFPController()) + } + } + + + @Test + fun testRunEM() { + + runTestHandlingFlakyAndCompilation( + "HttpOracleInvalidMergePatchFPEM", + 200 + ) { args: MutableList -> + + setOption(args, "httpOracles", "true") + setOption(args, "dtoForRequestPayload", "true") + + val solution = initAndRun(args) + + assertTrue(solution.individuals.size >= 1) + + assertHasAtLeastOne(solution, HttpVerb.POST, 201, "/api/merge-patch-fp", null) + assertHasAtLeastOne(solution, HttpVerb.GET, 404, "/api/merge-patch-fp/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/api/merge-patch-fp/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.PATCH, 404, "/api/merge-patch-fp/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.PATCH, 202, "/api/merge-patch-fp/{id}", null) + + val faults = DetectedFaultUtils.getDetectedFaultCategories(solution) + assertFalse( DefinedFaultCategory.HTTP_INVALID_MERGE_PATCH in faults ) + } + } +} diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/nonidempotentputfp/HttpNonIdempotentPutFPEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/nonidempotentputfp/HttpNonIdempotentPutFPEMTest.kt new file mode 100644 index 0000000000..682b3710da --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/nonidempotentputfp/HttpNonIdempotentPutFPEMTest.kt @@ -0,0 +1,49 @@ +package org.evomaster.e2etests.spring.openapi.v3.httporaclefp.nonidempotentputfp + +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.nonidempotentputfp.HttpNonIdempotentPutFPController +import com.webfuzzing.commons.faults.DefinedFaultCategory +import org.evomaster.core.problem.enterprise.DetectedFaultUtils +import org.evomaster.core.problem.rest.data.HttpVerb +import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test + +class HttpNonIdempotentPutFPEMTest : SpringTestBase(){ + + companion object { + @BeforeAll + @JvmStatic + fun init() { + initClass(HttpNonIdempotentPutFPController()) + } + } + + + @Test + fun testRunEM() { + + runTestHandlingFlakyAndCompilation( + "HttpNonIdempotentPutFPEM", + 500 + ) { args: MutableList -> + + setOption(args, "httpOracles", "true") + + val solution = initAndRun(args) + + assertTrue(solution.individuals.size >= 1) + + assertHasAtLeastOne(solution, HttpVerb.POST, 201, "/api/accounts", null) + assertHasAtLeastOne(solution, HttpVerb.GET, 404, "/api/accounts/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/api/accounts/{id}", null) + assertHasAtLeastOne(solution, HttpVerb.PUT, 404, "/api/accounts/{id}/deposit", null) + assertHasAtLeastOne(solution, HttpVerb.PUT, 202, "/api/accounts/{id}/deposit", null) + + + val faults = DetectedFaultUtils.getDetectedFaultCategories(solution) + assertFalse( DefinedFaultCategory.HTTP_NON_IDEMPOTENT_PUT in faults ) + } + } +} \ No newline at end of file diff --git a/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/partialupdateputfp/HttpPartialUpdatePutFPEMTest.kt b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/partialupdateputfp/HttpPartialUpdatePutFPEMTest.kt new file mode 100644 index 0000000000..e8dba5d009 --- /dev/null +++ b/core-tests/e2e-tests/spring/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/httporaclefp/partialupdateputfp/HttpPartialUpdatePutFPEMTest.kt @@ -0,0 +1,46 @@ +package org.evomaster.e2etests.spring.openapi.v3.httporaclefp.partialupdateputfp + +import com.foo.rest.examples.spring.openapi.v3.httporaclefp.partialupdateputfp.HttpPartialUpdatePutFPController +import com.webfuzzing.commons.faults.DefinedFaultCategory +import org.evomaster.core.problem.enterprise.DetectedFaultUtils +import org.evomaster.core.problem.rest.data.HttpVerb +import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +class HttpPartialUpdatePutFPEMTest : SpringTestBase(){ + + companion object { + @BeforeAll + @JvmStatic + fun init() { + initClass(HttpPartialUpdatePutFPController()) + } + } + + //FIXME: fails due to missing "hidden" not ignored as not defined in schema of GET + @Disabled("Currently failing, as revealing a bug in the code") + @Test + fun testRunEM() { + + runTestHandlingFlakyAndCompilation( + "HttpPartialUpdatePutFPEM", + 1000 + ) { args: MutableList -> + + setOption(args, "httpOracles", "true") + + val solution = initAndRun(args) + + assertTrue(solution.individuals.size >= 1) + + assertHasAtLeastOne(solution, HttpVerb.PUT, 200, "/api/resources/{id}", null) + + val faults = DetectedFaultUtils.getDetectedFaultCategories(solution) + assertFalse( DefinedFaultCategory.HTTP_PARTIAL_UPDATE_PUT in faults ) + } + } +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt index b3e7533026..a93fbc8c85 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/fitness/AbstractRestFitness.kt @@ -826,7 +826,9 @@ abstract class AbstractRestFitness : HttpWsFitness() { rcr.setStatusCode(statusCode) rcr.setLocation(response.location?.toString()) - rcr.setAllow(response.allowedMethods.joinToString(",")) + if(response.getHeaderString("allow") != null) { + rcr.setAllow(response.allowedMethods.joinToString(",")) + } rcr.setAppliedLink(appliedLink) rcr.setHeaders(response.stringHeaders)