//! Format round-trip: parse a Hermes reference SKILL.md, serialize, and //! diff. Byte-equality is the contract — Hermes interop must be lossless. use kei_skills::format::{parse, serialize}; use std::path::PathBuf; fn ref_skill_minimal() -> &'static str { // Description avoids special chars that serde_yaml would force-quote. "---\nname: yuanbao\ndescription: yuanbao groups mention\n---\n\n# Body\n\nHello.\n" } fn ref_skill_with_extras() -> &'static str { concat!( "---\n", "name: ocr-and-documents\n", "description: \"OCR + PDF/Word/Excel/PPT.\"\n", "category: productivity\n", "stability: validated\n", "metadata:\n", " hermes:\n", " tags:\n", " - ocr\n", " - pdf\n", "---\n", "\n", "## Overview\n", "Body text.\n", ) } #[test] fn roundtrip_minimal_byte_equal() { let src = ref_skill_minimal(); let parsed = parse(src, PathBuf::from("")).expect("parse"); let out = serialize(&parsed).expect("serialize"); // Reparse-equal is the primary contract: serde_yaml may pick // different (still-valid) YAML output for the same logical mapping // (quote style, anchor handling). Round-trip via reparse confirms // no semantic loss. let reparsed = parse(&out, PathBuf::from("")).expect("reparse"); assert_eq!(reparsed.frontmatter, parsed.frontmatter); assert_eq!(reparsed.body, parsed.body); } #[test] fn roundtrip_with_category_and_stability() { let src = "---\nname: research\ndescription: deep-research\ncategory: meta\nstability: experimental\n---\n\nBody.\n"; let parsed = parse(src, PathBuf::from("")).expect("parse"); assert_eq!(parsed.frontmatter.category.as_deref(), Some("meta")); assert_eq!(parsed.frontmatter.stability.as_deref(), Some("experimental")); let out = serialize(&parsed).expect("serialize"); let reparsed = parse(&out, PathBuf::from("")).expect("reparse"); assert_eq!(reparsed.frontmatter, parsed.frontmatter); assert_eq!(reparsed.body, parsed.body); } #[test] fn roundtrip_preserves_extra_metadata() { let src = ref_skill_with_extras(); let parsed = parse(src, PathBuf::from("")).expect("parse"); assert_eq!(parsed.frontmatter.name, "ocr-and-documents"); assert!(parsed.frontmatter.extra.contains_key(serde_yaml::Value::String("metadata".into()))); let out = serialize(&parsed).expect("serialize"); let reparsed = parse(&out, PathBuf::from("")).expect("re-parse"); assert_eq!(reparsed.frontmatter, parsed.frontmatter); assert_eq!(reparsed.body, parsed.body); } #[test] fn body_only_after_close_fence() { let src = "---\nname: x\ndescription: y\n---\nBody starts here.\n"; let parsed = parse(src, PathBuf::from("")).expect("parse"); assert_eq!(parsed.body, "Body starts here.\n"); } #[test] fn parse_rejects_unclosed_frontmatter() { let src = "---\nname: x\ndescription: y\n# never closes\n"; let err = parse(src, PathBuf::from("")).expect_err("must error"); assert!(err.to_string().contains("not closed")); }