Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.json

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


@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping(path = ["/api/accounts"])
@RestController
open class HttpNonIdempotentPutApplication {

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(HttpNonIdempotentPutApplication::class.java, *args)
}

private val data = mutableMapOf<Int, AccountData>()

fun reset(){
data.clear()
}
}

data class AccountData(
var balance: Int
)

data class DepositRequest(
val amount: Int
)


@PostMapping()
open fun create(@RequestBody body: AccountData): ResponseEntity<AccountData> {
val id = data.size + 1
data[id] = body.copy()
return ResponseEntity.status(201).body(data[id])
}

@GetMapping(path = ["/{id}"])
open fun get(@PathVariable("id") id: Int): ResponseEntity<AccountData> {
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<AccountData> {

val resource = data[id]
?: return ResponseEntity.status(404).build()

// wrong: PUT must be idempotent, but each call accumulates the deposit
resource.balance += body.amount

return ResponseEntity.status(200).body(resource)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.urlencoded

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ModelAttribute
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.RequestMapping
import org.springframework.web.bind.annotation.RestController


@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping(path = ["/api/accounts"])
@RestController
open class HttpNonIdempotentPutUrlEncodedApplication {

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(HttpNonIdempotentPutUrlEncodedApplication::class.java, *args)
}

private val data = mutableMapOf<Int, AccountData>()

fun reset(){
data.clear()
}
}

open class AccountData(
var balance: Int = 0
)

open class DepositRequest(
var amount: Int = 0
)


@PostMapping(
consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE],
produces = [MediaType.APPLICATION_JSON_VALUE]
)
open fun create(@ModelAttribute body: AccountData): ResponseEntity<AccountData> {
val id = data.size + 1
data[id] = AccountData(balance = body.balance)
return ResponseEntity.status(201).body(data[id])
}

@GetMapping(
path = ["/{id}"],
produces = [MediaType.APPLICATION_JSON_VALUE]
)
open fun get(@PathVariable("id") id: Int): ResponseEntity<AccountData> {
val resource = data[id]
?: return ResponseEntity.status(404).build()
return ResponseEntity.status(200).body(resource)
}

@PutMapping(
path = ["/{id}/deposit"],
consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE],
produces = [MediaType.APPLICATION_JSON_VALUE]
)
open fun deposit(
@PathVariable("id") id: Int,
@ModelAttribute body: DepositRequest
): ResponseEntity<AccountData> {

val resource = data[id]
?: return ResponseEntity.status(404).build()

// wrong: PUT must be idempotent, but each call accumulates the deposit
resource.balance += body.amount

return ResponseEntity.status(200).body(resource)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.xml

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
import org.springframework.http.MediaType
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 javax.xml.bind.annotation.XmlAccessType
import javax.xml.bind.annotation.XmlAccessorType
import javax.xml.bind.annotation.XmlRootElement


@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping(path = ["/api/accounts"])
@RestController
open class HttpNonIdempotentPutXMLApplication {

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(HttpNonIdempotentPutXMLApplication::class.java, *args)
}

private val data = mutableMapOf<Int, AccountData>()

fun reset(){
data.clear()
}
}

@XmlRootElement(name = "accountData")
@XmlAccessorType(XmlAccessType.FIELD)
open class AccountData(
var balance: Int = 0
)

@XmlRootElement(name = "depositRequest")
@XmlAccessorType(XmlAccessType.FIELD)
open class DepositRequest(
var amount: Int = 0
)


@PostMapping(
consumes = [MediaType.APPLICATION_XML_VALUE],
produces = [MediaType.APPLICATION_XML_VALUE]
)
open fun create(@RequestBody body: AccountData): ResponseEntity<AccountData> {
val id = data.size + 1
data[id] = AccountData(balance = body.balance)
return ResponseEntity.status(201).body(data[id])
}

@GetMapping(
path = ["/{id}"],
produces = [MediaType.APPLICATION_XML_VALUE]
)
open fun get(@PathVariable("id") id: Int): ResponseEntity<AccountData> {
val resource = data[id]
?: return ResponseEntity.status(404).build()
return ResponseEntity.status(200).body(resource)
}

