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 index e8dba5d009..07b9a213f4 100644 --- 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 @@ -8,7 +8,6 @@ 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(){ @@ -21,8 +20,6 @@ class HttpPartialUpdatePutFPEMTest : SpringTestBase(){ } } - //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() { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/oracle/HttpSemanticsOracle.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/oracle/HttpSemanticsOracle.kt index b0736b5e4d..3564cccad3 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/oracle/HttpSemanticsOracle.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/oracle/HttpSemanticsOracle.kt @@ -346,9 +346,13 @@ object HttpSemanticsOracle { return null } - val wipedFields = computeWipedFields(allPutSchemaFields - sentFields, schema, get) + val getSchemaFields = extractGetSchemaFields(schema, get) + val comparableSentFields = if (getSchemaFields.isEmpty()) sentFields else sentFields intersect getSchemaFields + val wipedFields = if (getSchemaFields.isEmpty()) emptySet() else (allPutSchemaFields - sentFields) intersect getSchemaFields - val mismatches = mismatchedPutFields(putBody ?: "", getBody, sentFields, wipedFields, bodyParam) + if (comparableSentFields.isEmpty() && wipedFields.isEmpty()) return null + + val mismatches = mismatchedPutFields(putBody ?: "", getBody, comparableSentFields, wipedFields, bodyParam) if(mismatches.isEmpty()){ return null } @@ -391,21 +395,18 @@ object HttpSemanticsOracle { } /** - * Wiped candidates are restricted to fields the GET schema actually exposes, otherwise + * Sent and wiped candidates are restricted to fields the GET schema actually exposes, otherwise * write-only fields (e.g. passwords) would cause false positives. */ - private fun computeWipedFields( - candidates: Set, + private fun extractGetSchemaFields( schema: RestSchema?, get: RestCallAction ): Set { - if (candidates.isEmpty() || schema == null) return emptySet() - val getSchemaFields = SchemaUtils.extractResponseSchemaFields( + if (schema == null) return emptySet() + return SchemaUtils.extractResponseSchemaFields( schema, get.path.toString(), HttpVerb.GET, statusMatcher = SchemaUtils.statusGroupMatcher(StatusGroup.G_2xx) ) - if (getSchemaFields.isEmpty()) return emptySet() - return candidates intersect getSchemaFields } internal fun mismatchedPutFields( diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/oracle/HttpSemanticsOracleTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/oracle/HttpSemanticsOracleTest.kt index 68997021f2..12ae5e943d 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/oracle/HttpSemanticsOracleTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/oracle/HttpSemanticsOracleTest.kt @@ -647,6 +647,51 @@ class HttpSemanticsOracleTest { assertFalse(mismatch) } + @Test + fun testPut_sentWriteOnlyFieldNotInGetSchema_noFalsePositive() { + val schema = buildUsersSchema( + putWritable = listOf("name", "hidden"), + getResponseFields = listOf("name", "timestamp") + ) + val mismatch = runMismatchedPutOracle( + path = "/users", + putBody = jsonPutBodyParam(activeFields = mapOf("name" to "Alice", "hidden" to "secret")), + getResponseBody = """{"name":"Alice","timestamp":"123"}""", + schema = schema + ) + assertFalse(mismatch) + } + + @Test + fun testPut_sentWriteOnlyFieldNotInGetSchema_exposedFieldChanged_returnsTrue() { + val schema = buildUsersSchema( + putWritable = listOf("name", "hidden"), + getResponseFields = listOf("name") + ) + val mismatch = runMismatchedPutOracle( + path = "/users", + putBody = jsonPutBodyParam(activeFields = mapOf("name" to "Alice", "hidden" to "secret")), + getResponseBody = """{"name":"Bob"}""", + schema = schema + ) + assertTrue(mismatch) + } + + @Test + fun testPut_allSentFieldsWriteOnly_returnsFalse() { + val schema = buildUsersSchema( + putWritable = listOf("hidden"), + getResponseFields = listOf("id") + ) + val mismatch = runMismatchedPutOracle( + path = "/users", + putBody = jsonPutBodyParam(activeFields = mapOf("hidden" to "secret")), + getResponseBody = """{"id":"1"}""", + schema = schema + ) + assertFalse(mismatch) + } + @Test fun testPut_putReturnedNon2xx_returnsFalse() { val mismatch = runMismatchedPutOracle(