agm_core/renderer/
json.rs1use crate::model::file::AgmFile;
4
5#[must_use]
11pub fn render_json(file: &AgmFile) -> String {
12 serde_json::to_string_pretty(file).expect("AgmFile is always serializable")
13}
14
15#[cfg(test)]
20mod tests {
21 use super::*;
22 use crate::model::fields::{FieldValue, NodeType, Span};
23 use crate::model::file::{AgmFile, Header};
24 use crate::model::node::Node;
25 use std::collections::BTreeMap;
26
27 fn minimal_file() -> AgmFile {
28 AgmFile {
29 header: Header {
30 agm: "1".to_owned(),
31 package: "test.minimal".to_owned(),
32 version: "0.1.0".to_owned(),
33 title: None,
34 owner: None,
35 imports: None,
36 default_load: None,
37 description: None,
38 tags: None,
39 status: None,
40 load_profiles: None,
41 target_runtime: None,
42 },
43 nodes: vec![Node {
44 id: "test.node".to_owned(),
45 node_type: NodeType::Facts,
46 summary: "a minimal test node".to_owned(),
47 priority: None,
48 stability: None,
49 confidence: None,
50 status: None,
51 depends: None,
52 related_to: None,
53 replaces: None,
54 conflicts: None,
55 see_also: None,
56 items: None,
57 steps: None,
58 fields: None,
59 input: None,
60 output: None,
61 detail: None,
62 rationale: None,
63 tradeoffs: None,
64 resolution: None,
65 examples: None,
66 notes: None,
67 code: None,
68 code_blocks: None,
69 verify: None,
70 agent_context: None,
71 target: None,
72 execution_status: None,
73 executed_by: None,
74 executed_at: None,
75 execution_log: None,
76 retry_count: None,
77 parallel_groups: None,
78 memory: None,
79 scope: None,
80 applies_when: None,
81 valid_from: None,
82 valid_until: None,
83 tags: None,
84 aliases: None,
85 keywords: None,
86 extra_fields: BTreeMap::new(),
87 span: Span::new(1, 3),
88 }],
89 }
90 }
91
92 #[test]
93 fn test_render_json_minimal_valid_json() {
94 let file = minimal_file();
95 let output = render_json(&file);
96 let parsed: serde_json::Value =
97 serde_json::from_str(&output).expect("should be valid JSON");
98 assert!(parsed.is_object());
99 }
100
101 #[test]
102 fn test_render_json_omits_none_fields() {
103 let file = minimal_file();
104 let output = render_json(&file);
105 assert!(!output.contains("priority"));
106 assert!(!output.contains("depends"));
107 assert!(!output.contains("steps"));
108 assert!(!output.contains("code"));
109 assert!(!output.contains("execution_status"));
110 assert!(!output.contains("memory"));
111 assert!(!output.contains("title"));
112 assert!(!output.contains("owner"));
113 }
114
115 #[test]
116 fn test_render_json_span_not_serialized() {
117 let file = minimal_file();
118 let output = render_json(&file);
119 assert!(!output.contains("start_line"));
120 assert!(!output.contains("end_line"));
121 assert!(!output.contains("span"));
122 }
123
124 #[test]
125 fn test_render_json_uses_spec_field_names() {
126 let file = minimal_file();
127 let output = render_json(&file);
128 assert!(output.contains("\"node\""));
130 assert!(output.contains("\"type\""));
131 assert!(!output.contains("\"id\""));
132 assert!(!output.contains("\"node_type\""));
133 }
134
135 #[test]
136 fn test_render_json_extra_fields_inlined() {
137 let mut file = minimal_file();
138 file.nodes[0].extra_fields.insert(
139 "custom_key".to_owned(),
140 FieldValue::Scalar("custom_val".to_owned()),
141 );
142 let output = render_json(&file);
143 assert!(output.contains("custom_key"));
144 assert!(output.contains("custom_val"));
145 }
146
147 #[test]
148 fn test_render_json_agm_version_as_stored_string() {
149 let file = minimal_file();
152 let output = render_json(&file);
153 assert!(output.contains("\"agm\""));
155 }
156}