use std::collections::BTreeMap;
use std::time::Duration;
use serde::{Deserialize, Serialize};
pub const DIAGNOSTICS_GENERATE_PATH: &str = "/data/api/v1/diagnostics/bundle/generate";
pub const DIAGNOSTICS_STATUS_PATH: &str = "/data/api/v1/diagnostics/bundle/status";
pub const DIAGNOSTICS_DOWNLOAD_PATH: &str = "/data/api/v1/diagnostics/bundle/download";
pub const BUNDLE_GENERATING_STATES: &[&str] = &[
"Generating",
];
pub const BUNDLE_CAPTURED_STATES: &[&str] = &[
"Generating",
"Valid",
"Invalid",
];
pub const BUNDLE_UNAVAILABLE_STATES: &[&str] = &[
"Invalid",
];
pub fn is_generating(state: &str) -> bool {
BUNDLE_GENERATING_STATES.contains(&state)
}
pub fn is_bundle_unavailable(state: &str) -> bool {
BUNDLE_UNAVAILABLE_STATES.contains(&state)
}
pub const BUNDLE_DOWNLOAD_TIMEOUT: Duration = Duration::from_secs(300);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BundleStatusWire {
#[serde(default)]
pub state: String,
#[serde(
rename = "fileSize",
alias = "file_size",
default,
skip_serializing_if = "Option::is_none"
)]
pub file_size: Option<u64>,
#[serde(flatten)]
pub extra: BTreeMap<String, serde_json::Value>,
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::{
BUNDLE_CAPTURED_STATES, BUNDLE_DOWNLOAD_TIMEOUT, BundleStatusWire, is_bundle_unavailable,
is_generating,
};
#[test]
fn bundle_status_parses_the_live_8_3_6_captures() {
let generating: BundleStatusWire =
serde_json::from_str(r#"{"state":"Generating"}"#).expect("generating capture parses");
assert_eq!(generating.state, "Generating");
assert_eq!(
generating.file_size, None,
"fileSize is ABSENT while generating (not null, not 0)"
);
let valid: BundleStatusWire = serde_json::from_str(r#"{"state":"Valid","fileSize":61053}"#)
.expect("terminal capture parses");
assert_eq!(valid.state, "Valid");
assert_eq!(valid.file_size, Some(61_053), "bytes == Content-Length");
let round = serde_json::to_value(&generating).expect("serialize");
assert!(round.get("fileSize").is_none(), "absent stays absent");
let round = serde_json::to_value(&valid).expect("serialize");
assert_eq!(round["fileSize"], 61_053);
}
#[test]
fn bundle_status_parses_the_live_8_3_3_capture() {
let valid: BundleStatusWire = serde_json::from_str(r#"{"state":"Valid","fileSize":55281}"#)
.expect("terminal capture parses");
assert_eq!(valid.state, "Valid");
assert_eq!(valid.file_size, Some(55_281));
}
#[test]
fn generating_membership_is_exact() {
assert!(is_generating("Generating"));
assert!(!is_generating("Valid"));
assert!(!is_generating("Failed"));
assert!(!is_generating("generating"), "case-sensitive");
assert!(!is_generating("RUNNING"), "no guessed vocabulary");
assert!(!is_generating(""));
}
#[test]
fn captured_vocabulary_is_the_three_observed_states() {
assert_eq!(BUNDLE_CAPTURED_STATES, &["Generating", "Valid", "Invalid"]);
assert_eq!(super::BUNDLE_GENERATING_STATES, &["Generating"]);
assert_eq!(super::BUNDLE_UNAVAILABLE_STATES, &["Invalid"]);
}
#[test]
fn invalid_is_a_captured_terminal_unavailable_state() {
assert!(
BUNDLE_CAPTURED_STATES.contains(&"Invalid"),
"Invalid is in the captured vocabulary"
);
assert!(
!is_generating("Invalid"),
"Invalid is terminal, not mid-generation"
);
assert!(
is_bundle_unavailable("Invalid"),
"Invalid is the unavailable (immediate-exit) class"
);
assert!(
!is_bundle_unavailable("Valid"),
"Valid stays terminal success"
);
assert!(
!is_generating("Mystery") && !is_bundle_unavailable("Mystery"),
"a genuinely unknown state is neither — honest unknowns keep polling"
);
}
#[test]
fn bundle_download_timeout_is_300_seconds() {
assert_eq!(BUNDLE_DOWNLOAD_TIMEOUT, Duration::from_secs(300));
}
#[test]
fn unknown_keys_round_trip() {
let wire: BundleStatusWire = serde_json::from_value(serde_json::json!({
"state": "Valid",
"fileSize": 61053,
"zzFuturePointReleaseKey": {"future": true}
}))
.expect("unknown keys never refuse");
assert_eq!(
wire.extra.get("zzFuturePointReleaseKey"),
Some(&serde_json::json!({"future": true})),
"unknown keys ride passthrough"
);
assert_eq!(wire.state, "Valid");
}
#[test]
fn generating_set_is_subset_of_captured_vocabulary() {
for state in super::BUNDLE_GENERATING_STATES {
assert!(
BUNDLE_CAPTURED_STATES.contains(state),
"every generating state is captured: {state}"
);
}
for state in super::BUNDLE_UNAVAILABLE_STATES {
assert!(
BUNDLE_CAPTURED_STATES.contains(state),
"every unavailable state is captured: {state}"
);
}
}
}