mod error;
mod metadata;
mod provider_permission;
mod registry;
mod types;
pub use error::RuntimeEffectError;
pub(crate) use metadata::effect_verification_refs;
pub use metadata::{EFFECT_VERIFICATION_REFS_METADATA, insert_effect_verification_ref};
pub use provider_permission::{
PROVIDER_PERMISSION_EFFECT_FAMILY, PROVIDER_PERMISSION_GRANT_ID_ENV,
PROVIDER_PERMISSION_GRANTED_SCOPES_ENV, ProviderPermissionAdmission, ProviderPermissionEffect,
};
pub use registry::RuntimeEffectRegistry;
pub use types::{
EffectAdmission, EffectMetadataRefreshRequest, EffectOutputRequest, EffectReceiptRequest,
EffectReplay, EffectReplayOutputRequest, EffectReplayReceiptRequest, EffectStepRequest,
RuntimeEffect,
};
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use std::path::Path;
use runx_contracts::{AuthorityVerb, JsonObject, Reference};
use runx_core::state_machine::AuthorityAdmissionWitness;
use runx_parser::GraphStep;
use super::*;
use crate::adapter::{InvocationStatus, SkillOutput};
struct MockEffect;
impl RuntimeEffect for MockEffect {
fn family(&self) -> &'static str {
"deploy"
}
fn admit(
&self,
request: EffectStepRequest<'_>,
) -> Result<Option<EffectAdmission>, RuntimeEffectError> {
let _ = request;
Ok(Some(EffectAdmission::new(
"deploy",
AuthorityVerb::Write,
AuthorityAdmissionWitness {
verb: AuthorityVerb::Write,
parent_term_id: "parent".to_owned(),
child_term_id: "child".to_owned(),
idempotency_key: Some("deploy-key".to_owned()),
capability_ref: None,
},
(),
)))
}
}
#[test]
fn registry_dispatches_effect_family() {
let registry = RuntimeEffectRegistry::with_effect(MockEffect);
let step = test_step();
let inputs = JsonObject::new();
let env = BTreeMap::new();
let result = registry.admit(EffectStepRequest {
step: &step,
inputs: &inputs,
env: &env,
graph_dir: Path::new("."),
});
assert!(
matches!(
&result,
Ok(Some(admission))
if admission.family() == "deploy" && admission.verb() == AuthorityVerb::Write
),
"unexpected admission result: {result:?}"
);
}
#[test]
fn registry_rejects_missing_effect_family_after_admission() {
let registry = RuntimeEffectRegistry::empty();
let step = test_step();
let admission = EffectAdmission::new(
"absent",
AuthorityVerb::Write,
AuthorityAdmissionWitness {
verb: AuthorityVerb::Write,
parent_term_id: "parent".to_owned(),
child_term_id: "child".to_owned(),
idempotency_key: None,
capability_ref: None,
},
(),
);
let claim = JsonObject::new();
let mut output = SkillOutput {
status: InvocationStatus::Success,
stdout: String::new(),
stderr: String::new(),
exit_code: Some(0),
duration_ms: 0,
metadata: JsonObject::new(),
};
let result = registry.prepare_output(EffectOutputRequest {
step: &step,
admission: &admission,
claim: &claim,
output: &mut output,
});
assert!(
matches!(result, Err(RuntimeEffectError::MissingFamily { ref family }) if family == "absent"),
"unexpected missing-family result: {result:?}"
);
}
#[test]
fn verification_refs_round_trip_through_metadata() {
let mut metadata = JsonObject::new();
let reference = Reference::runx(runx_contracts::ReferenceType::Verification, "proof:1");
let insert = insert_effect_verification_ref(&mut metadata, reference.clone());
assert!(insert.is_ok(), "unexpected insert result: {insert:?}");
assert_eq!(effect_verification_refs(&metadata), Ok(vec![reference]));
}
fn test_step() -> GraphStep {
GraphStep {
id: "ship".to_owned(),
label: None,
skill: None,
tool: None,
run: None,
instructions: None,
artifacts: None,
runner: None,
inputs: JsonObject::new(),
context: BTreeMap::new(),
context_edges: Vec::new(),
context_skills: Vec::new(),
scopes: Vec::new(),
allowed_tools: None,
retry: None,
policy: None,
fanout_group: None,
when: None,
mutating: false,
idempotency_key: None,
mint_authority: None,
requested_scope_from: None,
}
}
}