# The Verb Layer The verb layer provides optional, domain-specific helpers for recurring test actions. It uses the same scenario builder, workloads, expectations, and capabilities described in the preceding chapters. --- ## Two Equivalent Levels The explicit API names the objects being assembled: ```rust,ignore let scenario = QueueScenarioBuilder::with_deployment(QueueTopology::new(5)) .with_node_control() .with_network_control() .with_workload(QueueProduceWorkload::new().operations(400).rate_per_sec(40)) .with_workload(RandomRestartWorkload::new( Duration::from_secs(5), Duration::from_secs(15), Duration::from_secs(15), )) .with_workload(NetworkPartitionWorkload::new( NetworkPartitionSpec::new([ vec!["node-0", "node-1"], vec!["node-2", "node-3", "node-4"], ]), Duration::from_secs(20), Duration::from_secs(60), )) .with_expectation(QueueConverges::new(400).timeout(Duration::from_secs(60))) .with_run_duration(Duration::from_secs(120)) .build()?; ``` The verb API lowers to those same operations: ```rust,ignore QueueScenario::nodes(5) .produce(400).rate_per_sec(40).done() .restart_nodes_randomly().every_secs(5, 15).done() .partition(["node-0", "node-1"], ["node-2", "node-3", "node-4"]) .hold_secs(20).done() .expect_converged(400).within_secs(60) .run_secs(120) .await?; ``` The explicit API remains available at every point. Use it for one-off workloads, unusual policies, or operations that do not have a domain verb. --- ## How Verbs Map to the Builder A verb does not introduce a second runtime. Its sub-builder stores an ordinary workload or expectation and adds it when `done()` or a terminal method is called: ```rust,ignore pub trait QueueDslExt: CoreBuilderAccess + Sized { fn produce(self, operations: usize) -> QueueProduceBuilder { QueueProduceBuilder { builder: self, workload: QueueProduceWorkload::new().operations(operations), } } } impl> QueueProduceBuilder { pub fn done(self) -> B { self.builder.map_core_builder(|builder| { builder.with_workload(self.workload) }) } } ``` Both forms therefore use the same execution, failure aggregation, and teardown. Generic and application-specific verbs can extend the same builder chain. --- ## Capability-Aware Verbs Some actions require a runtime capability. The verb should request it when the requirement follows directly from the action: - `restart_nodes_randomly()` transitions a plain builder to a node-control builder. - `partition(...).done()` requests network control before adding the partition workload. - A data-plane verb such as `produce(...)` needs no control capability. The resulting Rust type records the capability transition. If a deployer cannot supply the requested capability, deployment fails before the workload starts. Do not hide an unrelated policy choice inside a verb. A verb may request what its action necessarily needs; retry policy, cleanup policy, backend selection, and other test-wide decisions remain explicit. --- ## Designing Application Verbs Put vocabulary shared by applications in the framework and vocabulary specific to one protocol in that application's testing crate. A verb should: 1. names an operation in the application's domain; 2. configures one workload or expectation, or a small fixed combination; 3. exposes meaningful options through a short sub-builder; 4. returns the underlying builder through `done()` or a clear terminal method; 5. preserves access to `with_workload` and `with_expectation` for uncommon cases. The queue example keeps `partition` and random restarts generic, while `produce` and `expect_converged` live with the queue integration. Other applications can then reuse chaos behavior without depending on queue terminology. --- ## See Also - [Workloads and Concurrency](workloads.md) and [Expectations and Evaluation](expectations.md): the objects verbs add. - [Scenario Capabilities](capabilities.md): the requirements capability-aware verbs request. - [Chaos and Controlled Failure](chaos.md): the generic restart and partition workloads.