Skip to content

Arazzo Sampler V1 - #1682

Open
daniellopera15 wants to merge 19 commits into
masterfrom
feature/arazzo-sampler
Open

Arazzo Sampler V1 #1682
daniellopera15 wants to merge 19 commits into
masterfrom
feature/arazzo-sampler

Conversation

@daniellopera15

Copy link
Copy Markdown
Collaborator

No description provided.

import org.evomaster.core.search.service.mutator.StandardMutator
import org.evomaster.core.search.service.mutator.StructureMutator

class ArazzoRestModule(private val bindRemote: Boolean = true) : RestBaseModule() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add @JavaDoc of this class

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After a more careful consideration, this class should be deleted.

import org.evomaster.core.search.service.mutator.MutatedGeneSpecification
import org.evomaster.core.sql.SqlInsertBuilder

class ArazzoStructureMutator : ApiWsStructureMutator() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if we need a specific mutator for Arazzo? I think we need to discuss this with Andrea

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can discuss it. However, the MIO algorithm needs to use the mutator. We could use an existing mutator, but that might break the workflow logic.


import com.google.inject.Inject
import com.webfuzzing.arazzo.access.ArazzoAccess
import com.webfuzzing.arazzo.models.domain.Workflow

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in a future PR, rename Workflow to ArazzoWorkflow

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add as a //TODO to rename it to ArazzoWorkflow

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

lateinit var skippedEndpoints : List<Endpoint>
private set

var workflowsArazzo = mutableListOf<Workflow>()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename field to arazzoWorkflows

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

var workflowsArazzo = mutableListOf<Workflow>()
private set

lateinit var workflowsArazzoById: Map<String, Workflow>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename field to to arazzoWorkflowsById

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return listOfNotNull(findActionForOperation(step.operationId))
}
if (!step.workflowId.isNullOrBlank()) {
val nested = workflowsArazzoById[step.workflowId] ?: return emptyList()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but it is expected for the workflowId to be missing? Shouldn't this signal an exception?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is better for it to throw an exception. However, the ideal approach would be for the ArazzoParser to perform the validations and guarantee a valid Arazzo document.

/**
* Every Step Arazzo has its corresponding RestCallAction in the actionCluster
*/
private fun findActionForOperation(operationId: String): RestCallAction? {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can operationId be null? If not, please add a Objects.requireNonNull()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am adding an exception.

val nested = workflowsArazzoById[step.workflowId] ?: return emptyList()
return nested.steps.flatMap { resolveStep(it) }
}
return emptyList()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about this recursive approach, could you change it to a iterative approach to build the RestCallActions?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Refactored to an iterative solution using ArrayDeque.

val template = actionCluster.values
.filterIsInstance<RestCallAction>()
.find { it.operationId == operationId }
return template?.copy() as? RestCallAction

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happen if no operationId is found? Is it expected?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am adding an exception.


enum class ArazzoStrategy {
NONE,
ENABLED

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ENABLED is not declarative enough. Please replace with a more declarative name and add a Javadoc to describe this strategy

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about calling it WORKFLOW?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniellopera15 was it modified to WORKFLOW? i still see ENABLED here

import javax.annotation.PostConstruct
import kotlin.sequences.forEach


Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes to this file if they are not needed (this seems refactoring such as formatting and unused imports)


enum class ArazzoStrategy {
NONE,
ENABLED

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Comment thread core/pom.xml Outdated
<dependency>
<groupId>org.evomaster</groupId>
<artifactId>arazzo-parser</artifactId>
<scope>compile</scope>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the compiler scope? The parser will be required in the distribution to be able to parse Arazzo specs within the evomaster.jar, right?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default scope for dependencies is compile. I'm going to remove it.

EMConfig.ProblemType.REST -> {
if (config.blackBox) {
if (config.isEnabledArazzoStrategy()) {
ArazzoRestModule()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure that an ArazzoRestModule is required. I think Arazzo sampling should be possible to enable in BlackBoxRestModule, ResourceRestModule and RestModule. The Arazzo sampling looks more like a sampling strategy.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s a possibility. I’m going to look into making a commit that includes the Sampler but not the Module. That way, we’d avoid the Mutator issue.

import org.evomaster.core.search.service.mutator.StandardMutator
import org.evomaster.core.search.service.mutator.StructureMutator

class ArazzoRestModule(private val bindRemote: Boolean = true) : RestBaseModule() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After a more careful consideration, this class should be deleted.

@jgaleotti
jgaleotti requested a review from arcuri82 September 7, 2026 14:59

enum class ArazzoStrategy {
NONE,
ENABLED

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daniellopera15 was it modified to WORKFLOW? i still see ENABLED here

/**
* Enum that enables and disables the generation of individuals based on Arazzo Workflows.
*/
enum class ArazzoStrategy {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it is only two option on/off, why is this an enum and not a boolean?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or are there already plans to add more in future? in that case, leave a TODO comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. At the moment, there are no plans to add more in the future. We're going to switch it to a boolean.


@Experimental
@Cfg("Probability of controlling the creation of Arazzo individuals.")
@Probability

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess that should add activating=false, as controlled by arazzoStrategy.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done



@Experimental
@Cfg("arazzo location in disk")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arazzo path location on disk

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


@Experimental
@Cfg("arazzo location in disk")
var arazzoLocation: String? = null

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be ="", with ExistingPath(val canBeBlank: Boolean, val shouldBeWritable: Boolean) having true,false

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as overlay option

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

*/
fun load(openAPI: OpenAPI, location: String?) {
if (location.isNullOrBlank()) {
throw ConfigProblemException("arazzoLocation must not be null when Arazzo strategy is enabled")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arazzon strategy will be on by default in future, with location empty by default

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

}
val workflows = readArazzoWorkflows(openAPI, location)
if (workflows.isEmpty()) {
throw ConfigProblemException("There must be at least one Arazzo workflow.")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this error message, add info on the location

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


private fun readArazzoWorkflows(openAPI: OpenAPI, location: String): List<Workflow> {
val arazzoText = ArazzoAccess.readFromDisk(location)
return ArazzoParser.parse(arazzoText, openAPI).workflows

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe the parse should be in a try/catch, throwing a ConfigProblemException if any error? eg, what happens if user specify wrong non-Arazzo file by mistake?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

* Choose a random workflow
*/
fun sampleAtRandom(
actionCluster: Map<String, Action>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this given as input, and not read directly from an injected field holding ref to the singleton sampler?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you talking about doing something like this?

@Inject
private lateinit var sampler: AbstractRestSampler

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. by the time sampleAtRandom is called, the singletons should all be already initialized

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

actionCluster: Map<String, Action>,
createIndividual: (SampleType, MutableList<RestCallAction>) -> RestIndividual,
): RestIndividual {
val actions = buildArazzoRestCallActions(workflow.steps, actionCluster)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should clarify here in a comment that, for the time being, we are only considering one single OpenAPI schema fro the workflow, and ignoring cases of arazzo spanning different APIs

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants