6.8 KiB
Application, AppDeployment, and Environments
This chapter distinguishes the Application trait, the AppDeployment trait, and the concrete environment types that implement Application.
The Application Trait
Application is the contract between the scenario engine and whatever system you are testing. It bundles the backend-specific types the engine needs, without the engine ever knowing what your application does:
use testing_framework_core::scenario::Application;
pub trait Application: Send + Sync + 'static {
type Deployment: DeploymentDescriptor + Clone + 'static;
type NodeClient: Clone + Send + Sync + 'static;
type NodeConfig: Clone + Send + Sync + 'static;
fn external_node_client(source: &ExternalNodeSource) -> Result<Self::NodeClient, DynError>;
fn build_node_client(access: &NodeAccess) -> Result<Self::NodeClient, DynError>;
fn node_readiness_path() -> &'static str; // default: "/"
}
The associated types and methods are:
Deployment: the topology descriptor, i.e. how many nodes exist and how they relate.NodeClient: the typed client workloads use to talk to one node.NodeConfig: the per-node configuration your binary consumes.- Client constructors:
build_node_clientturns deployer-providedNodeAccessinto a client;external_node_clientdoes the same for nodes the framework did not start. Both return an "unsupported" error by default. An environment must implement the operations it supports (Ownership and Design Boundaries). node_readiness_path: the HTTP path deployers probe during readiness gating.
An implementation of Application is called an environment. Everything generic in the framework (ScenarioBuilder<E>, Workload<E>, Expectation<E>, RunContext<E>) is parameterized over one.
Source: testing-framework/core/src/env.rs.
The AppDeployment Trait
Application describes a uniform node population. A system containing a cluster plus another process, or several different clusters, is represented through AppDeployment in testing-framework-app:
use testing_framework_app::{AppDeployment, AppHandle, DeployContext};
pub trait AppDeployment<E: Application, P = LocalClusterProvisioner>: Send + 'static {
type Handle: AppHandle;
async fn deploy(self, ctx: &mut DeployContext<E, P>) -> Result<Self::Handle, DynError>;
}
An AppDeployment is a deployable unit: it consumes its description, prepares whatever it represents, and returns a typed runtime handle. AppHandle is a blanket implementation, so any Clone + Send + Sync + 'static type qualifies. The handle provides access and control; managed resources acquired through framework adapters are owned separately by scenario cleanup.
Deployments compose: inside deploy, the DeployContext lets a parent deployment call ctx.deploy(child) or ctx.deploy_and_expose(child), then ctx.expose(handle) to publish typed handles to workloads. See AppDeployment and DeployContext for the full context API.
An AppDeployment registered with .with_app(...) runs during scenario preparation. It participates in the scenario lifecycle; it does not replace that lifecycle.
Source: testing-framework/app/src/deployment.rs.
Concrete Environments
AppHostEnv: an environment without outer nodes
AppHostEnv is an environment with no outer nodes at all. Its topology, AppHostTopology, reports a node count of zero; its NodeClient and NodeConfig are both (); asking it for a node client is an error. It exists so that a scenario can be composed entirely from application deployments:
use testing_framework_app::{AppHost, AppScenarioBuilderExt};
let builder = AppHost::scenario() // ScenarioBuilder<AppHostEnv>, zero nodes
.with_app(KvLocalApp::nodes(3)); // apps provide all processes
The system is supplied by with_app deployments, and workloads access it through typed handles instead of outer node clients. See AppHost and with_app.
Source: testing-framework/app/src/host.rs.
KvEnv: a uniform node environment
The kvstore example shows a full environment for a real binary:
pub struct KvEnv;
impl Application for KvEnv {
type Deployment = KvTopology; // ClusterTopology
type NodeClient = KvHttpClient;
type NodeConfig = KvNodeConfig;
fn build_node_client(access: &NodeAccess) -> Result<Self::NodeClient, DynError> {
Ok(KvHttpClient::new(access.api_base_url()?))
}
fn node_readiness_path() -> &'static str {
"/health/ready"
}
}
KvEnv additionally implements LocalBinaryApp (in examples/kvstore/testing/integration/src/local_env.rs) to tell the local deployer which binary to run, how to render per-node configs, and which port serves the HTTP API. The same environment type also backs AppDeployment presets like KvLocalApp, whose handle is a whole child cluster. Implementing Application walks through this in detail.
Source: examples/kvstore/testing/integration/src/app.rs.
How the Three Relate
graph TD
SB["ScenarioBuilder<E>"] -->|"E: Application"| APP["Application<br/>(env contract)"]
KV["KvEnv"] -.->|implements| APP
AH["AppHostEnv<br/>(zero-node env)"] -.->|implements| APP
AD["AppDeployment<E, P>"] -->|"deploys via"| DC["DeployContext<E, P>"]
AD -->|returns| H["typed Handle"]
SB -->|".with_app(...)"| AD
SB:::sc
H:::hd
classDef sc stroke:#9b6dd6,stroke-width:2.5px;
classDef hd stroke:#4caf7d,stroke-width:2.5px;
| Role | What it is | What it answers | Example |
|---|---|---|---|
Application |
Trait bundling Deployment, NodeClient, NodeConfig for the scenario engine |
"What types does the engine plumb around?" | KvEnv |
AppDeployment |
Trait for one deployable unit returning a typed handle | "How is this piece prepared, and what can test code access?" | KvLocalApp |
| Concrete environment | A type implementing Application |
"Which system am I testing, uniform or zero-node?" | KvEnv, AppHostEnv |
Application and AppDeployment are not alternatives. Every scenario has one environment type E, and app deployments are registered inside that scenario. A uniform kvstore cluster uses KvEnv directly; a heterogeneous stack uses AppHostEnv and supplies its components as app deployments.
Where to Go Next
- Scenario Model and Lifecycle: what a scenario is and how it runs.
- Choosing an Entry Pattern: which combination of these pieces fits your system.
- Part II — Composing Applications: the app layer in depth.
- Part IV — Uniform Clusters and Configuration: implementing an environment for your own binary.