diff --git a/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt b/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt index 7870281982..d6d0a4881c 100644 --- a/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt +++ b/core/src/main/kotlin/org/evomaster/core/output/service/RestTestCaseWriter.kt @@ -436,6 +436,7 @@ class RestTestCaseWriter : HttpWsTestCaseWriter { "\"$path\"" } + //FIXME this should be same algorithm as in AbstractRestFitness val idPointer = res.getResourceId()?.pointer ?: "/id" val extract = extractValueFromJsonResponse(resVarName, idPointer) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt index ee7f7e2981..7d3901b8d6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt @@ -329,7 +329,7 @@ class RestPath(path: String) { gene.getViewOfElements() .joinToString("&") { "$name=${encode(it.getValueAsRawString())}" } } else { - val value = encode(gene!!.getValueAsRawString()) + val value = encode(gene.getValueAsRawString()) "$name=$value" } } @@ -391,6 +391,12 @@ class RestPath(path: String) { it.value.name == t.name && (it.value.scope == null || it.value.scope == RestLinkParameter.Scope.PATH) }?.key + /* + TODO are these correct??? are we properly escaping? + also, URI does not comply with RFC 3968... :( + need more testing + */ + if(variable != null){ /* reserved characters need to be encoded diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/CallGraphService.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/CallGraphService.kt index 3f4e09a709..0565430881 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/CallGraphService.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/CallGraphService.kt @@ -65,7 +65,7 @@ class CallGraphService { /** * Check in the schema if there is any action which is a direct child of [a] and last path element is a parameter */ - fun hasParameterChild(a: RestCallAction): Boolean { + fun isThereChildActionWithParameter(a: RestCallAction): Boolean { return sampler.seeAvailableActions() .filterIsInstance() .map { it.path } @@ -81,9 +81,9 @@ class CallGraphService { fun resolveLocationForParentOfChildOperationUsingCreatedResource(create: RestCallAction): String? { - if(hasParameterChild(create)) { + if(isThereChildActionWithParameter(create)) { //simple case - return create.resolvedPath() + return create.resolvedOnlyPath() } /* 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 b2a39cc9ac..b4acf9696f 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 @@ -63,7 +63,10 @@ import org.evomaster.core.taint.TaintAnalysis import org.evomaster.core.utils.StackTraceUtils import org.slf4j.Logger import org.slf4j.LoggerFactory +import java.net.URI import java.net.URL +import java.net.URLEncoder +import java.nio.charset.StandardCharsets import javax.annotation.PostConstruct import javax.inject.Inject import javax.ws.rs.ProcessingException @@ -877,23 +880,41 @@ abstract class AbstractRestFitness : HttpWsFitness() { it is or not a valid char. Furthermore, likely needed to be done in resolveLocation, or at least check how RestAssured would behave + TODO update RestPathTest, check TODO there, once fixed */ //it.replace("\"", "") + //FIXME outputFormat shouldn't really be used here + //FIXME in resolveLocation GeneUtils.applyEscapes(it, GeneUtils.EscapeMode.URI, configuration.outputFormat) } + Lazy.assert { URI.create(fullUri).isAbsolute } - val builder = if (a.produces.isEmpty()) { - log.debug("No 'produces' type defined for {}", path) - client.target(fullUri).request("*/*") + val builder = try { + if (a.produces.isEmpty()) { + log.debug("No 'produces' type defined for {}", path) + client.target(fullUri).request("*/*") - } else { - /* + } else { + /* TODO: This only considers the first in the list of produced responses This is fine for endpoints that only produce one type of response. Could be a problem in future */ - client.target(fullUri).request(a.produces.first()) + client.target(fullUri).request(a.produces.first()) + } + } catch (e: Exception) { + /* + FIXME we need to solve this issue somehow, as location values might be invalid... + but i guess that should be done in resolveLocation + */ + throw RuntimeException(""" + Failed to build HTTP invocation. + Resolved path: $path + Location header: $locationHeader + Resolved location: $fullUri + Error: ${e.message} + """.trimIndent(), e) } handleHeaders(a, builder, cookies, tokens) @@ -1004,8 +1025,17 @@ abstract class AbstractRestFitness : HttpWsFitness() { val id = rcr.getResourceId() if (id != null) { - location = callGraphService.resolveLocationForChildOperationUsingCreatedResource(a,id.value) + + //FIXME tmp fix. need to be handled properly, also in generated tests with test-utils-* + val escapedId = URLEncoder.encode(id.value, StandardCharsets.UTF_8) + .replace("+", "%20"); + + location = callGraphService.resolveLocationForChildOperationUsingCreatedResource(a,escapedId) if(location != null) { + /* + FIXME this case seems ignored in RestTestCaseWriter.handleLocationHeader. + Need proper handling + E2E for all these cases + */ rcr.setHeuristicsForChainedLocation(true) } } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/regex/QuantifierRxGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/regex/QuantifierRxGene.kt index 49ff6bfb5d..d6d5469404 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/regex/QuantifierRxGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/regex/QuantifierRxGene.kt @@ -200,7 +200,9 @@ class QuantifierRxGene( } for (i in 0 until other.atoms.size) { - if (!this.atoms[i].containsSameValueAs(other.atoms[i])) { + val x = this.atoms[i] + val y = other.atoms[i] + if (!x.possiblySame(y) || !x.containsSameValueAs(y)) { return false } } diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt index 8e3abbe0c4..8c05aa3aaf 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/string/StringGene.kt @@ -822,9 +822,10 @@ class StringGene( try { toAddGenes.add(RegexHandler.createGeneForJVM(regex)) log.trace("Regex, added specification for: {}", regex) - } catch (e: Exception) { LoggingUtil.uniqueWarn(log, "Failed to handle regex: $regex") + } catch (e: java.lang.StackOverflowError){ + LoggingUtil.uniqueWarn(log, "Failed to handle regex, as it gives a stack overflow error: $regex") } } diff --git a/core/src/test/kotlin/org/evomaster/core/parser/RegexHandlerTest.kt b/core/src/test/kotlin/org/evomaster/core/parser/RegexHandlerTest.kt index e98878b4f7..ef41b38b14 100644 --- a/core/src/test/kotlin/org/evomaster/core/parser/RegexHandlerTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/parser/RegexHandlerTest.kt @@ -8,12 +8,20 @@ import org.evomaster.core.search.service.AdaptiveParameterControl import org.evomaster.core.search.service.Randomness import org.evomaster.core.search.service.mutator.MutationWeightControl import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import java.util.regex.Pattern internal class RegexHandlerTest{ + @Disabled("Needs to hande lookahead in regex") + @Test + fun testLanguageTool(){ + val s = "^((?iu)@.+)$" + RegexHandler.createGeneForJVM(s) + } + @Test fun testCwaIssue(){ diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt index 3f6e247101..7ddf3d809b 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt @@ -1,6 +1,7 @@ package org.evomaster.core.problem.rest import io.swagger.v3.oas.models.parameters.Parameter +import org.evomaster.core.output.OutputFormat import org.evomaster.core.problem.rest.builder.RestActionBuilderV3 import org.evomaster.core.problem.rest.data.HttpVerb import org.evomaster.core.problem.rest.data.RestPath @@ -10,13 +11,44 @@ import org.evomaster.core.search.gene.collection.ArrayGene import org.evomaster.core.search.gene.wrapper.CustomMutationRateGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.string.StringGene +import org.evomaster.core.search.gene.utils.GeneUtils +import org.glassfish.jersey.uri.internal.JerseyUriBuilder import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource +import java.net.URISyntaxException +import org.junit.jupiter.api.assertThrows +import org.mockserver.configuration.Configuration.configuration internal class RestPathTest{ + @Test + fun testFamilieBaSakIssue(){ + + val x = "/api/satsendring/kjorsatsendring?EMextraParam123=42/Trigget satsendring for fagsakene []" + + assertThrows{JerseyUriBuilder.fromUri(x).build()} + + val path = RestPath("/api/satsendring/kjorsatsendring") + val q = QueryParam("EMextraParam123", StringGene("EMextraParam123", "42/Trigget satsendring for fagsakene []")) + + val uri = path.resolve(listOf(q)) + + assertNotEquals(x, uri) + + JerseyUriBuilder.fromUri(uri).build() + + // check escape + //TODO update once fixing AbstractRestFitness + val y = "/api/satsendring/kjorsatsendring/Trigget satsendring for fagsakene []" + JerseyUriBuilder.fromUri(y).build() + + val e = GeneUtils.applyEscapes(y, GeneUtils.EscapeMode.URI, OutputFormat.JAVA_JUNIT_4) + //FIXME spaces are not escaped + //assertNotEquals(y,e) + } + @Test fun testNameQualifier(){