@PutMapping(
path = ["/{id}/deposit"],
consumes = [MediaType.APPLICATION_XML_VALUE],
produces = [MediaType.APPLICATION_XML_VALUE]
)
open fun deposit(
@PathVariable("id") id: Int,
@RequestBody body: DepositRequest
): ResponseEntity<AccountData> {

val resource = data[id]
?: return ResponseEntity.status(404).build()

// wrong: PUT must be idempotent, but each call accumulates the deposit
resource.balance += body.amount

return ResponseEntity.status(200).body(resource)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.json

import com.foo.rest.examples.spring.openapi.v3.SpringController


class HttpNonIdempotentPutController: SpringController(HttpNonIdempotentPutApplication::class.java){

override fun resetStateOfSUT() {
HttpNonIdempotentPutApplication.reset()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.urlencoded

import com.foo.rest.examples.spring.openapi.v3.SpringController
import com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.xml.HttpNonIdempotentPutXMLApplication


class HttpNonIdempotentPutUrlencodedController: SpringController(HttpNonIdempotentPutUrlEncodedApplication::class.java){

override fun resetStateOfSUT() {
HttpNonIdempotentPutUrlEncodedApplication.reset()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.xml

import com.foo.rest.examples.spring.openapi.v3.SpringController
import com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.xml.HttpNonIdempotentPutXMLApplication


class HttpNonIdempotentPutXMLController: SpringController(HttpNonIdempotentPutXMLApplication::class.java){

override fun resetStateOfSUT() {
HttpNonIdempotentPutXMLApplication.reset()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.evomaster.e2etests.spring.openapi.v3.httporacle.nonidempotentput

import com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.json.HttpNonIdempotentPutController
import org.evomaster.core.problem.enterprise.DetectedFaultUtils
import org.evomaster.core.problem.enterprise.ExperimentalFaultCategory
import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test

class HttpNonIdempotentPutEMTest : SpringTestBase(){

companion object {
@BeforeAll
@JvmStatic
fun init() {
initClass(HttpNonIdempotentPutController())
}
}


@Test
fun testRunEM() {

runTestHandlingFlakyAndCompilation(
"HttpNonIdempotentPutEM",
500
) { args: MutableList<String> ->

setOption(args, "security", "false")
setOption(args, "schemaOracles", "false")
setOption(args, "httpOracles", "true")
setOption(args, "useExperimentalOracles", "true")

val solution = initAndRun(args)

assertTrue(solution.individuals.size >= 1)

val faults = DetectedFaultUtils.getDetectedFaultCategories(solution)
assertEquals(1, faults.size)
assertEquals(ExperimentalFaultCategory.HTTP_NON_IDEMPOTENT_PUT, faults.first())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.evomaster.e2etests.spring.openapi.v3.httporacle.nonidempotentput

import com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.json.HttpNonIdempotentPutController
import com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.urlencoded.HttpNonIdempotentPutUrlencodedController
import com.foo.rest.examples.spring.openapi.v3.httporacle.nonidempotentput.xml.HttpNonIdempotentPutXMLController
import org.evomaster.core.problem.enterprise.DetectedFaultUtils
import org.evomaster.core.problem.enterprise.ExperimentalFaultCategory
import org.evomaster.e2etests.spring.openapi.v3.SpringTestBase
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test

class HttpNonIdempotentPutUrlencodedEMTest : SpringTestBase(){

companion object {
@BeforeAll
@JvmStatic
fun init() {
initClass(HttpNonIdempotentPutUrlencodedController())
}
}


@Test
fun testRunEM() {

runTestHandlingFlakyAndCompilation(
"HttpNonIdempotentPutUrlencodedEM",
1000
) { args: MutableList<String> ->

setOption(args, "security", "false")
setOption(args, "schemaOracles", "false")
setOption(args, "httpOracles", "true")
setOption(args, "useExperimentalOracles", "true")

val solution = initAndRun(args)

assertTrue(solution.individuals.size >= 1)

val faults = DetectedFaultUtils.getDetectedFaultCategories(solution)
assertEquals(1, faults.size)
assertEquals(ExperimentalFaultCategory.HTTP_NON_IDEMPOTENT_PUT, faults.first())
}
}
}
Loading
Loading