| Crates.io | greentic-conformance |
| lib.rs | greentic-conformance |
| version | 0.1.0 |
| created_at | 2025-10-30 19:16:16.294573+00 |
| updated_at | 2025-10-30 19:16:16.294573+00 |
| description | Reusable conformance suites for Greentic packs, flows, runners, and components. |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1908831 |
| size | 66,479 |
Reusable conformance harness for Greentic packs, flows, runners, and components. The crate exposes ergonomic helpers that downstream projects can import to standardise validation logic while keeping the actual runtime integrations local to the project under test.
.ygtc) for structural integrity and schema metadata.All suites return structured reports so higher-level tests can apply additional assertions or emit richer diagnostics.
Add the crate to your workspace:
[dev-dependencies]
greentic-conformance = { path = "../greentic-conformance" }
Then exercise whichever suites you need from your integration tests:
use greentic_conformance::{validate_flow_folder, verify_pack_exports, PackSuiteOptions};
#[test]
fn pack_exports_are_well_formed() {
let report = PackSuiteOptions::default()
.with_runtime_adapter(|path| {
// Swap this closure with a wasmtime-based adapter that calls list_flows.
println!("Interrogating component: {}", path.display());
Ok(vec!["example.flow".to_string()])
})
.verify_pack_exports("./packs/example/component.wasm")
.unwrap();
assert_eq!(report.runtime_flows.unwrap(), vec!["example.flow"]);
}
#[test]
fn flows_follow_schema() {
let validation = validate_flow_folder("./flows").unwrap();
assert_eq!(validation.flows.len(), 3);
}
#[test]
fn flows_with_custom_rules() {
use anyhow::anyhow;
use greentic_conformance::FlowValidationOptions;
let options = FlowValidationOptions::default()
.allow_extension("flow")
.allow_missing_schema()
.add_validator(|flow| {
if flow.nodes.iter().any(|node| node.kind == "end") {
Ok(())
} else {
Err(anyhow!("flow {} is missing an end node", flow.id))
}
});
let validation = options.validate_flow_folder("./flows").unwrap();
assert!(!validation.flows.is_empty());
}
#[test]
fn runner_smoke_test() {
use greentic_conformance::{RunnerExpectation, RunnerOptions};
use serde_json::json;
let report = RunnerOptions::default()
.add_arg("--mode")
.add_arg("test")
.with_expectation(
RunnerExpectation::success()
.require_json_stdout()
.with_expected_egress(json!({ "disable_network": "1" })),
)
.smoke_run_with_mocks("./target/debug/greentic-runner", "./packs/example")
.unwrap();
assert_eq!(report.snapshot.status, 0);
}
#[test]
fn component_roundtrip() {
use greentic_conformance::ComponentInvocationOptions;
let invocation = ComponentInvocationOptions::default()
.invoke_generic_component(
"./target/debug/example-component",
"echo",
r#"{ "message": "hello" }"#,
)
.unwrap();
assert_eq!(invocation.output_json.unwrap()["message"], "hello");
}
The suites never enable network access implicitly. To opt-in for online checks (for example when a runner needs to hit remote connectors) set GREENTIC_ENABLE_ONLINE=1 in your environment. Downstream harnesses can read this flag to decide whether to execute or skip online-dependent assertions.
PackRuntimeAdapter (for example a wasmtime loader) via PackSuiteOptions::runtime_adapter. When provided, it will call the adapter’s list_flows hook and ensure the runtime view matches the manifest.FlowValidationOptions so you can add file extensions, relax schema requirements, or register custom validators before calling validate_flow_folder..add_arg, .add_env, and .with_expectation on RunnerOptions to tailor each smoke test..allow_non_json_output() on ComponentInvocationOptions if your component returns another format.cargo fmt – style the codebasecargo clippy -- -D warnings – lint the cratecargo test – run the local test suite (no online access)CI enforces all three commands. Online checks can be enabled via repository or environment configuration (see .github/workflows/ci.yml).
Licensed under the MIT License. Contributions are welcome under the same terms.