AsyncAPI 3.x: the fitness, and --problemType ASYNCAPI runs - #1753
Draft
LautaroPetaccio wants to merge 4 commits into
Draft
AsyncAPI 3.x: the fitness, and --problemType ASYNCAPI runs#1753LautaroPetaccio wants to merge 4 commits into
LautaroPetaccio wants to merge 4 commits into
Conversation
LautaroPetaccio
force-pushed
the
feature/asyncapi-fitness
branch
from
September 12, 2026 23:05
8347c38 to
eea6cba
Compare
LautaroPetaccio
added this pull request to stack #1712
September 13, 2026 19:50
LautaroPetaccio
force-pushed
the
feature/asyncapi-fitness
branch
from
September 13, 2026 20:27
bf16b4d to
02bd557
Compare
LautaroPetaccio
force-pushed
the
feature/asyncapi-fitness
branch
from
September 13, 2026 20:42
02bd557 to
0cfc38c
Compare
RemoteController.executeNewAsyncApiActionAndGetReply PUTs the action to the driver and reads back an AsyncApiReplyDto, as its RPC counterpart does. It has a default that throws, so the controllers that never publish need not say so one by one. EMConfig gains asyncApiReplyTimeoutMs, experimental, and a constraint: there is no test writer for AsyncAPI yet, so a run must say --createTests false rather than search for an hour and fail at the end. The two tests that already parsed --problemType ASYNCAPI say so now. Two experimental fault categories: a promised reply that never arrives, and a reply matching none of the messages the contract declares.
AsyncApiBlackBoxFitness publishes each message of a test through the driver and turns what comes back into targets: for every operation, what publishing to it was seen to do, and, when a reply came back, which of the messages the contract declares for the reply it was. A contract listing a result and an error thus gives the search two things to reach, which is the AsyncAPI analogue of REST's (status x endpoint). AsyncApiReplyClassifier is what recognises a reply: a structural match against each declared payload schema, reading the parts of JSON Schema that tell one message from another and giving the benefit of the doubt on the rest. The most specific match wins. AsyncApiModule binds it all, with the driver bound unconditionally, and Main uses it in place of the message it showed until now. One test runs a whole MIO search against a stand-in for the NCS service and sees both declared replies of an operation covered.
Review: with room for exactly two messages, a one-message test was mutated by removal, leaving a test that publishes nothing; and a test could never grow to the maximum the user allowed, only to one less. Both bounds are now stated as such, and AsyncApiStructureMutatorTest holds them. Seeding test cases is refused at start-up, like writing them, instead of failing inside the sampler. The whole-search suite is named after the class it exercises, AsyncApiModule, and the three suites that build the injector share how they do it.
Review: the ':' joining a target id and the '-' inside a correlation id were spelled out where used; they are constants now, and the two kinds of target id are built in one place each. The remote controller named its queryFromDatabase parameter four times over, once in the new call and three in the ones it mirrors; it is a constant now. The fake driver's one field comes before its companion.
LautaroPetaccio
force-pushed
the
feature/asyncapi-fitness
branch
from
September 13, 2026 20:52
0cfc38c to
a3b119e
Compare
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.
Twelfth in the AsyncAPI stack, on top of #1752. With this one
--problemType ASYNCAPIruns a whole search, everything but writing the tests.What a search over an AsyncAPI service looks for
REST covers
(status × endpoint). There are no status codes here, so the targets are:(outcome × operation)— what publishing to an operation was seen to do:PUBLISHED(fire-and-forget),REPLIED, orNO_REPLY.(declared reply × operation)— when a reply came back, which of the messages the contract declares for that reply it was. A reply channel that lists a result and an error gives the search two things to reach, and telling them apart is what makes an asynchronous service observable from outside.Two things are faults (both
@Experimentalcategories): a promised reply that never arrives, and a reply that matches none of the declared messages. A message the driver could not publish is neither — it is a broken setup, no target is registered for it, and the test stops there.Recognising a reply
AsyncApiReplyClassifiermatches the observed payload against each declared payload schema. It is deliberately a classifier, not a validator: it reads the parts of JSON Schema that tell one message from another —type(including type lists),required,const/enum,allOf/anyOf/oneOf,items,$refto component schemas — and gives the benefit of the doubt on anything it does not understand (formats, bounds, patterns). When several declared messages fit, the most specific wins: the one pinning down the most top-level fields. No new dependency.The fitness
AsyncApiBlackBoxFitnessresolves everything the driver needs from the document — address, content type, correlation location and pointer, reply address, timeout — so the driver never reads the document. The correlation id isrunId-counter, withrunIddrawn fromRandomness, so a seeded run is reproducible and ids never repeat within a run. Headers travel as a map by way of the gene's own JSON printing, which is what knows which optional headers are on.Not supported yet, and said so with a one-time warning: a reply address announced inside the request (
reply.address.location) rather than fixed by the contract. Such an operation is published without waiting.Plumbing
RemoteController.executeNewAsyncApiActionAndGetReply, mirroring the RPC call. It has a default that throws, so the four other controllers (three of them test fakes) need not each say they cannot publish.EMConfig.asyncApiReplyTimeoutMs,@Experimental, default 5 s.--createTests falseis required for AsyncAPI until the test writer exists. Failing at start-up beats searching for an hour and failing at the end.TestCaseWriteris bound to the existingNoTestCaseWriter.AsyncApiModule, one for both modes, binding the driver unconditionally. It does not callsuper.configure(), like the RPC, GraphQL and Web modules: whatEnterpriseModulebinds there needsFitnessFunction<RestIndividual>.AsyncApiStructureMutator: one message more or one fewer. Unlike RPC's, it states its two bounds outright — a test keeps at least one message, and can grow to exactlymaxTestSize— because mirroring RPC's arithmetic emptied a one-message test whenmaxTestSizewas 2, and never let a test reach the maximum.Main'sASYNCAPIbranch binds the module instead of throwing.Testing
Three suites build the injector
Mainwould, with the driver replaced by a fake, through one sharedAsyncApiTestInjector.AsyncApiReplyClassifierTest(16): result vs error behind$refs, undeclared payloads, non-JSON,3.0is an integer, most-specific-wins, type lists; and, on a document written for the purpose,constthrough a chain of references,enum,oneOf/allOf/anyOf, array items, booleans vs their spelling, an unknown type name, and a reference into the middle of a schema being given the benefit of the doubt.AsyncApiBlackBoxFitnessTest(13): every DTO field the driver is told; unique correlation ids across evaluations; result and error as distinct targets; undeclared reply and silence as the two faults; fire-and-forget covered byPUBLISHED; headers as a map without the stamped one, structured headers as JSON; a correlation id located in the payload; a reply address announced at run time being published without waiting; a driver that could not publish, with and without saying why; a reply without its correlation id recorded rather than judged.AsyncApiStructureMutatorTest(4): the two bounds above, the maximum being reachable, added messages coming from the document, nothing happening when only one message is allowed. The first fails against RPC's arithmetic.AsyncApiModuleTest(1): a whole MIO search, 100 action evaluations, black-box, through the moduleMainbinds, against a stand-in that answers like the NCS service. Asserts that every operation was answered, that some operation reached both its declared replies, that nothing was a fault, and that no correlation id repeated.All AsyncAPI suites plus the config ones: 193 tests, 0 failures. jacoco over the AsyncAPI package: every new class above 90% line coverage.