Skip to content

Commit d8c4059

Browse files
committed
test(cli): add JSON output contract tests for doctor+config validate
1 parent cfb3879 commit d8c4059

2 files changed

Lines changed: 87 additions & 2 deletions

File tree

crates/loopforge-cli/src/dispatch/config.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ pub(super) fn run(command: ConfigCommand) -> anyhow::Result<()> {
88
let paths = RexosPaths::discover()?;
99
let report = validate_config(&paths);
1010
if json {
11-
println!("{}", serde_json::to_string_pretty(&report)?);
11+
println!(
12+
"{}",
13+
serde_json::to_string_pretty(&build_config_validate_json(&report)?)?
14+
);
1215
} else if report.valid {
1316
println!("config valid: {}", report.config_path);
1417
} else {
@@ -25,3 +28,35 @@ pub(super) fn run(command: ConfigCommand) -> anyhow::Result<()> {
2528
}
2629
}
2730
}
31+
32+
fn build_config_validate_json(
33+
report: &crate::config_validation::ConfigValidationReport,
34+
) -> anyhow::Result<serde_json::Value> {
35+
Ok(serde_json::to_value(report)?)
36+
}
37+
38+
#[cfg(test)]
39+
mod tests {
40+
use super::*;
41+
use crate::config_validation::ConfigValidationReport;
42+
use serde_json::json;
43+
44+
#[test]
45+
fn build_config_validate_json_keeps_expected_shape() {
46+
let report = ConfigValidationReport {
47+
valid: false,
48+
config_path: "/tmp/config.toml".to_string(),
49+
errors: vec!["router.planning.provider is empty".to_string()],
50+
};
51+
52+
let out = build_config_validate_json(&report).unwrap();
53+
assert_eq!(
54+
out,
55+
json!({
56+
"valid": false,
57+
"config_path": "/tmp/config.toml",
58+
"errors": ["router.planning.provider is empty"],
59+
})
60+
);
61+
}
62+
}

crates/loopforge-cli/src/dispatch/doctor.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ pub(super) async fn run(json: bool, strict: bool, timeout_ms: u64) -> anyhow::Re
1111
.await?;
1212

1313
if json {
14-
println!("{}", serde_json::to_string_pretty(&report)?);
14+
println!(
15+
"{}",
16+
serde_json::to_string_pretty(&build_doctor_json(&report)?)?
17+
);
1518
} else {
1619
println!("{}", report.to_text());
1720
}
@@ -22,3 +25,50 @@ pub(super) async fn run(json: bool, strict: bool, timeout_ms: u64) -> anyhow::Re
2225
}
2326
Ok(())
2427
}
28+
29+
fn build_doctor_json(report: &doctor::DoctorReport) -> anyhow::Result<serde_json::Value> {
30+
Ok(serde_json::to_value(report)?)
31+
}
32+
33+
#[cfg(test)]
34+
mod tests {
35+
use super::*;
36+
use crate::doctor::{CheckStatus, DoctorCheck, DoctorReport, DoctorSummary};
37+
use serde_json::json;
38+
39+
#[test]
40+
fn build_doctor_json_keeps_expected_shape_and_omits_empty_fields() {
41+
let report = DoctorReport {
42+
checks: vec![
43+
DoctorCheck {
44+
id: "alpha".to_string(),
45+
status: CheckStatus::Ok,
46+
message: String::new(),
47+
},
48+
DoctorCheck {
49+
id: "beta".to_string(),
50+
status: CheckStatus::Warn,
51+
message: "missing env".to_string(),
52+
},
53+
],
54+
summary: DoctorSummary {
55+
ok: 1,
56+
warn: 1,
57+
error: 0,
58+
},
59+
next_actions: Vec::new(),
60+
};
61+
62+
let out = build_doctor_json(&report).unwrap();
63+
assert_eq!(
64+
out,
65+
json!({
66+
"checks": [
67+
{ "id": "alpha", "status": "ok" },
68+
{ "id": "beta", "status": "warn", "message": "missing env" },
69+
],
70+
"summary": { "ok": 1, "warn": 1, "error": 0 },
71+
})
72+
);
73+
}
74+
}

0 commit comments

Comments
 (0)