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,59 @@
package com.foo.rest.examples.bb.headerassertions

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.ResponseEntity
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])
@RestController
@RequestMapping(path = ["/api/headerassertions"])
open class BBHeaderAssertionsApplication {

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

@GetMapping(path = ["/ok/401"])
fun ok401(): ResponseEntity<String>{
CoveredTargets.cover("ok401")
return ResponseEntity.status(401).header("WWW-Authenticate","Basic realm=\"Realm\"").build()
}

@GetMapping(path = ["/fail/401"])
fun fail401(): ResponseEntity<String>{
CoveredTargets.cover("fail401")
return ResponseEntity.status(401).build()
}

@GetMapping(path = ["/ok/405"])
fun ok405(): ResponseEntity<String>{
CoveredTargets.cover("ok405")
return ResponseEntity.status(405).header("Allow","POST,OPTIONS").build()
}

@GetMapping(path = ["/fail/405"])
fun fail405(): ResponseEntity<String>{
CoveredTargets.cover("fail405")
return ResponseEntity.status(405).build()
}

@GetMapping(path = ["/ok/426"])
fun ok426(): ResponseEntity<String>{
CoveredTargets.cover("ok426")
return ResponseEntity.status(426).header("Upgrade","foo \"bar\" \$x").build()
}

@GetMapping(path = ["/fail/426"])
fun fail426(): ResponseEntity<String>{
CoveredTargets.cover("fail426")
return ResponseEntity.status(426).build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.foo.rest.examples.bb.headerassertions

import com.foo.rest.examples.bb.SpringController

class BBHeaderAssertionsController : SpringController(BBHeaderAssertionsApplication::class.java)
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.evomaster.e2etests.spring.rest.bb.headerassertions

import com.foo.rest.examples.bb.headerassertions.BBHeaderAssertionsController
import org.evomaster.core.output.OutputFormat
import org.evomaster.core.problem.enterprise.DetectedFaultUtils
import org.evomaster.core.problem.enterprise.ExperimentalFaultCategory
import org.evomaster.core.problem.rest.data.HttpVerb
import org.evomaster.e2etests.spring.rest.bb.SpringTestBase
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

class BBHeaderAssertionsEMTest : SpringTestBase() {

companion object {
init {
shouldApplyInstrumentation = false
}

@BeforeAll
@JvmStatic
fun init() {
initClass(BBHeaderAssertionsController())
}
}



@ParameterizedTest
@EnumSource
fun testBlackBoxOutput(outputFormat: OutputFormat) {

executeAndEvaluateBBTest(
outputFormat,
"headerassertions",
100,
3,
listOf("ok401","fail401","ok405","fail405","ok426","fail426")
){ args: MutableList<String> ->

setOption(args, "useExperimentalOracles", "true")
setOption(args, "statusOracles", "true")

val solution = initAndRun(args)

assertTrue(solution.individuals.size >= 1)

val prefix = "/api/headerassertions"

assertHasAtLeastOne(solution, HttpVerb.GET, 401, "$prefix/ok/401", null)
assertHasAtLeastOne(solution, HttpVerb.GET, 401, "$prefix/fail/401", null)
assertHasAtLeastOne(solution, HttpVerb.GET, 405, "$prefix/ok/405", null)
assertHasAtLeastOne(solution, HttpVerb.GET, 405, "$prefix/fail/405", null)
assertHasAtLeastOne(solution, HttpVerb.GET, 426, "$prefix/ok/426", null)
assertHasAtLeastOne(solution, HttpVerb.GET, 426, "$prefix/fail/426", null)

val faults = DetectedFaultUtils.getDetectedFaults(solution)
assertTrue(faults.isNotEmpty())

assertTrue(faults.none{
it.operationId == "GET:$prefix/ok/401"
&& it.category == ExperimentalFaultCategory.HTTP_STATUS_NO_401_IF_NO_WWW_AUTHENTICATE })
assertTrue(faults.any{
it.operationId == "GET:$prefix/fail/401"
&& it.category == ExperimentalFaultCategory.HTTP_STATUS_NO_401_IF_NO_WWW_AUTHENTICATE })

assertTrue(faults.none{
it.operationId == "GET:$prefix/ok/405"
&& it.category == ExperimentalFaultCategory.HTTP_STATUS_NO_405_IF_NO_ALLOW })
assertTrue(faults.any{
it.operationId == "GET:$prefix/fail/405"
&& it.category == ExperimentalFaultCategory.HTTP_STATUS_NO_405_IF_NO_ALLOW })

assertTrue(faults.none{
it.operationId == "GET:$prefix/ok/426"
&& it.category == ExperimentalFaultCategory.HTTP_STATUS_NO_426_IF_NO_UPGRADE })
assertTrue(faults.any{
it.operationId == "GET:$prefix/fail/426"
&& it.category == ExperimentalFaultCategory.HTTP_STATUS_NO_426_IF_NO_UPGRADE })
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,52 @@ abstract class HttpWsTestCaseWriter : ApiTestCaseWriter() {
//----------------------------------------------------------------------------------------
// assertion lines

protected fun addHeaderAssertions(lines: Lines, res: HttpWsCallResult, responseVariableName: String?){

val status = res.getStatusCode()

//TODO: verb order in Allow header is flaky
val allow = res.getAllow() //could had rather checked if was OPTIONS, but we don't have that info as input here
if(!allow.isNullOrBlank() || status == 405){
addAssertionOnHeader(lines, "allow", res.getHeader("allow"), true, responseVariableName)
}
if(status == 401){
addAssertionOnHeader(lines, "www-authenticate", res.getHeader("www-authenticate"), false, responseVariableName)
}
if(status == 426) {
addAssertionOnHeader(lines, "upgrade", res.getHeader("upgrade"), false, responseVariableName)
}
}

protected fun addAssertionOnHeader(lines: Lines, name: String, value: String?, flaky: Boolean, responseVariableName: String?){

val instruction =
if(value != null) {
val escaped = GeneUtils.applyEscapes(value, GeneUtils.EscapeMode.ASSERTION, format)
when {
format.isJavaOrKotlin() -> ".header(\"$name\", \"$escaped\")"
format.isJavaScript() ->
"expect($responseVariableName.header[\"$name\"].startsWith(\"$escaped\")).toBe(true);"
format.isPython() -> "assert \"$escaped\" in $responseVariableName.headers[\"$name\"]"
else -> throw IllegalStateException("Unsupported format $format")
}
} else {
when {
format.isJavaOrKotlin() -> ".header(\"$name\", isEmptyOrNullString())"
format.isJavaScript() ->
"expect($responseVariableName.header[\"$name\"]).toBeUndefined();"
format.isPython() -> "assert \"$name\" not in $responseVariableName.headers"
else -> throw IllegalStateException("Unsupported format $format")
}
}

if(flaky) {
lines.addSingleCommentLine(instruction)
} else {
lines.add(instruction)
}
}

protected fun handleResponseAssertions(lines: Lines, res: HttpWsCallResult, responseVariableName: String?) {

assert(responseVariableName != null || format.isJavaOrKotlin())
Expand All @@ -837,19 +883,7 @@ abstract class HttpWsTestCaseWriter : ApiTestCaseWriter() {
lines.add(".assertThat()")
}

val allow = res.getAllow()
if(!allow.isNullOrBlank()){
val instruction = when {
format.isJavaOrKotlin() -> ".header(\"Allow\", \"$allow\")"
format.isJavaScript() ->
"expect($responseVariableName.header[\"allow\"].startsWith(\"$allow\")).toBe(true);"
format.isPython() -> "assert \"$allow\" in $responseVariableName.headers[\"allow\"]"
else -> throw IllegalStateException("Unsupported format $format")
}
//lines.add(instruction)
//TODO: verb order in Allow header is flaky
lines.addSingleCommentLine(instruction)
}
addHeaderAssertions(lines, res, responseVariableName)

if (res.getTooLargeBody()) {
lines.addSingleCommentLine("the response payload was too large, above the threshold of ${config.maxResponseByteSize} bytes." +
Expand Down
Loading