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:
Luca Rossi
2026-02-25 00:02:49 +01:00
committed by GitHub
2 changed files with 144 additions and 1 deletions

View File

@@ -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"));
}
}

View File

@@ -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"));
}
}