Files
2026-10-14 14:24:08 -03:00

115 lines
5.9 KiB
Rust

//! Folder and duplicate commands share the headless registry surface.
mod common;
use common::{seed, temp_vault};
use notescraft_engine::{Registry, Session, register_core};
use serde_json::json;
#[test]
fn folder_commands_move_links_bookmarks_buffers_and_attachments() {
let vault = temp_vault("folders");
seed(
vault.path(),
&[("old/A.md", "[[old/deep/B]]"), ("old/deep/B.md", "[[old/A]]"), ("root.md", "[[old/A]] [[old/deep/B]] ![[old/pic.png]]"), ("old/pic.png", "image")],
);
let mut session = Session::open(vault.path()).expect("session");
session.add_bookmark("old/A.md", None, None).expect("bookmark");
session.open_buffer("old/A.md").expect("buffer");
session.buffer_mut("old/A.md").expect("buffer").edit(0..0, "unsaved ").expect("edit");
let mut reg = Registry::new();
register_core(&mut reg).expect("registry");
reg.run("folder.create", &mut session, &json!({"path":"empty"})).expect("create");
assert!(vault.path().join("empty").is_dir());
reg.run("folder.rename", &mut session, &json!({"from":"old","to":"new"})).expect("rename");
assert_eq!(session.read_note("root.md").expect("read"), "[[new/A]] [[new/deep/B]] ![[new/pic.png]]");
assert_eq!(session.read_note("new/A.md").expect("read"), "[[new/deep/B]]");
assert_eq!(session.read_note("new/deep/B.md").expect("read"), "[[new/A]]");
assert_eq!(session.bookmarks().expect("bookmarks").first().map(|b| b.path.as_str()), Some("new/A.md"));
assert!(session.buffer("new/A.md").is_some_and(|buffer| buffer.is_dirty() && buffer.text().starts_with("unsaved")));
assert!(session.index().files().contains(&"new/pic.png"));
assert!(session.index().note("old/A.md").is_none());
reg.run("folder.delete", &mut session, &json!({"path":"new"})).expect("trash");
assert!(!vault.path().join("new").exists());
assert!(session.index().note("new/A.md").is_none());
assert!(!session.index().files().contains(&"new/pic.png"));
assert!(session.buffer("new/A.md").is_none());
}
#[test]
fn duplicate_preserves_content_and_uses_first_free_copy_name() {
let vault = temp_vault("duplicate");
seed(vault.path(), &[("sub/Note.md", "héllo\r\n"), ("sub/Note copy.md", "occupied")]);
let mut session = Session::open(vault.path()).expect("session");
let mut reg = Registry::new();
register_core(&mut reg).expect("registry");
let result = reg.run("note.duplicate", &mut session, &json!({"path":"sub/Note.md"})).expect("duplicate");
assert_eq!(result.get("path"), Some(&json!("sub/Note copy 2.md")));
assert_eq!(session.read_note("sub/Note copy 2.md").expect("read"), "héllo\r\n");
assert!(session.index().note("sub/Note copy 2.md").is_some());
}
#[test]
fn folders_refuse_root_reserved_collisions_and_read_only() {
let vault = temp_vault("folder-guards");
seed(vault.path(), &[("old/A.md", "body"), ("taken/B.md", "body")]);
let mut session = Session::open(vault.path()).expect("session");
let mut reg = Registry::new();
register_core(&mut reg).expect("registry");
for path in ["", ".", ".obsidian", ".notescraft", "../outside"] {
for id in ["folder.create", "folder.delete"] {
assert!(reg.run(id, &mut session, &json!({"path":path})).is_err(), "{id}: {path}");
}
assert!(reg.run("folder.rename", &mut session, &json!({"from":"old","to":path})).is_err());
}
for to in ["taken", "old/nested"] {
assert!(reg.run("folder.rename", &mut session, &json!({"from":"old","to":to})).is_err());
}
let mut readonly = Session::open_read_only(vault.path()).expect("readonly");
for id in ["folder.create", "folder.rename", "folder.delete", "note.duplicate"] {
assert!(!reg.is_enabled(id, &readonly));
assert!(reg.run(id, &mut readonly, &json!({"path":"old"})).is_err());
}
}
#[cfg(unix)]
#[test]
fn folder_aliases_cannot_move_vault_root_or_private_state() {
let vault = temp_vault("folder-symlinks");
let mut session = Session::open(vault.path()).expect("session");
std::os::unix::fs::symlink(vault.path(), vault.path().join("alias")).expect("symlink");
std::os::unix::fs::symlink(vault.path().join(".notescraft"), vault.path().join("private")).expect("symlink");
let mut reg = Registry::new();
register_core(&mut reg).expect("registry");
for path in ["alias", "private"] {
assert!(reg.run("folder.rename", &mut session, &json!({"from":path,"to":"moved"})).is_err());
}
assert!(vault.path().is_dir());
assert!(vault.path().join(".notescraft").is_dir());
}
#[test]
fn folder_move_without_link_updates_keeps_links_and_moves_bookmarks() {
let vault = temp_vault("folder-no-rewrite");
seed(vault.path(), &[("old/A.md", "body"), ("root.md", "[[old/A]]")]);
let mut session = Session::open(vault.path()).expect("session");
session.add_bookmark("old/A.md", Some("Heading"), None).expect("bookmark");
let mut reg = Registry::new();
register_core(&mut reg).expect("registry");
reg.run("folder.rename", &mut session, &json!({"from":"old","to":"new","update_links":false})).expect("rename");
assert_eq!(session.read_note("root.md").expect("read"), "[[old/A]]");
assert_eq!(session.bookmarks().expect("bookmarks").first().map(|b| b.path.as_str()), Some("new/A.md"));
}
#[test]
fn renamed_links_refresh_clean_buffers_and_search() {
let vault = temp_vault("rename-mirrors");
seed(vault.path(), &[("A.md", "# A"), ("root.md", "[[A]]")]);
let mut session = Session::open(vault.path()).expect("session");
session.open_buffer("root.md").expect("buffer");
let mut reg = Registry::new();
register_core(&mut reg).expect("registry");
reg.run("note.rename", &mut session, &json!({"from":"A.md","to":"Zebra.md"})).expect("rename");
assert_eq!(session.buffer("root.md").map(notescraft_engine::Buffer::text), Some("[[Zebra]]"));
let result = reg.run("search", &mut session, &json!({"query":"Zebra"})).expect("search");
assert!(result.to_string().contains("root.md"));
}