use std::collections::HashMap;
use hwpforge_blueprint::builtins::{builtin_default, builtin_gov_proposal};
use hwpforge_blueprint::error::BlueprintError;
use hwpforge_blueprint::inheritance::resolve_template;
use hwpforge_blueprint::registry::StyleRegistry;
use hwpforge_blueprint::schema::template_schema_json;
use hwpforge_blueprint::template::Template;
use hwpforge_foundation::{Alignment, Color, HwpUnit};
#[test]
fn full_pipeline_yaml_to_registry() {
let yaml = r#"
meta:
name: test_pipeline
version: 1.0.0
page:
width: 210mm
height: 297mm
styles:
body:
char_shape:
font: 한컴바탕
size: 10pt
color: '#000000'
para_shape:
alignment: Justify
line_spacing:
spacing_type: Percentage
value: '160%'
heading:
char_shape:
font: 한컴바탕
size: 16pt
bold: true
color: '#003366'
para_shape:
alignment: Left
spacing:
before: 12pt
after: 6pt
markdown_mapping:
body: body
heading1: heading
"#;
let template = Template::from_yaml(yaml).unwrap();
assert_eq!(template.meta.name, "test_pipeline");
assert_eq!(template.styles.len(), 2);
let registry = StyleRegistry::from_template(&template).unwrap();
assert_eq!(registry.style_count(), 2);
assert_eq!(registry.font_count(), 1);
let body = registry.get_style("body").unwrap();
let body_cs = registry.char_shape(body.char_shape_id).unwrap();
assert_eq!(body_cs.font, "한컴바탕");
assert_eq!(body_cs.size, HwpUnit::from_pt(10.0).unwrap());
assert_eq!(body_cs.color, Color::BLACK);
let body_ps = registry.para_shape(body.para_shape_id).unwrap();
assert_eq!(body_ps.alignment, Alignment::Justify);
assert_eq!(body_ps.line_spacing_value, 160.0);
let heading = registry.get_style("heading").unwrap();
let heading_cs = registry.char_shape(heading.char_shape_id).unwrap();
assert!(heading_cs.bold);
assert_eq!(heading_cs.color, Color::from_rgb(0x00, 0x33, 0x66));
let heading_ps = registry.para_shape(heading.para_shape_id).unwrap();
assert_eq!(heading_ps.space_before, HwpUnit::from_pt(12.0).unwrap());
}
#[test]
fn inheritance_pipeline_parent_child_to_registry() {
let parent_yaml = r#"
meta:
name: base
styles:
body:
char_shape:
font: Arial
size: 10pt
para_shape:
alignment: Left
heading:
char_shape:
font: Arial
size: 16pt
bold: true
"#;
let child_yaml = r#"
meta:
name: custom
extends: base
styles:
body:
char_shape:
size: 12pt
color: '#003366'
para_shape:
alignment: Justify
"#;
let parent = Template::from_yaml(parent_yaml).unwrap();
let child = Template::from_yaml(child_yaml).unwrap();
let mut provider = HashMap::new();
provider.insert("base".to_string(), parent);
provider.insert("custom".to_string(), child.clone());
let resolved = resolve_template(&child, &provider).unwrap();
assert!(resolved.meta.extends.is_none());
let body = resolved.styles.get("body").unwrap();
assert_eq!(body.char_shape.as_ref().unwrap().font, Some("Arial".to_string()));
assert_eq!(body.char_shape.as_ref().unwrap().size, Some(HwpUnit::from_pt(12.0).unwrap()));
assert_eq!(body.char_shape.as_ref().unwrap().color, Some(Color::from_rgb(0, 0x33, 0x66)));
assert!(resolved.styles.contains_key("heading"));
let registry = StyleRegistry::from_template(&resolved).unwrap();
assert_eq!(registry.style_count(), 2);
assert_eq!(registry.font_count(), 1); }
#[test]
fn builtin_default_full_pipeline() {
let template = builtin_default().unwrap();
let registry = StyleRegistry::from_template(&template).unwrap();
assert_eq!(registry.style_count(), 7);
assert_eq!(registry.font_count(), 2);
for name in &["body", "heading1", "heading2", "heading3", "code", "blockquote", "list_item"] {
let entry = registry.get_style(name).unwrap_or_else(|| panic!("missing style: {name}"));
assert!(registry.char_shape(entry.char_shape_id).is_some());
assert!(registry.para_shape(entry.para_shape_id).is_some());
}
}
#[test]
fn builtin_gov_proposal_full_pipeline() {
let default = builtin_default().unwrap();
let gov = builtin_gov_proposal().unwrap();
let mut provider = HashMap::new();
provider.insert("default".to_string(), default);
provider.insert("gov_proposal".to_string(), gov.clone());
let resolved = resolve_template(&gov, &provider).unwrap();
let registry = StyleRegistry::from_template(&resolved).unwrap();
assert_eq!(registry.style_count(), 9);
let title = registry.get_style("title").unwrap();
let title_ps = registry.para_shape(title.para_shape_id).unwrap();
assert_eq!(title_ps.alignment, Alignment::Center);
}
#[test]
fn invalid_yaml_returns_parse_error() {
let err = Template::from_yaml("meta:\n name: [invalid").unwrap_err();
assert!(matches!(err, BlueprintError::YamlParse { .. }));
}
#[test]
fn empty_styles_registry_error() {
let yaml = "meta:\n name: empty\nstyles: {}\n";
let template = Template::from_yaml(yaml).unwrap();
let err = StyleRegistry::from_template(&template).unwrap_err();
assert!(matches!(err, BlueprintError::EmptyStyleMap));
}
#[test]
fn missing_font_registry_error() {
let yaml = r#"
meta:
name: broken
styles:
body:
char_shape:
size: 10pt
"#;
let template = Template::from_yaml(yaml).unwrap();
let err = StyleRegistry::from_template(&template).unwrap_err();
match err {
BlueprintError::StyleResolution { field, .. } => assert_eq!(field, "font"),
_ => panic!("Expected StyleResolution error"),
}
}
#[test]
fn schema_json_is_valid_and_describes_template() {
let json = template_schema_json();
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(value["title"], "Template");
assert!(value["properties"]["meta"].is_object());
assert!(value["properties"]["styles"].is_object());
}