Merge pull request #55 from refactoringhq/task/rename-nota-vuota
fix: rename fails on empty notes — handle empty frontmatter block
This commit is contained in:
@@ -128,7 +128,12 @@ fn is_list_continuation(line: &str) -> bool {
|
||||
/// Split content into frontmatter body and the rest after the closing `---`.
|
||||
/// Returns `(fm_content, rest)` where `fm_content` is between the opening and closing `---`.
|
||||
fn split_frontmatter(content: &str) -> Result<(&str, &str), String> {
|
||||
let fm_end = content[4..]
|
||||
let after_open = &content[4..];
|
||||
// Handle empty frontmatter: closing --- immediately after opening ---\n
|
||||
if let Some(stripped) = after_open.strip_prefix("---") {
|
||||
return Ok(("", stripped));
|
||||
}
|
||||
let fm_end = after_open
|
||||
.find("\n---")
|
||||
.map(|i| i + 4)
|
||||
.ok_or_else(|| "Malformed frontmatter: no closing ---".to_string())?;
|
||||
@@ -639,4 +644,73 @@ mod tests {
|
||||
fn test_format_yaml_key_with_period() {
|
||||
assert_eq!(format_yaml_key("key.name"), "\"key.name\"");
|
||||
}
|
||||
|
||||
// --- split_frontmatter / empty frontmatter edge cases ---
|
||||
|
||||
#[test]
|
||||
fn test_split_frontmatter_empty_block() {
|
||||
// ---\n---\n (no fields between opening and closing ---)
|
||||
let result = split_frontmatter("---\n---\n");
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"split_frontmatter should handle empty frontmatter block"
|
||||
);
|
||||
let (fm, rest) = result.unwrap();
|
||||
assert_eq!(fm, "");
|
||||
assert_eq!(rest, "\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_frontmatter_empty_block_no_trailing_newline() {
|
||||
// ---\n--- (no trailing newline)
|
||||
let result = split_frontmatter("---\n---");
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"split_frontmatter should handle empty frontmatter without trailing newline"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_frontmatter_empty_block_with_body() {
|
||||
// ---\n---\n\n# Title\n
|
||||
let result = split_frontmatter("---\n---\n\n# Title\n");
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"split_frontmatter should handle empty frontmatter with body"
|
||||
);
|
||||
let (fm, rest) = result.unwrap();
|
||||
assert_eq!(fm, "");
|
||||
assert!(rest.contains("# Title"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_frontmatter_empty_block() {
|
||||
let content = "---\n---\n\n# Test\n";
|
||||
let result = update_frontmatter_content(
|
||||
content,
|
||||
"title",
|
||||
Some(FrontmatterValue::String("New Title".to_string())),
|
||||
);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"update_frontmatter_content should handle empty frontmatter block"
|
||||
);
|
||||
let updated = result.unwrap();
|
||||
assert!(updated.contains("title: New Title"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_frontmatter_no_body_after_closing() {
|
||||
// Frontmatter with title, no body after closing ---
|
||||
let content = "---\ntitle: Old\n---\n";
|
||||
let result = update_frontmatter_content(
|
||||
content,
|
||||
"title",
|
||||
Some(FrontmatterValue::String("New".to_string())),
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
let updated = result.unwrap();
|
||||
assert!(updated.contains("title: New"));
|
||||
assert!(!updated.contains("title: Old"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,4 +402,73 @@ mod tests {
|
||||
assert!(content.contains("title: New Name"));
|
||||
assert!(content.contains("# New Name"));
|
||||
}
|
||||
|
||||
// --- Regression: rename empty / minimal notes (nota vuota) ---
|
||||
|
||||
/// Helper: create a note, rename it, assert the rename succeeded and old file is gone.
|
||||
/// Returns the content of the renamed file for further assertions.
|
||||
fn rename_test_note(filename: &str, content: &str, new_title: &str) -> String {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let vault = dir.path();
|
||||
create_test_file(vault, filename, content);
|
||||
|
||||
let old_path = vault.join(filename);
|
||||
let result = rename_note(
|
||||
vault.to_str().unwrap(),
|
||||
old_path.to_str().unwrap(),
|
||||
new_title,
|
||||
)
|
||||
.expect("rename_note should succeed");
|
||||
|
||||
let expected_slug = title_to_slug(new_title);
|
||||
assert!(
|
||||
result.new_path.ends_with(&format!("{}.md", expected_slug)),
|
||||
"new path should end with slug: {}",
|
||||
expected_slug
|
||||
);
|
||||
assert!(!old_path.exists(), "old file should be removed");
|
||||
assert!(
|
||||
Path::new(&result.new_path).exists(),
|
||||
"new file should exist"
|
||||
);
|
||||
|
||||
fs::read_to_string(&result.new_path).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_note_empty_file() {
|
||||
rename_test_note("note/empty.md", "", "Renamed Empty");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_note_empty_frontmatter_no_body() {
|
||||
rename_test_note("note/empty-fm.md", "---\n---\n", "Renamed Note");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_note_frontmatter_title_no_body() {
|
||||
let content = rename_test_note(
|
||||
"note/titled.md",
|
||||
"---\ntitle: Old Title\ntype: Note\n---\n",
|
||||
"New Title",
|
||||
);
|
||||
assert!(content.contains("title: New Title"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_note_h1_only_no_body() {
|
||||
let content = rename_test_note("note/heading-only.md", "# Old Heading\n", "New Heading");
|
||||
assert!(content.contains("# New Heading"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_note_frontmatter_and_h1_no_body() {
|
||||
let content = rename_test_note(
|
||||
"note/full-empty.md",
|
||||
"---\ntitle: My Note\ntype: Note\nstatus: Active\n---\n\n# My Note\n\n",
|
||||
"Renamed Note",
|
||||
);
|
||||
assert!(content.contains("title: Renamed Note"));
|
||||
assert!(content.contains("# Renamed Note"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user