From fbc15abe60218352320ecba1b445de166bddacdc Mon Sep 17 00:00:00 2001 From: Lautaro Petaccio Date: Sat, 12 Sep 2026 19:16:11 -0300 Subject: [PATCH 1/4] AsyncAPI 3.x: the sampler AsyncApiSampler asks the driver where the document is, reads it, builds one action per publishable message, and samples sequences of them. The first individuals it hands out publish one message each, one per operation, so every operation is tried before the search starts combining them. One sampler for both modes. Where REST and GraphQL fetch the schema themselves in black-box mode, here the document comes through the driver either way, as the driver is what holds the connection to the broker. Seeded test cases are not supported yet, and say so. --- .../asyncapi/service/AsyncApiSampler.kt | 193 ++++++++++++++++++ .../asyncapi/service/AsyncApiSamplerTest.kt | 140 +++++++++++++ 2 files changed, 333 insertions(+) create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt create mode 100644 core/src/test/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSamplerTest.kt diff --git a/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt new file mode 100644 index 0000000000..d558bc4518 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt @@ -0,0 +1,193 @@ +package org.evomaster.core.problem.asyncapi.service + +import com.webfuzzing.asyncapi.access.AsyncApiAccess +import com.webfuzzing.asyncapi.models.AsyncApiDocument +import com.webfuzzing.asyncapi.parser.AsyncApiParsingException +import org.evomaster.client.java.controller.api.dto.SutInfoDto +import org.evomaster.client.java.controller.api.dto.problem.AsyncApiProblemDto +import org.evomaster.core.AnsiColor +import org.evomaster.core.logging.LoggingUtil +import org.evomaster.core.problem.api.service.ApiWsSampler +import org.evomaster.core.problem.asyncapi.builder.AsyncApiActionBuilder +import org.evomaster.core.problem.asyncapi.builder.AsyncApiGeneBuilder +import org.evomaster.core.problem.asyncapi.data.AsyncApiAction +import org.evomaster.core.problem.asyncapi.data.AsyncApiIndividual +import org.evomaster.core.problem.enterprise.SampleType +import org.evomaster.core.remote.SutProblemException +import org.evomaster.core.search.tracer.Traceable +import org.evomaster.core.search.warning.GeneralWarning +import org.evomaster.core.search.warning.WarningCategory +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import javax.annotation.PostConstruct + +/** + * Samples test cases for an AsyncAPI service: sequences of messages to publish, drawn from the + * operations the document declares. + * + * The same sampler serves white-box and black-box mode. The document always arrives through the + * driver, since the driver is what holds the connection to the broker (see + * [org.evomaster.core.EMConfig.usesDriver]). + */ +class AsyncApiSampler : ApiWsSampler() { + + companion object { + private val log: Logger = LoggerFactory.getLogger(AsyncApiSampler::class.java) + } + + /** + * One individual per action, each publishing that one message. Sampled before anything + * else, so that every operation is tried once before the search starts combining them. + */ + private val adHocInitialIndividuals: MutableList = mutableListOf() + + @PostConstruct + fun initialize() { + + log.debug("Initializing {}", AsyncApiSampler::class.simpleName) + + rc.checkConnection() + + val started = rc.startSUT() + if (!started) { + throw SutProblemException("Failed to start the system under test") + } + + val infoDto = rc.getSutInfo() + ?: throw SutProblemException("Failed to retrieve the info about the system under test") + + val problem = infoDto.asyncApiProblem + ?: throw SutProblemException("Missing problem definition object") + + val schema = readSchema(problem) + + val messages = AsyncApiActionBuilder.addActionsFromSchema( + schema, + actionCluster, + AsyncApiGeneBuilder.options(config) + ) + handleMessages(schema.warnings + messages) + + initSqlInfo(infoDto) + + initAdHocInitialIndividuals() + + if (config.seedTestCases) { + initSeededTests(infoDto) + } + + updateConfigBasedOnSutInfoDto(infoDto) + + log.debug("Done initializing {}", AsyncApiSampler::class.simpleName) + } + + /** + * The document, from wherever the driver said it is. The driver guarantees exactly one of + * the two is given. + */ + private fun readSchema(problem: AsyncApiProblemDto): AsyncApiDocument { + + try { + if (!problem.schemaLocation.isNullOrBlank()) { + return AsyncApiAccess.getAsyncApiFromLocation(problem.schemaLocation) + } + if (!problem.schemaText.isNullOrBlank()) { + return AsyncApiAccess.parseFromText(problem.schemaText) + } + } catch (e: AsyncApiParsingException) { + throw SutProblemException("Cannot read the AsyncAPI document: ${e.message}") + } + + throw SutProblemException("No info on the AsyncAPI document was provided") + } + + /** + * What the parser and the action builder had to skip. Reported to the user, and kept for + * the final report. + */ + private fun handleMessages(messages: List) { + + if (messages.isEmpty()) { + return + } + + LoggingUtil.getInfoLogger().warn( + AnsiColor.inRed( + "There are ${messages.size} detected issues when analyzing the AsyncAPI document." + + " These are not necessarily problems in the document, but possible (temporary)" + + " limitations of EvoMaster itself." + ) + ) + messages.forEachIndexed { index, s -> + LoggingUtil.getInfoLogger().warn(AnsiColor.inYellow("$index: $s")) + warningsAggregator.addWarning(GeneralWarning(WarningCategory.SCHEMA, s)) + } + } + + override fun sampleAtRandom(): AsyncApiIndividual { + + val n = randomness.nextInt(1, getMaxTestSizeDuringSampler()) + val actions = (0 until n).map { sampleRandomAction() }.toMutableList() + + return createIndividual(SampleType.RANDOM, actions) + } + + /** + * A copy of one of the action templates, chosen at random, with its genes initialized. + */ + fun sampleRandomAction(): AsyncApiAction { + + val action = randomness.choose(actionCluster).copy() as AsyncApiAction + action.doInitialize(randomness) + + return action + } + + override fun smartSample(): AsyncApiIndividual { + + if (adHocInitialIndividuals.isNotEmpty()) { + return adHocInitialIndividuals.removeAt(adHocInitialIndividuals.size - 1) + } + + return sampleAtRandom() + } + + override fun hasSpecialInitForSmartSampler(): Boolean { + return adHocInitialIndividuals.isNotEmpty() && config.isEnabledSmartSampling() + } + + override fun resetSpecialInit() { + initAdHocInitialIndividuals() + } + + override fun initSeededTests(infoDto: SutInfoDto?) { + throw IllegalStateException("Seeding test cases is not supported for AsyncAPI yet") + } + + private fun initAdHocInitialIndividuals() { + + adHocInitialIndividuals.clear() + + actionCluster.values.forEach { template -> + val action = template.copy() as AsyncApiAction + action.doInitialize(randomness) + adHocInitialIndividuals.add(createIndividual(SampleType.SMART, mutableListOf(action))) + } + } + + private fun createIndividual( + sampleType: SampleType, + actions: MutableList + ): AsyncApiIndividual { + + val individual = AsyncApiIndividual( + sampleType = sampleType, + actions = actions, + trackOperator = if (config.trackingEnabled()) this else null, + index = if (config.trackingEnabled()) time.evaluatedIndividuals else Traceable.DEFAULT_INDEX + ) + individual.doGlobalInitialize(searchGlobalState) + + return individual + } +} diff --git a/core/src/test/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSamplerTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSamplerTest.kt new file mode 100644 index 0000000000..57385965e6 --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSamplerTest.kt @@ -0,0 +1,140 @@ +package org.evomaster.core.problem.asyncapi.service + +import com.google.inject.AbstractModule +import com.google.inject.Injector +import com.netflix.governator.guice.LifecycleInjector +import com.webfuzzing.asyncapi.access.AsyncApiAccess +import org.evomaster.client.java.controller.api.dto.SutInfoDto +import org.evomaster.client.java.controller.api.dto.problem.AsyncApiProblemDto +import org.evomaster.core.BaseModule +import org.evomaster.core.EMConfig +import org.evomaster.core.problem.asyncapi.data.AsyncApiAction +import org.evomaster.core.problem.external.service.DummyController +import org.evomaster.core.remote.SutProblemException +import org.evomaster.core.remote.service.RemoteController +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Path + +class AsyncApiSamplerTest { + + companion object { + private const val NCS = "/asyncapi/sut/ncs-kafka.yaml" + + private val NCS_OPERATIONS = setOf("checkTriangle", "bessj", "expint", "fisher", "gammq", "remainder") + } + + /** + * A driver that only answers what the sampler asks at start-up: that the service is running, + * and where its AsyncAPI document is. + */ + private class FakeController(private val info: SutInfoDto) : RemoteController by DummyController() { + override fun checkConnection() {} + override fun startSUT() = true + override fun getSutInfo() = info + } + + private fun sutInfo(configure: AsyncApiProblemDto.() -> Unit) = SutInfoDto().apply { + asyncApiProblem = AsyncApiProblemDto().apply(configure) + defaultOutputFormat = SutInfoDto.OutputFormat.KOTLIN_JUNIT_5 + } + + private fun sampler(info: SutInfoDto, vararg options: String): AsyncApiSampler { + + val args = arrayOf("--seed=42", "--problemType=ASYNCAPI") + options + + val modules = listOf(BaseModule(args), object : AbstractModule() { + override fun configure() { + bind(RemoteController::class.java).toInstance(FakeController(info)) + bind(AsyncApiSampler::class.java).asEagerSingleton() + } + }) + + val injector: Injector = LifecycleInjector.builder().withModules(modules).build().createInjector() + + return injector.getInstance(AsyncApiSampler::class.java) + } + + private fun ncsSampler(vararg options: String) = + sampler(sutInfo { schemaText = AsyncApiAccess.readFromResource(NCS) }, *options) + + @Test + fun testOneActionPerOperationTheDriverDescribes() { + + val sampler = ncsSampler("--blackBox=false") + + assertEquals(NCS_OPERATIONS, sampler.seeAvailableActions().map { it.getName() }.toSet()) + } + + @Test + fun testTheSameDocumentServesBlackBoxMode() { + + /* + No black-box branch in the sampler: the document arrives through the driver either + way, since the driver is what holds the connection to the broker. + */ + val sampler = ncsSampler("--blackBox=true") + + assertEquals(NCS_OPERATIONS.size, sampler.numberOfDistinctActions()) + } + + @Test + fun testTheDocumentCanBeFetchedFromWhereTheDriverSaysItIs(@TempDir dir: Path) { + + val file = dir.resolve("asyncapi.yaml") + Files.write(file, AsyncApiAccess.readFromResource(NCS).toByteArray(StandardCharsets.UTF_8)) + + val sampler = sampler(sutInfo { schemaLocation = file.toString() }, "--blackBox=false") + + assertEquals(NCS_OPERATIONS.size, sampler.numberOfDistinctActions()) + } + + @Test + fun testARandomIndividualPublishesBetweenOneAndMaxTestSizeMessages() { + + val sampler = ncsSampler("--blackBox=false", "--maxTestSize=4") + + repeat(50) { + val individual = sampler.sample(forceRandomSample = true) + val actions = individual.seeMainExecutableActions() + + assertTrue(actions.size in 1..4, "got ${actions.size} messages") + assertTrue(actions.all { it is AsyncApiAction }) + assertTrue(actions.all { it.isInitialized() }, "an action was sampled with uninitialized genes") + assertTrue(individual.seeInitializingActions().isEmpty()) + } + } + + @Test + fun testEveryOperationIsTriedOnceBeforeMessagesAreCombined() { + + val sampler = ncsSampler("--blackBox=false", "--probOfSmartSampling=1.0") + + val first = (1..NCS_OPERATIONS.size).map { + assertTrue(sampler.hasSpecialInit(), "ran out of single-message individuals early") + sampler.sample() + } + + //six individuals of one message each, covering the six operations + assertTrue(first.all { it.seeMainExecutableActions().size == 1 }) + assertEquals(NCS_OPERATIONS, first.map { it.seeMainExecutableActions().single().getName() }.toSet()) + + assertFalse(sampler.hasSpecialInit()) + } + + @Test + fun testADocumentThatIsNotThereIsAProblemWithTheSut(@TempDir dir: Path) { + + //the likeliest mistake in a driver: a path that is right on the author's machine only + val e = assertThrows(Throwable::class.java) { + sampler(sutInfo { schemaLocation = dir.resolve("absent.yaml").toString() }, "--blackBox=false") + } + + //Guice and Governator wrap whatever the sampler throws while it is being created + val causes = generateSequence(e) { it.cause }.toList() + assertTrue(causes.any { it is SutProblemException }, causes.joinToString { it.toString() }) + } +} From e912ff135e030b1d2c5c68f17da2d1991c296914 Mon Sep 17 00:00:00 2001 From: Lautaro Petaccio Date: Sat, 12 Sep 2026 20:04:35 -0300 Subject: [PATCH 2/4] AsyncAPI 3.x: the sampler's other paths under test Review: what the parser skips was reported to the user and kept for the final report, but nothing checked it; nor that the single-message individuals return after a reset, nor the two other ways start-up can fail. Four tests, and the helper that builds the injector is shared. --- .../asyncapi/service/AsyncApiSamplerTest.kt | 105 ++++++++++++++++-- 1 file changed, 96 insertions(+), 9 deletions(-) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSamplerTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSamplerTest.kt index 57385965e6..6c225e8105 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSamplerTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSamplerTest.kt @@ -12,6 +12,8 @@ import org.evomaster.core.problem.asyncapi.data.AsyncApiAction import org.evomaster.core.problem.external.service.DummyController import org.evomaster.core.remote.SutProblemException import org.evomaster.core.remote.service.RemoteController +import org.evomaster.core.search.service.WarningsAggregator +import org.evomaster.core.search.warning.WarningCategory import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir @@ -31,9 +33,12 @@ class AsyncApiSamplerTest { * A driver that only answers what the sampler asks at start-up: that the service is running, * and where its AsyncAPI document is. */ - private class FakeController(private val info: SutInfoDto) : RemoteController by DummyController() { + private class FakeController( + private val info: SutInfoDto, + private val starts: Boolean = true + ) : RemoteController by DummyController() { override fun checkConnection() {} - override fun startSUT() = true + override fun startSUT() = starts override fun getSutInfo() = info } @@ -42,20 +47,30 @@ class AsyncApiSamplerTest { defaultOutputFormat = SutInfoDto.OutputFormat.KOTLIN_JUNIT_5 } - private fun sampler(info: SutInfoDto, vararg options: String): AsyncApiSampler { + private fun injector(info: SutInfoDto, starts: Boolean = true, vararg options: String): Injector { val args = arrayOf("--seed=42", "--problemType=ASYNCAPI") + options val modules = listOf(BaseModule(args), object : AbstractModule() { override fun configure() { - bind(RemoteController::class.java).toInstance(FakeController(info)) + bind(RemoteController::class.java).toInstance(FakeController(info, starts)) bind(AsyncApiSampler::class.java).asEagerSingleton() } }) - val injector: Injector = LifecycleInjector.builder().withModules(modules).build().createInjector() + return LifecycleInjector.builder().withModules(modules).build().createInjector() + } + + private fun sampler(info: SutInfoDto, vararg options: String): AsyncApiSampler = + injector(info, true, *options).getInstance(AsyncApiSampler::class.java) - return injector.getInstance(AsyncApiSampler::class.java) + /** + * Whatever the sampler threw while being created, unwrapped from what Guice and Governator + * wrap it in. + */ + private fun causesOfFailingToCreate(block: () -> Unit): List { + val e = assertThrows(Throwable::class.java) { block() } + return generateSequence(e) { it.cause }.toList() } private fun ncsSampler(vararg options: String) = @@ -129,12 +144,84 @@ class AsyncApiSamplerTest { fun testADocumentThatIsNotThereIsAProblemWithTheSut(@TempDir dir: Path) { //the likeliest mistake in a driver: a path that is right on the author's machine only - val e = assertThrows(Throwable::class.java) { + val causes = causesOfFailingToCreate { sampler(sutInfo { schemaLocation = dir.resolve("absent.yaml").toString() }, "--blackBox=false") } - //Guice and Governator wrap whatever the sampler throws while it is being created - val causes = generateSequence(e) { it.cause }.toList() assertTrue(causes.any { it is SutProblemException }, causes.joinToString { it.toString() }) } + + @Test + fun testAServiceThatDoesNotStartIsAProblemWithTheSut() { + + val causes = causesOfFailingToCreate { + injector(sutInfo { schemaText = AsyncApiAccess.readFromResource(NCS) }, starts = false, "--blackBox=false") + } + + assertTrue(causes.any { it is SutProblemException && it.message!!.contains("start") }, causes.joinToString { it.toString() }) + } + + @Test + fun testADriverThatDeclaresNoAsyncApiServiceIsAProblemWithTheSut() { + + val causes = causesOfFailingToCreate { + sampler(SutInfoDto(), "--blackBox=false") + } + + assertTrue(causes.any { it is SutProblemException && it.message!!.contains("problem definition") }, causes.joinToString { it.toString() }) + } + + @Test + fun testWhatTheParserHadToSkipReachesTheUser() { + + //an operation whose reply names a channel the document never declares + val document = """ + asyncapi: 3.0.0 + info: + title: Skips + version: 1.0.0 + channels: + requests: + address: app.requests + messages: + request: + payload: + type: object + operations: + ask: + action: receive + channel: + ${'$'}ref: '#/channels/requests' + reply: + channel: + ${'$'}ref: '#/channels/nowhere' + """.trimIndent() + + val injector = injector(sutInfo { schemaText = document }, true, "--blackBox=false") + + //the operation is still there: one message that could not be resolved does not cost the document + assertEquals(1, injector.getInstance(AsyncApiSampler::class.java).numberOfDistinctActions()) + + //and what was skipped is on record for the final report + val warnings = injector.getInstance(WarningsAggregator::class.java).getWarnings() + assertTrue( + warnings.any { it.category == WarningCategory.SCHEMA && it.message.contains("nowhere") }, + warnings.joinToString { it.message } + ) + } + + @Test + fun testTheSingleMessageIndividualsComeBackAfterAReset() { + + val sampler = ncsSampler("--blackBox=false", "--probOfSmartSampling=1.0") + + repeat(NCS_OPERATIONS.size) { sampler.sample() } + assertFalse(sampler.hasSpecialInit()) + + sampler.resetSpecialInit() + + assertTrue(sampler.hasSpecialInit()) + val again = (1..NCS_OPERATIONS.size).map { sampler.sample() } + assertEquals(NCS_OPERATIONS, again.map { it.seeMainExecutableActions().single().getName() }.toSet()) + } } From 763d8c80b6aa2804e15ce05f4d1f746800dc2933 Mon Sep 17 00:00:00 2001 From: Lautaro Petaccio Date: Sun, 13 Sep 2026 17:24:56 -0300 Subject: [PATCH 3/4] AsyncAPI 3.x: a line on each of the sampler's methods --- .../asyncapi/service/AsyncApiSampler.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt index d558bc4518..f85e03bea0 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt @@ -41,6 +41,10 @@ class AsyncApiSampler : ApiWsSampler() { */ private val adHocInitialIndividuals: MutableList = mutableListOf() + /** + * Start the service through the driver, read its document, and build one action per + * publishable message. Anything the parser or the builder had to skip is reported. + */ @PostConstruct fun initialize() { @@ -124,6 +128,9 @@ class AsyncApiSampler : ApiWsSampler() { } } + /** + * A test of one to `maxTestSize` messages, each a random action with fresh genes. + */ override fun sampleAtRandom(): AsyncApiIndividual { val n = randomness.nextInt(1, getMaxTestSizeDuringSampler()) @@ -143,6 +150,9 @@ class AsyncApiSampler : ApiWsSampler() { return action } + /** + * The next single-message individual while any are left, then a random test. + */ override fun smartSample(): AsyncApiIndividual { if (adHocInitialIndividuals.isNotEmpty()) { @@ -152,18 +162,30 @@ class AsyncApiSampler : ApiWsSampler() { return sampleAtRandom() } + /** + * Whether single-message individuals are still waiting to be handed out. + */ override fun hasSpecialInitForSmartSampler(): Boolean { return adHocInitialIndividuals.isNotEmpty() && config.isEnabledSmartSampling() } + /** + * Prepare the single-message individuals again, so every operation is tried once more. + */ override fun resetSpecialInit() { initAdHocInitialIndividuals() } + /** + * Not supported yet: there is no format to read AsyncAPI test cases from. + */ override fun initSeededTests(infoDto: SutInfoDto?) { throw IllegalStateException("Seeding test cases is not supported for AsyncAPI yet") } + /** + * One individual per action, publishing that one message. + */ private fun initAdHocInitialIndividuals() { adHocInitialIndividuals.clear() @@ -175,6 +197,13 @@ class AsyncApiSampler : ApiWsSampler() { } } + /** + * Wrap [actions] as an individual ready for the search: tracked if tracking is on, and + * with its global state and local ids set. + * + * @param sampleType how the individual came to be, which the structure mutators read to + * choose how to change it + */ private fun createIndividual( sampleType: SampleType, actions: MutableList From 8ab6d9d4cb23bd8c83a6686a1fc13a0ea2767b79 Mon Sep 17 00:00:00 2001 From: Lautaro Petaccio Date: Sun, 13 Sep 2026 17:52:31 -0300 Subject: [PATCH 4/4] AsyncAPI 3.x: say what the sampler leaves out Four TODOs, where someone will next look: no AsyncAPI-specific smart strategy yet, message examples parsed but unused, no seeding format, and a method shared verbatim with the REST sampler. --- .../problem/asyncapi/service/AsyncApiSampler.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt index f85e03bea0..bbfea75981 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/asyncapi/service/AsyncApiSampler.kt @@ -105,6 +105,10 @@ class AsyncApiSampler : ApiWsSampler() { throw SutProblemException("No info on the AsyncAPI document was provided") } + /* + TODO Line for line the same as AbstractRestSampler.handleMessages. Once a third sampler + needs it, hoist it to EnterpriseSampler. + */ /** * What the parser and the action builder had to skip. Reported to the user, and kept for * the final report. @@ -139,6 +143,11 @@ class AsyncApiSampler : ApiWsSampler() { return createIndividual(SampleType.RANDOM, actions) } + /* + TODO Message examples (AsyncApiMessage.getExamples) are parsed and never read. Sampling + from them some of the time, as REST does with probRestExamples, would start the search + from payloads the author knows the service accepts. + */ /** * A copy of one of the action templates, chosen at random, with its genes initialized. */ @@ -150,6 +159,11 @@ class AsyncApiSampler : ApiWsSampler() { return action } + /* + TODO No AsyncAPI-specific strategy yet, only the single-message individuals and then + random tests. The one that matters is chaining: a message whose payload needs an id + that only the reply to a previous message produces. + */ /** * The next single-message individual while any are left, then a random test. */ @@ -180,6 +194,7 @@ class AsyncApiSampler : ApiWsSampler() { * Not supported yet: there is no format to read AsyncAPI test cases from. */ override fun initSeededTests(infoDto: SutInfoDto?) { + //TODO There is no format to read message-based test cases from yet, as Postman is for REST throw IllegalStateException("Seeding test cases is not supported for AsyncAPI yet") }