use koi_common::capability::{Capability, CapabilityStatus};
use crate::cores::Cores;
pub struct CapabilityReport {
pub status: CapabilityStatus,
pub enabled: bool,
}
impl CapabilityReport {
fn present(status: CapabilityStatus) -> Self {
Self {
status,
enabled: true,
}
}
fn disabled(name: &str) -> Self {
Self {
status: CapabilityStatus {
name: name.to_string(),
summary: "disabled".to_string(),
healthy: false,
},
enabled: false,
}
}
fn stopped(name: &str) -> Self {
Self {
status: CapabilityStatus {
name: name.to_string(),
summary: "stopped".to_string(),
healthy: false,
},
enabled: true,
}
}
}
pub async fn assemble_capabilities(cores: &Cores) -> Vec<CapabilityReport> {
let mut caps = Vec::with_capacity(7);
caps.push(match &cores.mdns {
Some(core) => CapabilityReport::present(core.status()),
None => CapabilityReport::disabled("mdns"),
});
caps.push(match &cores.certmesh {
Some(core) => CapabilityReport::present(core.status()),
None => CapabilityReport::disabled("certmesh"),
});
caps.push(match &cores.dns {
Some(rt) if rt.status().await.running => CapabilityReport::present(rt.core().status()),
Some(_) => CapabilityReport::stopped("dns"),
None => CapabilityReport::disabled("dns"),
});
caps.push(match &cores.health {
Some(rt) if rt.status().await.running => CapabilityReport::present(rt.core().status()),
Some(_) => CapabilityReport::stopped("health"),
None => CapabilityReport::disabled("health"),
});
caps.push(match &cores.proxy {
Some(rt) => {
let listeners = rt.status().await;
let summary = if listeners.is_empty() {
"no listeners".to_string()
} else {
format!("{} listeners", listeners.len())
};
CapabilityReport::present(CapabilityStatus {
name: "proxy".to_string(),
summary,
healthy: true,
})
}
None => CapabilityReport::disabled("proxy"),
});
caps.push(match &cores.udp {
Some(rt) => CapabilityReport::present(Capability::status(rt.as_ref())),
None => CapabilityReport::disabled("udp"),
});
caps.push(match &cores.runtime {
Some(rt) => CapabilityReport::present(rt.capability_status().await),
None => CapabilityReport::disabled("runtime"),
});
caps
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn all_disabled_ladder_is_the_canonical_seven_rungs() {
let caps = assemble_capabilities(&Cores::default()).await;
let rungs: Vec<(&str, &str, bool, bool)> = caps
.iter()
.map(|c| {
(
c.status.name.as_str(),
c.status.summary.as_str(),
c.status.healthy,
c.enabled,
)
})
.collect();
assert_eq!(
rungs,
vec![
("mdns", "disabled", false, false),
("certmesh", "disabled", false, false),
("dns", "disabled", false, false),
("health", "disabled", false, false),
("proxy", "disabled", false, false),
("udp", "disabled", false, false),
("runtime", "disabled", false, false),
]
);
}
#[tokio::test]
async fn capability_status_projection_matches_v1_status_shape() {
let caps = assemble_capabilities(&Cores::default()).await;
let statuses: Vec<CapabilityStatus> = caps.into_iter().map(|c| c.status).collect();
let json = serde_json::to_value(&statuses).unwrap();
let first = &json[0];
assert_eq!(first["name"], "mdns");
assert_eq!(first["summary"], "disabled");
assert_eq!(first["healthy"], false);
assert!(first.get("enabled").is_none(), "/v1/status omits `enabled`");
}
}