use kube::api::{Api, Patch, PatchParams};
use kube::Resource;
use serde::{de::DeserializeOwned, Serialize};
use serde_json::json;
use std::fmt::Debug;
#[must_use]
pub fn merge_status_body<S: Serialize + ?Sized>(status: &S) -> serde_json::Value {
json!({ "status": status })
}
pub async fn merge_status<K, S>(api: &Api<K>, name: &str, status: &S) -> Result<K, kube::Error>
where
K: Resource + DeserializeOwned + Clone + Debug,
K::DynamicType: Default,
S: Serialize + ?Sized,
{
let body = merge_status_body(status);
api.patch_status(name, &PatchParams::default(), &Patch::Merge(&body))
.await
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Serialize;
use serde_json::json;
#[test]
fn merge_status_body_wraps_typed_status_under_top_level_status_slot() {
#[derive(Serialize)]
struct S {
phase: &'static str,
reason: &'static str,
}
let body = merge_status_body(&S {
phase: "Bound",
reason: "member allocated",
});
assert_eq!(
body,
json!({ "status": { "phase": "Bound", "reason": "member allocated" } }),
);
}
#[test]
fn merge_status_body_top_level_key_is_exactly_status_lowercase() {
let body = merge_status_body(&json!({"phase": "Running"}));
let obj = body.as_object().expect("top-level must be a JSON object");
assert_eq!(obj.len(), 1, "wrap adds exactly ONE top-level slot");
assert!(
obj.contains_key("status"),
"top-level slot must be exactly `status` (lowercase)"
);
}
#[test]
fn merge_status_body_accepts_pre_serialized_json_value_verbatim() {
let pre = json!({"phase": "Attested", "phaseSince": "2026-05-01T00:00:00Z"});
let body = merge_status_body(&pre);
assert_eq!(body, json!({"status": pre}));
}
#[test]
fn merge_status_body_wraps_scalar_status_without_object_promotion() {
let body = merge_status_body(&"Attested");
assert_eq!(body, json!({"status": "Attested"}));
}
#[test]
fn merge_status_body_preserves_struct_update_composition_bytewise() {
#[derive(Serialize)]
struct Base {
phase: &'static str,
phase_since: &'static str,
extra: Option<&'static str>,
}
fn base() -> Base {
Base {
phase: "Queued",
phase_since: "2026-05-01T00:00:00Z",
extra: None,
}
}
let struct_update = Base {
extra: Some("pool matched"),
..base()
};
let spelled_out = Base {
phase: "Queued",
phase_since: "2026-05-01T00:00:00Z",
extra: Some("pool matched"),
};
assert_eq!(
merge_status_body(&struct_update),
merge_status_body(&spelled_out),
"struct-update composition serializes byte-identically to the fully-spelled struct literal",
);
}
#[test]
fn merge_status_delegates_wire_body_construction_to_merge_status_body() {
let body_via_helper = merge_status_body(&json!({"phase": "Running"}));
assert_eq!(body_via_helper["status"]["phase"], "Running");
}
}