use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::flux_resource::FluxResource;
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Boundary {
#[serde(default)]
pub preconditions: Vec<Condition>,
#[serde(default)]
pub postconditions: Vec<Condition>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout: Option<String>,
}
impl Boundary {
#[must_use]
pub fn has_condition_kind(&self, kind: ConditionKind) -> bool {
self.has_precondition_kind(kind) || self.has_postcondition_kind(kind)
}
#[must_use]
pub fn has_precondition_kind(&self, kind: ConditionKind) -> bool {
self.preconditions.has_kind(kind)
}
#[must_use]
pub fn has_postcondition_kind(&self, kind: ConditionKind) -> bool {
self.postconditions.has_kind(kind)
}
#[must_use]
pub fn find_condition_kind(&self, kind: ConditionKind) -> Option<&Condition> {
self.find_precondition_kind(kind)
.or_else(|| self.find_postcondition_kind(kind))
}
#[must_use]
pub fn find_precondition_kind(&self, kind: ConditionKind) -> Option<&Condition> {
self.preconditions.find_kind(kind)
}
#[must_use]
pub fn find_postcondition_kind(&self, kind: ConditionKind) -> Option<&Condition> {
self.postconditions.find_kind(kind)
}
pub fn iter_condition_kind(
&self,
kind: ConditionKind,
) -> std::iter::Chain<KindMatches<'_>, KindMatches<'_>> {
self.iter_precondition_kind(kind)
.chain(self.iter_postcondition_kind(kind))
}
pub fn iter_precondition_kind(&self, kind: ConditionKind) -> KindMatches<'_> {
self.preconditions.iter_kind(kind)
}
pub fn iter_postcondition_kind(&self, kind: ConditionKind) -> KindMatches<'_> {
self.postconditions.iter_kind(kind)
}
#[must_use]
pub fn count_condition_kind(&self, kind: ConditionKind) -> usize {
self.count_precondition_kind(kind) + self.count_postcondition_kind(kind)
}
#[must_use]
pub fn count_precondition_kind(&self, kind: ConditionKind) -> usize {
self.preconditions.count_kind(kind)
}
#[must_use]
pub fn count_postcondition_kind(&self, kind: ConditionKind) -> usize {
self.postconditions.count_kind(kind)
}
}
pub trait ConditionSliceExt {
fn iter_kind(&self, kind: ConditionKind) -> KindMatches<'_>;
fn find_kind(&self, kind: ConditionKind) -> Option<&Condition> {
self.iter_kind(kind).next()
}
fn has_kind(&self, kind: ConditionKind) -> bool {
self.find_kind(kind).is_some()
}
fn count_kind(&self, kind: ConditionKind) -> usize {
self.iter_kind(kind).count()
}
}
pub struct KindMatches<'a> {
inner: std::slice::Iter<'a, Condition>,
kind: ConditionKind,
}
impl<'a> Iterator for KindMatches<'a> {
type Item = &'a Condition;
fn next(&mut self) -> Option<Self::Item> {
self.inner.by_ref().find(|c| c.kind == self.kind)
}
}
impl ConditionSliceExt for [Condition] {
fn iter_kind(&self, kind: ConditionKind) -> KindMatches<'_> {
KindMatches {
inner: self.iter(),
kind,
}
}
}
#[track_caller]
pub fn assert_slice_refinement_composition_laws<S>(slice: &S)
where
S: ConditionSliceExt + ?Sized,
{
for kind in ConditionKind::ALL {
let find_result = slice.find_kind(kind);
let has_result = slice.has_kind(kind);
let count_result = slice.count_kind(kind);
let iter_next_kind = slice.iter_kind(kind).next().map(|c| c.kind);
let iter_count = slice.iter_kind(kind).count();
assert_eq!(
find_result.map(|c| c.kind),
iter_next_kind,
"find_kind({kind:?}) drifted from iter_kind({kind:?}).next()",
);
assert_eq!(
count_result, iter_count,
"count_kind({kind:?}) drifted from iter_kind({kind:?}).count()",
);
assert_eq!(
has_result,
find_result.is_some(),
"has_kind({kind:?}) drifted from find_kind({kind:?}).is_some()",
);
assert_eq!(
has_result,
count_result > 0,
"has_kind({kind:?}) drifted from (count_kind({kind:?}) > 0)",
);
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Condition {
pub kind: ConditionKind,
#[serde(default)]
#[schemars(schema_with = "crate::schema_helpers::preserve_unknown_object")]
pub params: serde_json::Value,
}
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
JsonSchema,
tatara_closed_set::DeriveClosedSet,
)]
#[serde(rename_all = "PascalCase")]
#[closed_set(via = "as_str", display, generate_unknown)]
pub enum ConditionKind {
ProcessPhase,
KustomizationHealthy,
HelmReleaseReleased,
PromQL,
Cel,
NixEval,
JobAttested,
ClosedLoopAuth,
}
impl ConditionKind {
pub const ALL: [Self; 8] = [
Self::ProcessPhase,
Self::KustomizationHealthy,
Self::HelmReleaseReleased,
Self::PromQL,
Self::Cel,
Self::NixEval,
Self::JobAttested,
Self::ClosedLoopAuth,
];
pub const fn as_str(self) -> &'static str {
match self {
Self::ProcessPhase => "ProcessPhase",
Self::KustomizationHealthy => "KustomizationHealthy",
Self::HelmReleaseReleased => "HelmReleaseReleased",
Self::PromQL => "PromQL",
Self::Cel => "Cel",
Self::NixEval => "NixEval",
Self::JobAttested => "JobAttested",
Self::ClosedLoopAuth => "ClosedLoopAuth",
}
}
pub const fn stub_message(self) -> Option<&'static str> {
match self {
Self::PromQL => Some("PromQL evaluator not yet implemented"),
Self::Cel => Some("CEL evaluator not yet implemented"),
Self::NixEval => Some("NixEval evaluator not yet implemented"),
Self::ProcessPhase
| Self::KustomizationHealthy
| Self::HelmReleaseReleased
| Self::JobAttested
| Self::ClosedLoopAuth => None,
}
}
pub const fn is_stub(self) -> bool {
self.stub_message().is_some()
}
pub const fn flux_resource(self) -> Option<FluxResource> {
match self {
Self::KustomizationHealthy => Some(FluxResource::Kustomization),
Self::HelmReleaseReleased => Some(FluxResource::HelmRelease),
Self::ProcessPhase
| Self::PromQL
| Self::Cel
| Self::NixEval
| Self::JobAttested
| Self::ClosedLoopAuth => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn serde_process_phase_condition() {
let c = Condition {
kind: ConditionKind::ProcessPhase,
params: json!({ "processRef": "secret-injection", "phase": "Attested" }),
};
let yaml = serde_yaml::to_string(&c).unwrap();
assert!(yaml.contains("kind: ProcessPhase"));
assert!(yaml.contains("processRef: secret-injection"));
}
#[test]
fn serde_closed_loop_auth_condition() {
let c = Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({
"issuer": { "service": "demo-app-issuer", "port": 8080 },
"consumer": { "service": "demo-app-gateway", "port": 8000 },
"probeImage": "ghcr.io/pleme-io/closed-loop-probe:0.1.0",
}),
};
let yaml = serde_yaml::to_string(&c).unwrap();
assert!(yaml.contains("kind: ClosedLoopAuth"));
assert!(yaml.contains("probeImage: ghcr.io/pleme-io/closed-loop-probe:0.1.0"));
let back: Condition = serde_yaml::from_str(&yaml).unwrap();
assert_eq!(back.kind, ConditionKind::ClosedLoopAuth);
}
#[test]
fn serde_job_attested_condition() {
let c = Condition {
kind: ConditionKind::JobAttested,
params: json!({ "name": "seed-job", "namespace": "demo-test" }),
};
let yaml = serde_yaml::to_string(&c).unwrap();
assert!(yaml.contains("kind: JobAttested"));
}
#[test]
fn condition_kind_is_well_formed_closed_set() {
tatara_closed_set::assert_closed_set_well_formed::<ConditionKind>();
}
#[test]
fn condition_kind_as_str_matches_serde() {
crate::tagged_union::assert_label_matches_serde_serialization::<ConditionKind>();
}
#[test]
fn condition_kind_display_matches_as_str() {
crate::tagged_union::assert_display_matches_label::<ConditionKind>();
}
#[test]
fn unknown_condition_kind_errors() {
use std::str::FromStr;
for bad in ["processPhase", "PROMQL", "Promql", "Bogus"] {
let err = ConditionKind::from_str(bad).unwrap_err();
assert_eq!(err.0, bad, "error payload should echo input verbatim");
}
}
#[test]
fn condition_kind_stub_set_matches_stubs() {
use ConditionKind::*;
for kind in ConditionKind::ALL {
let expected_is_stub = matches!(kind, PromQL | Cel | NixEval);
assert_eq!(
kind.is_stub(),
expected_is_stub,
"is_stub disagreed for {kind:?}",
);
assert_eq!(
kind.stub_message().is_some(),
expected_is_stub,
"stub_message disagreed for {kind:?}",
);
}
}
#[test]
fn condition_kind_stub_messages_are_pinned() {
assert_eq!(
ConditionKind::PromQL.stub_message(),
Some("PromQL evaluator not yet implemented"),
);
assert_eq!(
ConditionKind::Cel.stub_message(),
Some("CEL evaluator not yet implemented"),
);
assert_eq!(
ConditionKind::NixEval.stub_message(),
Some("NixEval evaluator not yet implemented"),
);
}
#[test]
fn kustomization_healthy_projects_to_flux_resource_kustomization() {
assert_eq!(
ConditionKind::KustomizationHealthy.flux_resource(),
Some(FluxResource::Kustomization),
);
}
#[test]
fn helm_release_released_projects_to_flux_resource_helm_release() {
assert_eq!(
ConditionKind::HelmReleaseReleased.flux_resource(),
Some(FluxResource::HelmRelease),
);
}
#[test]
fn non_flux_fetching_kinds_project_to_none() {
use ConditionKind::*;
let non_flux: Vec<_> = ConditionKind::ALL
.iter()
.copied()
.filter(|k| k.flux_resource().is_none())
.collect();
assert_eq!(
non_flux,
vec![
ProcessPhase,
PromQL,
Cel,
NixEval,
JobAttested,
ClosedLoopAuth
],
);
}
#[test]
fn flux_resource_projection_is_injective_on_the_some_arms() {
let mut seen = std::collections::HashSet::new();
for k in ConditionKind::ALL {
if let Some(fr) = k.flux_resource() {
assert!(
seen.insert(fr),
"duplicate FluxResource projection at {k:?}: {fr:?}",
);
}
}
}
#[test]
fn flux_resource_projection_is_const_fn_reachable() {
const K: Option<FluxResource> = ConditionKind::KustomizationHealthy.flux_resource();
const H: Option<FluxResource> = ConditionKind::HelmReleaseReleased.flux_resource();
const P: Option<FluxResource> = ConditionKind::ProcessPhase.flux_resource();
assert_eq!(K, Some(FluxResource::Kustomization));
assert_eq!(H, Some(FluxResource::HelmRelease));
assert_eq!(P, None);
}
fn condition_with(kind: ConditionKind) -> Condition {
Condition {
kind,
params: json!({}),
}
}
#[test]
fn has_condition_kind_returns_false_on_empty_boundary_for_every_kind() {
let b = Boundary::default();
for kind in ConditionKind::ALL {
assert!(
!b.has_condition_kind(kind),
"default boundary must return false for {kind:?}",
);
}
}
#[test]
fn has_condition_kind_reads_postconditions_per_kind() {
for populated in ConditionKind::ALL {
let mut b = Boundary::default();
b.postconditions.push(condition_with(populated));
for query in ConditionKind::ALL {
let expected = query == populated;
assert_eq!(
b.has_condition_kind(query),
expected,
"postcondition populated={populated:?}: query {query:?} drifted",
);
}
}
}
#[test]
fn has_condition_kind_reads_preconditions_per_kind() {
for populated in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(populated));
for query in ConditionKind::ALL {
let expected = query == populated;
assert_eq!(
b.has_condition_kind(query),
expected,
"precondition populated={populated:?}: query {query:?} drifted",
);
}
}
}
#[test]
fn has_condition_kind_unions_pre_and_post_condition_arms() {
let mut b = Boundary::default();
b.preconditions
.push(condition_with(ConditionKind::KustomizationHealthy));
b.postconditions
.push(condition_with(ConditionKind::ClosedLoopAuth));
assert!(
b.has_condition_kind(ConditionKind::KustomizationHealthy),
"pre-only kind must resolve through the union",
);
assert!(
b.has_condition_kind(ConditionKind::ClosedLoopAuth),
"post-only kind must resolve through the union",
);
assert!(
!b.has_condition_kind(ConditionKind::PromQL),
"an absent kind must return false even with populated halves",
);
}
#[test]
fn condition_slice_has_kind_returns_false_on_empty_slice_for_every_kind() {
let empty: &[Condition] = &[];
for kind in ConditionKind::ALL {
assert!(
!empty.has_kind(kind),
"empty slice must return false for {kind:?}",
);
}
}
#[test]
fn condition_slice_has_kind_reads_kind_field_per_variant() {
for populated in ConditionKind::ALL {
let slice = [condition_with(populated)];
for query in ConditionKind::ALL {
let expected = query == populated;
assert_eq!(
slice.has_kind(query),
expected,
"populated={populated:?}: query {query:?} drifted",
);
}
}
}
#[test]
fn condition_slice_has_kind_scans_beyond_the_first_position() {
let slice = [
condition_with(ConditionKind::KustomizationHealthy),
condition_with(ConditionKind::ClosedLoopAuth),
condition_with(ConditionKind::JobAttested),
];
for present in [
ConditionKind::KustomizationHealthy,
ConditionKind::ClosedLoopAuth,
ConditionKind::JobAttested,
] {
assert!(
slice.has_kind(present),
"kind at any position must resolve true: {present:?}",
);
}
for absent in [
ConditionKind::ProcessPhase,
ConditionKind::HelmReleaseReleased,
ConditionKind::PromQL,
ConditionKind::Cel,
ConditionKind::NixEval,
] {
assert!(
!slice.has_kind(absent),
"kind absent from the slice must resolve false: {absent:?}",
);
}
}
#[test]
fn boundary_has_condition_kind_equals_or_of_half_slice_probes() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(pre_kind));
b.postconditions.push(condition_with(post_kind));
for query in ConditionKind::ALL {
let expected =
b.preconditions.has_kind(query) || b.postconditions.has_kind(query);
assert_eq!(
b.has_condition_kind(query),
expected,
"union drifted: pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn has_precondition_kind_returns_false_on_empty_boundary_for_every_kind() {
let b = Boundary::default();
for kind in ConditionKind::ALL {
assert!(
!b.has_precondition_kind(kind),
"default boundary must return false on precondition arm for {kind:?}",
);
}
}
#[test]
fn has_postcondition_kind_returns_false_on_empty_boundary_for_every_kind() {
let b = Boundary::default();
for kind in ConditionKind::ALL {
assert!(
!b.has_postcondition_kind(kind),
"default boundary must return false on postcondition arm for {kind:?}",
);
}
}
#[test]
fn has_precondition_kind_reads_preconditions_slice_only() {
for populated in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(populated));
for query in ConditionKind::ALL {
let expected_pre = query == populated;
assert_eq!(
b.has_precondition_kind(query),
expected_pre,
"precondition-only populated={populated:?}: query {query:?} drifted \
on precondition arm",
);
assert!(
!b.has_postcondition_kind(query),
"precondition-only populated={populated:?}: query {query:?} must \
return false on postcondition arm (postconditions is empty)",
);
}
}
}
#[test]
fn has_postcondition_kind_reads_postconditions_slice_only() {
for populated in ConditionKind::ALL {
let mut b = Boundary::default();
b.postconditions.push(condition_with(populated));
for query in ConditionKind::ALL {
let expected_post = query == populated;
assert_eq!(
b.has_postcondition_kind(query),
expected_post,
"postcondition-only populated={populated:?}: query {query:?} \
drifted on postcondition arm",
);
assert!(
!b.has_precondition_kind(query),
"postcondition-only populated={populated:?}: query {query:?} must \
return false on precondition arm (preconditions is empty)",
);
}
}
}
#[test]
fn boundary_has_condition_kind_composes_precondition_and_postcondition_arms() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(pre_kind));
b.postconditions.push(condition_with(post_kind));
for query in ConditionKind::ALL {
let via_arms =
b.has_precondition_kind(query) || b.has_postcondition_kind(query);
assert_eq!(
b.has_condition_kind(query),
via_arms,
"union arm drifted from OR of half-slice arms: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn has_precondition_and_postcondition_kind_delegate_to_slice_has_kind() {
for populated in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(populated));
b.postconditions.push(condition_with(populated));
for query in ConditionKind::ALL {
assert_eq!(
b.has_precondition_kind(query),
b.preconditions.has_kind(query),
"precondition arm must delegate to preconditions.has_kind: \
populated={populated:?} query={query:?}",
);
assert_eq!(
b.has_postcondition_kind(query),
b.postconditions.has_kind(query),
"postcondition arm must delegate to postconditions.has_kind: \
populated={populated:?} query={query:?}",
);
}
}
}
#[test]
fn condition_slice_find_kind_returns_none_on_empty_slice_for_every_kind() {
let empty: &[Condition] = &[];
for kind in ConditionKind::ALL {
assert!(
empty.find_kind(kind).is_none(),
"empty slice must return None for {kind:?}",
);
}
}
#[test]
fn condition_slice_find_kind_reads_kind_field_per_variant() {
for populated in ConditionKind::ALL {
let slice = [condition_with(populated)];
for query in ConditionKind::ALL {
let hit = slice.find_kind(query);
if query == populated {
assert_eq!(
hit.map(|c| c.kind),
Some(populated),
"populated={populated:?}: query {query:?} must return Some",
);
} else {
assert!(
hit.is_none(),
"populated={populated:?}: query {query:?} must return None",
);
}
}
}
}
#[test]
fn condition_slice_find_kind_returns_first_position_on_duplicate_kinds() {
let first = Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "probeImage": "first" }),
};
let second = Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "probeImage": "second" }),
};
let slice = [first, second];
let hit = slice
.find_kind(ConditionKind::ClosedLoopAuth)
.expect("populated slice must resolve Some on the matching kind");
assert_eq!(
hit.params
.get("probeImage")
.and_then(serde_json::Value::as_str),
Some("first"),
"find_kind must return the FIRST position's Condition on duplicate kinds",
);
}
#[test]
fn condition_slice_has_kind_equals_find_kind_is_some() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let slice = [condition_with(pre_kind), condition_with(post_kind)];
for query in ConditionKind::ALL {
assert_eq!(
slice.has_kind(query),
slice.find_kind(query).is_some(),
"slice-level has/find refinement bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn find_condition_kind_triad_delegates_to_slice_find_kind() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(pre_kind));
b.postconditions.push(condition_with(post_kind));
for query in ConditionKind::ALL {
let via_pre = b.preconditions.find_kind(query);
let via_post = b.postconditions.find_kind(query);
assert_eq!(
b.find_precondition_kind(query).map(|c| c.kind),
via_pre.map(|c| c.kind),
"precondition find arm must delegate to preconditions.find_kind: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
assert_eq!(
b.find_postcondition_kind(query).map(|c| c.kind),
via_post.map(|c| c.kind),
"postcondition find arm must delegate to postconditions.find_kind: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
let expected_union = via_pre.or(via_post).map(|c| c.kind);
assert_eq!(
b.find_condition_kind(query).map(|c| c.kind),
expected_union,
"union find arm must equal precondition.or_else(postcondition): \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn find_condition_kind_returns_precondition_side_on_dual_populated() {
let mut b = Boundary::default();
b.preconditions.push(Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "side": "pre" }),
});
b.postconditions.push(Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "side": "post" }),
});
let hit = b
.find_condition_kind(ConditionKind::ClosedLoopAuth)
.expect("dual-populated boundary must resolve Some");
assert_eq!(
hit.params.get("side").and_then(serde_json::Value::as_str),
Some("pre"),
"find_condition_kind must walk preconditions first: dual-populated kind \
returned postcondition-side Condition rather than precondition-side",
);
}
#[test]
fn boundary_has_triad_equals_find_triad_is_some_projection() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(pre_kind));
b.postconditions.push(condition_with(post_kind));
for query in ConditionKind::ALL {
assert_eq!(
b.has_precondition_kind(query),
b.find_precondition_kind(query).is_some(),
"precondition has/find bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
assert_eq!(
b.has_postcondition_kind(query),
b.find_postcondition_kind(query).is_some(),
"postcondition has/find bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
assert_eq!(
b.has_condition_kind(query),
b.find_condition_kind(query).is_some(),
"union has/find bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn condition_slice_iter_kind_yields_nothing_on_empty_slice_for_every_kind() {
let empty: &[Condition] = &[];
for kind in ConditionKind::ALL {
assert_eq!(
empty.iter_kind(kind).count(),
0,
"empty slice must yield nothing on iter_kind for {kind:?}",
);
}
}
#[test]
fn condition_slice_iter_kind_reads_kind_field_per_variant() {
for populated in ConditionKind::ALL {
let slice = [condition_with(populated)];
for query in ConditionKind::ALL {
let collected: Vec<_> = slice.iter_kind(query).map(|c| c.kind).collect();
if query == populated {
assert_eq!(
collected,
vec![populated],
"populated={populated:?}: query {query:?} must yield [populated]",
);
} else {
assert!(
collected.is_empty(),
"populated={populated:?}: query {query:?} must yield nothing",
);
}
}
}
}
#[test]
fn condition_slice_iter_kind_yields_every_match_in_slice_order_on_duplicates() {
let first = Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "probeImage": "first" }),
};
let middle = Condition {
kind: ConditionKind::PromQL,
params: json!({ "query": "up" }),
};
let second_cla = Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "probeImage": "second" }),
};
let slice = [first, middle, second_cla];
let hits: Vec<_> = slice
.iter_kind(ConditionKind::ClosedLoopAuth)
.map(|c| {
c.params
.get("probeImage")
.and_then(serde_json::Value::as_str)
.unwrap_or_default()
.to_owned()
})
.collect();
assert_eq!(
hits,
vec!["first".to_owned(), "second".to_owned()],
"iter_kind must yield every match in slice order (not just the first)",
);
assert_eq!(
slice.iter_kind(ConditionKind::ClosedLoopAuth).count(),
2,
"iter_kind must skip non-matching kinds, not include them in the stream",
);
}
#[test]
fn condition_slice_find_kind_equals_iter_kind_next() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let slice = [condition_with(pre_kind), condition_with(post_kind)];
for query in ConditionKind::ALL {
assert_eq!(
slice.find_kind(query).map(|c| c.kind),
slice.iter_kind(query).next().map(|c| c.kind),
"slice-level find/iter refinement bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn iter_condition_kind_triad_delegates_to_slice_iter_kind() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(pre_kind));
b.postconditions.push(condition_with(post_kind));
for query in ConditionKind::ALL {
let via_pre: Vec<_> =
b.preconditions.iter_kind(query).map(|c| c.kind).collect();
let via_post: Vec<_> =
b.postconditions.iter_kind(query).map(|c| c.kind).collect();
assert_eq!(
b.iter_precondition_kind(query)
.map(|c| c.kind)
.collect::<Vec<_>>(),
via_pre,
"precondition iter arm must delegate to preconditions.iter_kind: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
assert_eq!(
b.iter_postcondition_kind(query)
.map(|c| c.kind)
.collect::<Vec<_>>(),
via_post,
"postcondition iter arm must delegate to postconditions.iter_kind: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
let mut expected_union = via_pre.clone();
expected_union.extend(via_post.iter().copied());
assert_eq!(
b.iter_condition_kind(query)
.map(|c| c.kind)
.collect::<Vec<_>>(),
expected_union,
"union iter arm must chain precondition ⨟ postcondition: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn boundary_find_triad_equals_iter_triad_next_projection() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(pre_kind));
b.postconditions.push(condition_with(post_kind));
for query in ConditionKind::ALL {
assert_eq!(
b.find_precondition_kind(query).map(|c| c.kind),
b.iter_precondition_kind(query).next().map(|c| c.kind),
"precondition find/iter bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
assert_eq!(
b.find_postcondition_kind(query).map(|c| c.kind),
b.iter_postcondition_kind(query).next().map(|c| c.kind),
"postcondition find/iter bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
assert_eq!(
b.find_condition_kind(query).map(|c| c.kind),
b.iter_condition_kind(query).next().map(|c| c.kind),
"union find/iter bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn iter_condition_kind_yields_preconditions_before_postconditions_on_dual_populated() {
let mut b = Boundary::default();
b.preconditions.push(Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "side": "pre-1" }),
});
b.preconditions.push(Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "side": "pre-2" }),
});
b.postconditions.push(Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "side": "post-1" }),
});
let sides: Vec<_> = b
.iter_condition_kind(ConditionKind::ClosedLoopAuth)
.map(|c| {
c.params
.get("side")
.and_then(serde_json::Value::as_str)
.unwrap_or_default()
.to_owned()
})
.collect();
assert_eq!(
sides,
vec!["pre-1".to_owned(), "pre-2".to_owned(), "post-1".to_owned(),],
"iter_condition_kind must yield every precondition-side match before any \
postcondition-side match (chain order pinned by two-surface parity contract)",
);
}
#[test]
fn condition_slice_count_kind_returns_zero_on_empty_slice_for_every_kind() {
let empty: &[Condition] = &[];
for kind in ConditionKind::ALL {
assert_eq!(
empty.count_kind(kind),
0,
"empty slice must count 0 for {kind:?}",
);
}
}
#[test]
fn condition_slice_count_kind_reads_kind_field_per_variant() {
for populated in ConditionKind::ALL {
let slice = [condition_with(populated)];
for query in ConditionKind::ALL {
let expected = if query == populated { 1 } else { 0 };
assert_eq!(
slice.count_kind(query),
expected,
"populated={populated:?} query={query:?} \
must count {expected}",
);
}
}
}
#[test]
fn condition_slice_count_kind_counts_every_match_on_duplicates() {
let slice = [
Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "probeImage": "first" }),
},
Condition {
kind: ConditionKind::PromQL,
params: json!({ "query": "up" }),
},
Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "probeImage": "second" }),
},
];
assert_eq!(slice.count_kind(ConditionKind::ClosedLoopAuth), 2);
assert_eq!(slice.count_kind(ConditionKind::PromQL), 1);
for kind in ConditionKind::ALL {
if matches!(kind, ConditionKind::ClosedLoopAuth | ConditionKind::PromQL) {
continue;
}
assert_eq!(
slice.count_kind(kind),
0,
"non-populated kind {kind:?} must count 0",
);
}
}
#[test]
fn condition_slice_count_kind_equals_iter_kind_count() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let slice = [condition_with(pre_kind), condition_with(post_kind)];
for query in ConditionKind::ALL {
assert_eq!(
slice.count_kind(query),
slice.iter_kind(query).count(),
"count/iter bridge drifted: pre={pre_kind:?} \
post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn condition_slice_has_and_find_equal_count_greater_than_zero() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let slice = [condition_with(pre_kind), condition_with(post_kind)];
for query in ConditionKind::ALL {
let count = slice.count_kind(query);
assert_eq!(
slice.has_kind(query),
count > 0,
"has/count bridge drifted: pre={pre_kind:?} \
post={post_kind:?} query={query:?}",
);
assert_eq!(
slice.find_kind(query).is_some(),
count > 0,
"find/count bridge drifted: pre={pre_kind:?} \
post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn boundary_count_condition_kind_triad_delegates_and_sums_slice_count_kind() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(pre_kind));
b.postconditions.push(condition_with(post_kind));
for query in ConditionKind::ALL {
let via_pre = b.preconditions.count_kind(query);
let via_post = b.postconditions.count_kind(query);
assert_eq!(
b.count_precondition_kind(query),
via_pre,
"boundary precondition count arm must delegate: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
assert_eq!(
b.count_postcondition_kind(query),
via_post,
"boundary postcondition count arm must delegate: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
assert_eq!(
b.count_condition_kind(query),
via_pre + via_post,
"boundary union count arm must SUM pre + post: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn boundary_count_triad_equals_iter_triad_count_projection() {
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let mut b = Boundary::default();
b.preconditions.push(condition_with(pre_kind));
b.preconditions.push(condition_with(pre_kind));
b.postconditions.push(condition_with(post_kind));
for query in ConditionKind::ALL {
assert_eq!(
b.count_precondition_kind(query),
b.iter_precondition_kind(query).count(),
"precondition count/iter bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
assert_eq!(
b.count_postcondition_kind(query),
b.iter_postcondition_kind(query).count(),
"postcondition count/iter bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
assert_eq!(
b.count_condition_kind(query),
b.iter_condition_kind(query).count(),
"union count/iter bridge drifted: \
pre={pre_kind:?} post={post_kind:?} query={query:?}",
);
}
}
}
}
#[test]
fn slice_refinement_composition_laws_hold_across_authored_arrangements() {
let empty: &[Condition] = &[];
assert_slice_refinement_composition_laws(empty);
for populated in ConditionKind::ALL {
let single = [condition_with(populated)];
assert_slice_refinement_composition_laws(single.as_slice());
}
for pre_kind in ConditionKind::ALL {
for post_kind in ConditionKind::ALL {
let dual = [condition_with(pre_kind), condition_with(post_kind)];
assert_slice_refinement_composition_laws(dual.as_slice());
}
}
for populated in ConditionKind::ALL {
let duplicates = [
condition_with(populated),
condition_with(populated),
condition_with(populated),
];
assert_slice_refinement_composition_laws(duplicates.as_slice());
}
}
#[test]
fn slice_refinement_composition_laws_hold_on_interleaved_duplicates() {
let interleaved = [
Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "probeImage": "first" }),
},
Condition {
kind: ConditionKind::PromQL,
params: json!({ "query": "up" }),
},
Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "probeImage": "second" }),
},
Condition {
kind: ConditionKind::PromQL,
params: json!({ "query": "healthy" }),
},
Condition {
kind: ConditionKind::ClosedLoopAuth,
params: json!({ "probeImage": "third" }),
},
];
assert_slice_refinement_composition_laws(interleaved.as_slice());
}
}