//! Does `PrintReport` actually parse what real printers really send? //! //! The fixtures in `tests/fixtures/` are verbatim `print.push_status` //! payloads captured off real hardware on 2026-10-03 via //! `cargo run --example live_reports` (hardware identifiers scrubbed): //! //! - `push_status_v1_p1p_idle.json` - a P1P on BambuMQTT v1, idle. //! - `push_status_v2_h2c_printing.json` - an H2C on BambuMQTT v2, 13% into //! a real job. //! //! These exist because the struct was originally built from a *truncated* //! example payload, and the gap between that and reality turned out to be //! the cause of two live misbehaviours (silently empty `print_status()`, and //! bogus "sequence gap" warnings). A fixture is the only way to keep that //! from regressing without a printer plugged in. use continuum_proxy::printer::PrintReport; fn load(name: &str) -> serde_json::Value { let path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/"); let text = std::fs::read_to_string(format!("{path}{name}")).expect("fixture should exist"); serde_json::from_str(&text).expect("fixture should be valid JSON") } /// The failure mode that mattered: `handle_incoming_report` does /// `if let Ok(report) = serde_json::from_value::(..)`, so a /// parse error doesn't just lose that one update - it's invisible. Assert on /// the error text, not just `is_ok()`, so a regression says *what* broke. fn assert_parses(name: &str) { let value = load(name); match serde_json::from_value::(value) { Ok(_) => {} Err(err) => panic!("{name} failed to parse as PrintReport: {err}"), } } #[test] fn parses_v1_idle_push_status() { assert_parses("push_status_v1_p1p_idle.json"); } #[test] fn parses_v2_printing_push_status() { assert_parses("push_status_v2_h2c_printing.json"); } /// The whole point of consuming these at all: a print farm needs progress /// and temperatures. If the struct parses but drops these, it's no more /// useful than not parsing. #[test] fn v2_printing_report_carries_progress_and_temperatures() { let value = load("push_status_v2_h2c_printing.json"); let report: PrintReport = serde_json::from_value(value).expect("should parse"); assert_eq!(report.gcode_state.as_deref(), Some("RUNNING")); assert_eq!(report.mc_percent, Some(13)); assert_eq!(report.layer_num, Some(2)); assert_eq!(report.total_layer_num, Some(50)); assert_eq!(report.mc_remaining_time, Some(102)); assert_eq!(report.nozzle_temper, Some(230.0)); assert_eq!(report.subtask_name.as_deref(), Some("\u{15b}wieczka-24.stl")); } /// An idle printer still has to produce a coherent report - just with /// nothing printing. #[test] fn v1_idle_report_reads_as_idle() { let value = load("push_status_v1_p1p_idle.json"); let report: PrintReport = serde_json::from_value(value).expect("should parse"); assert_eq!(report.gcode_state.as_deref(), Some("IDLE")); assert_eq!(report.mc_percent, Some(0)); } /// v1 and v2 disagree about tray field names (`tray_temp`/`tray_time` vs /// `drying_temp`/`drying_time`) - this is the specific mismatch that made /// every v1 push carrying AMS data fail. Both spellings have to work. #[test] fn both_protocol_generations_parse_their_ams_trays() { for name in ["push_status_v1_p1p_idle.json", "push_status_v2_h2c_printing.json"] { let value = load(name); let report: PrintReport = serde_json::from_value(value).unwrap_or_else(|err| { panic!("{name}: {err}"); }); let ams = report.ams.expect("fixture should carry AMS state"); assert!(!ams.ams.is_empty(), "{name}: expected at least one AMS unit"); } }