use super::{update_frontmatter_content, FrontmatterValue}; struct UpdateCase<'a> { content: &'a str, key: &'a str, value: Option, expected_present: &'a [&'a str], expected_absent: &'a [&'a str], } fn assert_updated_content(case: UpdateCase<'_>) { let updated = update_frontmatter_content(case.content, case.key, case.value).unwrap(); for expected in case.expected_present { assert!( updated.contains(expected), "missing expected snippet: {expected}" ); } for unexpected in case.expected_absent { assert!( !updated.contains(unexpected), "found unexpected snippet: {unexpected}" ); } } #[test] fn test_update_frontmatter_replaces_or_adds_scalar_fields() { let cases = [ UpdateCase { content: "---\nStatus: Draft\n---\n# Test\n", key: "Status", value: Some(FrontmatterValue::String("Active".to_string())), expected_present: &["Status: Active"], expected_absent: &["Status: Draft"], }, UpdateCase { content: "---\nStatus: Draft\n---\n# Test\n", key: "Owner", value: Some(FrontmatterValue::String("Luca".to_string())), expected_present: &["Owner: Luca", "Status: Draft"], expected_absent: &[], }, UpdateCase { content: "---\n\"Is A\": Note\n---\n# Test\n", key: "Is A", value: Some(FrontmatterValue::String("Project".to_string())), expected_present: &["type: Project"], expected_absent: &["\"Is A\": Note", "\"Is A\": Project"], }, UpdateCase { content: "---\nStatus: Draft\n---\n# Test\n", key: "Reviewed", value: Some(FrontmatterValue::Bool(true)), expected_present: &["Reviewed: true"], expected_absent: &[], }, UpdateCase { content: "---\nStatus: Draft\n---\n# Test\n", key: "Priority", value: Some(FrontmatterValue::Number(5.0)), expected_present: &["Priority: 5"], expected_absent: &[], }, UpdateCase { content: "---\nStatus: Draft\n---\n# Test\n", key: "Score", value: Some(FrontmatterValue::Number(9.5)), expected_present: &["Score: 9.5"], expected_absent: &[], }, UpdateCase { content: "---\nStatus: Draft\n---\n# Test\n", key: "ClearMe", value: Some(FrontmatterValue::Null), expected_present: &["ClearMe: null"], expected_absent: &[], }, ]; for case in cases { assert_updated_content(case); } } #[test] fn test_update_frontmatter_list_and_delete_paths() { let list_cases = [ UpdateCase { content: "---\nStatus: Draft\n---\n# Test\n", key: "aliases", value: Some(FrontmatterValue::List(vec![ "Alias1".to_string(), "Alias2".to_string(), ])), expected_present: &["aliases:", " - \"Alias1\"", " - \"Alias2\""], expected_absent: &[], }, UpdateCase { content: "---\naliases:\n - Old1\n - Old2\nStatus: Draft\n---\n# Test\n", key: "aliases", value: Some(FrontmatterValue::List(vec!["New1".to_string()])), expected_present: &[" - \"New1\"", "Status: Draft"], expected_absent: &["Old1", "Old2"], }, UpdateCase { content: "---\naliases:\n - Alias1\n - Alias2\nStatus: Draft\n---\n# Test\n", key: "aliases", value: None, expected_present: &["Status: Draft"], expected_absent: &["aliases", "Alias1"], }, UpdateCase { content: "---\nStatus: Draft\nOwner: Luca\n---\n# Test\n", key: "Owner", value: None, expected_present: &["Status: Draft"], expected_absent: &["Owner"], }, UpdateCase { content: "---\nStatus: Draft\n---\n# Test\n", key: "tags", value: Some(FrontmatterValue::List(vec![])), expected_present: &["tags: []"], expected_absent: &[], }, ]; for case in list_cases { assert_updated_content(case); } } #[test] fn test_update_frontmatter_handles_missing_or_malformed_frontmatter() { let inserted = update_frontmatter_content( "# Test\n\nSome content here.", "Status", Some(FrontmatterValue::String("Draft".to_string())), ) .unwrap(); assert!(inserted.starts_with("---\n")); assert!(inserted.contains("Status: Draft")); assert!(inserted.contains("# Test")); let malformed = update_frontmatter_content( "---\nStatus: Draft\nNo closing fence here", "Status", Some(FrontmatterValue::String("Active".to_string())), ); assert!(malformed.is_err()); assert!(malformed.unwrap_err().contains("Malformed frontmatter")); let unchanged = update_frontmatter_content("---\nStatus: Draft\n---\n# Test\n", "Missing", None).unwrap(); assert_eq!(unchanged, "---\nStatus: Draft\n---\n# Test\n"); let no_frontmatter = update_frontmatter_content("# Test\n\nSome content.", "Missing", None).unwrap(); assert_eq!(no_frontmatter, "# Test\n\nSome content."); } #[test] fn test_update_frontmatter_canonicalizes_system_metadata_keys() { let cases = [ UpdateCase { content: "---\narchived: false\n---\n# Test\n", key: "_archived", value: Some(FrontmatterValue::Bool(true)), expected_present: &["_archived: true"], expected_absent: &["archived: false"], }, UpdateCase { content: "---\nicon: rocket\n---\n# Test\n", key: "icon", value: Some(FrontmatterValue::String("star".to_string())), expected_present: &["_icon: star"], expected_absent: &["\nicon:", "rocket"], }, UpdateCase { content: "---\nsidebar label: Projects\nsidebar_label: Legacy\n---\n# Test\n", key: "_sidebar_label", value: Some(FrontmatterValue::String("Programs".to_string())), expected_present: &["_sidebar_label: Programs"], expected_absent: &["sidebar label: Projects", "sidebar_label: Legacy"], }, UpdateCase { content: "---\nsort: modified:desc\n_sort: title:asc\n---\n# Test\n", key: "_sort", value: None, expected_present: &["# Test"], expected_absent: &["\nsort:", "\n_sort:"], }, ]; for case in cases { assert_updated_content(case); } } #[test] fn test_update_frontmatter_canonicalizes_type_key_case() { let cases = [ UpdateCase { content: "---\nType: Note\n---\n# Test\n", key: "type", value: Some(FrontmatterValue::String("Project".to_string())), expected_present: &["type: Project"], expected_absent: &["Type: Note"], }, UpdateCase { content: "---\n\"Is A\": Note\nis_a: Topic\n---\n# Test\n", key: "type", value: Some(FrontmatterValue::String("Project".to_string())), expected_present: &["type: Project"], expected_absent: &["\"Is A\": Note", "is_a: Topic"], }, UpdateCase { content: "---\nTYPE: Note\n---\n# Test\n", key: "Type", value: Some(FrontmatterValue::String("Person".to_string())), expected_present: &["type: Person"], expected_absent: &["TYPE: Note"], }, UpdateCase { content: "---\nType: Note\nstatus: Active\n---\n# Test\n", key: "type", value: None, expected_present: &["status: Active", "# Test"], expected_absent: &["Type: Note", "\ntype:"], }, ]; for case in cases { assert_updated_content(case); } }