#![allow(dead_code)]
use effect_runtime::error::EffectRuntimeValidationError;
use effect_runtime::vocab::*;
use effect_runtime::*;
use proptest::prelude::*;
use stack_ids::{EffectIntentId, EffectWindowId};
fn arb_commit_atomicity() -> impl Strategy<Value = CommitAtomicityV1> {
prop_oneof![
Just(CommitAtomicityV1::BoundedMultiStep),
Just(CommitAtomicityV1::ExactlyOnce),
Just(CommitAtomicityV1::AtLeastOnce),
Just(CommitAtomicityV1::BestEffort),
]
}
fn arb_retry_owner() -> impl Strategy<Value = RetryOwnerV1> {
prop_oneof![
Just(RetryOwnerV1::EffectRuntime),
Just(RetryOwnerV1::ForgeOrchestration),
Just(RetryOwnerV1::Verification),
Just(RetryOwnerV1::Provider),
Just(RetryOwnerV1::Operator),
]
}
fn arb_close_midflight() -> impl Strategy<Value = CloseMidflightBehaviorV1> {
prop_oneof![
Just(CloseMidflightBehaviorV1::AbortAndEmitReceipt),
Just(CloseMidflightBehaviorV1::FailClosed),
Just(CloseMidflightBehaviorV1::ContinueObservation),
Just(CloseMidflightBehaviorV1::BestEffortCompensation),
]
}
fn arb_effect_class() -> impl Strategy<Value = EffectClassV1> {
prop_oneof![
Just(EffectClassV1::ExternalWrite),
Just(EffectClassV1::ExternalRead),
Just(EffectClassV1::ExternalDelete),
Just(EffectClassV1::StateTransition),
Just(EffectClassV1::Compensation),
]
}
fn arb_blast_radius() -> impl Strategy<Value = BlastRadiusCeilingV1> {
prop_oneof![
Just(BlastRadiusCeilingV1::SingleTenant),
Just(BlastRadiusCeilingV1::SingleService),
Just(BlastRadiusCeilingV1::Regional),
Just(BlastRadiusCeilingV1::Global),
]
}
fn arb_reversibility() -> impl Strategy<Value = ReversibilityClassV1> {
prop_oneof![
Just(ReversibilityClassV1::Compensatable),
Just(ReversibilityClassV1::Replayable),
Just(ReversibilityClassV1::Irreversible),
]
}
fn arb_run_mode() -> impl Strategy<Value = RunModeV1> {
prop_oneof![
Just(RunModeV1::Simulated),
Just(RunModeV1::DryRun),
Just(RunModeV1::Replay),
]
}
fn arb_publication_status() -> impl Strategy<Value = PublicationStatusV1> {
prop_oneof![
Just(PublicationStatusV1::Required),
Just(PublicationStatusV1::AdvisoryOnly),
Just(PublicationStatusV1::Deferred),
Just(PublicationStatusV1::Suppressed),
]
}
fn arb_rfc3339_timestamp() -> impl Strategy<Value = String> {
(
2020u32..2030,
1u32..13,
1u32..29,
0u32..24,
0u32..60,
0u32..60,
)
.prop_map(|(y, m, d, h, min, s)| {
format!("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", y, m, d, h, min, s)
})
}
proptest! {
#[test]
fn effect_window_builder_validate_roundtrip(
commit_atomicity in arb_commit_atomicity(),
retry_owner in arb_retry_owner(),
close_midflight in arb_close_midflight(),
earliest in arb_rfc3339_timestamp(),
latest in arb_rfc3339_timestamp(),
timeout in arb_rfc3339_timestamp(),
) {
let window_id = EffectWindowId::new("ew-proptest");
let result = EffectWindowV1::builder(
window_id,
&earliest,
&latest,
&timeout,
commit_atomicity,
retry_owner,
close_midflight,
)
.build();
prop_assert!(result.is_ok(), "valid timestamps should build: {:?}", result.err());
let window = result.unwrap();
let json = serde_json::to_string(&window).unwrap();
let roundtrip: EffectWindowV1 = serde_json::from_str(&json).unwrap();
prop_assert_eq!(roundtrip.earliest_start, window.earliest_start);
prop_assert_eq!(roundtrip.timeout_at, window.timeout_at);
}
#[test]
fn effect_window_rejects_invalid_timestamp(
bad_stamp in "[a-z]{5,15}",
) {
let window_id = EffectWindowId::new("ew-bad");
let result = EffectWindowV1::builder(
window_id,
&bad_stamp,
"2025-01-01T00:00:00Z",
"2025-01-01T00:00:00Z",
CommitAtomicityV1::ExactlyOnce,
RetryOwnerV1::EffectRuntime,
CloseMidflightBehaviorV1::FailClosed,
)
.build();
prop_assert!(result.is_err());
if let Err(EffectRuntimeValidationError::InvalidTimestamp { field, .. }) = &result {
prop_assert_eq!(*field, "earliest_start");
} else {
prop_assert!(false, "expected InvalidTimestamp, got {:?}", result);
}
}
#[test]
fn effect_intent_builder_validate_roundtrip(
effect_class in arb_effect_class(),
blast_radius in arb_blast_radius(),
reversibility in arb_reversibility(),
run_mode in arb_run_mode(),
publication_status in arb_publication_status(),
) {
let intent_id = EffectIntentId::new("ei-proptest");
let window_id = EffectWindowId::new("ew-proptest");
let result = EffectIntentV1::builder(
intent_id,
effect_class,
"target_surface_test",
"intended_outcome_test",
vec!["artifact-ref-1".into()],
"capability_class_test",
"scope_test",
blast_radius,
reversibility,
"idempotency_key_test",
window_id,
vec!["obs-ref-1".into()],
vec!["approval-ref-1".into()],
run_mode,
publication_status,
)
.build();
prop_assert!(result.is_ok(), "valid intent should build: {:?}", result.err());
let intent = result.unwrap();
let json = serde_json::to_string(&intent).unwrap();
let roundtrip: EffectIntentV1 = serde_json::from_str(&json).unwrap();
prop_assert_eq!(roundtrip.effect_class, intent.effect_class);
prop_assert_eq!(roundtrip.blast_radius_ceiling, intent.blast_radius_ceiling);
prop_assert_eq!(roundtrip.reversibility_class, intent.reversibility_class);
}
}