Send target ids in the request body when fetching coverage, to avoid 414 - #1742
Merged
Conversation
Collaborator
Author
|
What I'm not sure about is whether to leave the |
Collaborator
Author
|
should change gets in the tests to new post, but leave get enpoint for backward compatibility - from meeting with @jgaleotti |
jgaleotti
approved these changes
Sep 9, 2026
arcuri82
approved these changes
Sep 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
RemoteControllerImplementation.getTestResultssent the ids of the targets as anidsquery parameter. On a large search there are thousands of them, and the resulting URI does
not fit in the request line: Jetty rejects anything above 8192 bytes with a 414, before
the request ever reaches
EMController, so the driver code never runs and cannot doanything about it.
Measured on the
jaspercase study (120s, seed 1): during the recomputation stage of theminimization,
EnterpriseFitnessrefetches descriptive ids for 1554 targets, which is7302 characters of query string. Together with the path, the other query parameters and
the standard headers, that is over the limit.
On the EvoMaster side the 414 is not an exception.
getTestResultsreturnsnull, thecaller logs
Cannot retrieve coverage with full descriptive ids, and the individual isdropped along with all of its targets. 22 individuals were lost this way in a single
jasper run. The failure is silent: nothing in the output says that coverage was thrown away.
Change
EMControllergains aPOSTvariant oftestResultsthat takes the ids in the body.Both variants delegate to the same private
computeTestResults, so there is exactly oneimplementation of the logic.
already does for the empty
idsquery parameter. Passing an empty set instead ofnullwould silently return no target at all.
RemoteControllerImplementationnow uses the POST variant.GETvariant is kept. It is part of the public contract of the Driver, it is what anyCore older than this change talks to, and it is the only form usable by hand (eg with
curl) to inspect a running Driver.A
POSTfor what is a read is not great REST, but there is no alternative: aGETcarryinga body cannot be sent by the HTTP client in use, as the JDK rewrites the method to
POSTonits own as soon as anything is written to the connection.
Test
RemoteControllerManyTargetsTestdrives the real client against a real driver and asks forthe 1554 targets that exposed the problem, with ids of realistic magnitude (5 digits, as at
the end of a jasper run). It asserts on the outcome of the API call rather than on how the
ids happen to be transported, so it keeps passing unchanged if the transport moves again,
and it catches a regression if it ever moves back into the URI.
Compatibility
A Core with this change requires a Driver with this change: there is no fallback to the
GETvariant if thePOSTis not there. The opposite direction is fine, since theGETisstill served. Anything that pins the client version needs to be updated together with the
Core — in EMB,
evomaster-versioninjdk_25_maven/pom.xml.Effect
Measured on
jasper, 300s, 12 successful runs over seeds 1-9, 6 per configuration.--heuristicsForSQLAdvanced=truewas needed in all of them to work around an unrelatedpre-existing problem with table functions in this SUT.
Cannot retrieve coverage--minimizeThresholdForLoss=0)With the loss threshold at 0 the warning fires if a single target is lost, so this is a
categorical result rather than an average.
Minimization now behaves as it should: comparing
--minimize=trueagainst--minimize=falsegives +343 targets (+3.0%, Â12 = 0.833) and 29 fewer generated tests (-6.1%). Before,
the reported number went down by 425 when minimizing.
GET vs POST
The obvious alternative is to keep the
GETand move the ids into its body. EvoMasteralready does exactly that elsewhere:
AbstractRestFitnessbuildsbuilder.build("GET", bodyEntity)forGETandDELETE(AbstractRestFitness.kt:1132-1133),citing RFC 9110 §9.3.1. That works because those calls go through the client from
HttpClientFactory.createTrustingJerseyClient(), which configuresApacheConnectorProvider(
HttpClientFactory.kt:62) precisely for this — its comment says the Jersey default "does nothandle PATCH properly and body payloads in GET/DELETE" — and disables JAX-RS compliance
validation (
HttpClientFactory.kt:74).The problem is that this endpoint is not that connection. Calls to the SUT are made with the
Apache-backed client; the Core-to-Driver connection uses
ClientBuilder.newClient()(
RemoteControllerImplementation.kt:61), ie Jersey's defaultHttpUrlConnectoroverjava.net.HttpURLConnection, which silently rewrites the method toPOSTas soon as anythingis written to the connection.
Switching this client to the Apache connector would make a
GETwith a body technicallypossible, but it would make the protocol depend on which connector is actually active at
runtime — and per the comment in
HttpClientFactory.kt:80-88, that is not something we canrely on or even check:
The assertion that would verify the connector is commented out for that reason. So if the
connector silently degrades, the
GETbecomes aPOSTagainst an endpoint that would notexist, or fails compliance validation — and it breaks every coverage request of every run,
not just a few SUT calls.
Two further reasons not to take that route:
@GEThas only@QueryParams and never reads abody, so
idswould default to empty, which that endpoint documents as "return everything".A Core sending the ids in a
GETbody against an older Driver would silently get alltargets instead of the requested subset. With
POSTthe same mismatch is an immediate 405..NET drivers.
POSTwith a JSON body is supported out of the box by every server framework;reading a body on
GETis not.