bc0b258e04
Sidecars (*.comments.json) stay out of the index and listings and follow rename and delete of their note; end-to-end comments test over a stub server (Ana comments and mentions Bob, Bob resolves, second sync changes nothing); viewer extension tables gain ico, ogv, 3gp, opus, aac; annotate crate: Edit::Replace for undoable text moves and edits, line items carry an arrow flag; syntax test fixed (TeX is a bundled grammar). Built by MiniMax M3.1 Flash and Claude Sonnet; verified with the full cargo xtask ci on the combined tree (4422 tests, clippy -D warnings, layers, tree, i18n). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
84 lines
3.6 KiB
Rust
84 lines
3.6 KiB
Rust
//! The life of the sidecar that holds the comments of a note (D1): it follows
|
|
//! the note when it moves and it goes when the note goes.
|
|
|
|
mod common;
|
|
|
|
use common::{seed, temp_vault};
|
|
use notescraft_engine::{Registry, Session, register_core};
|
|
use serde_json::{Value, json};
|
|
|
|
/// The note the tests comment on.
|
|
const NOTE: &str = "# Note\n\nThis always works, except on slow disks.\n";
|
|
|
|
/// The quote the tests anchor their thread to.
|
|
const QUOTE: &str = "This always works";
|
|
|
|
/// A registry, a session and a vault with one note.
|
|
fn fixture(name: &str) -> (Registry<Session>, Session, common::TempVault) {
|
|
let vault = temp_vault(name);
|
|
seed(vault.path(), &[("note.md", NOTE)]);
|
|
let mut reg = Registry::new();
|
|
register_core(&mut reg).expect("register the core commands");
|
|
let session = Session::open(vault.path()).expect("open the vault");
|
|
(reg, session, vault)
|
|
}
|
|
|
|
/// Adds a thread on the quote of the note.
|
|
fn add(reg: &Registry<Session>, session: &mut Session) {
|
|
let start = NOTE.find(QUOTE).expect("the quote is in the note");
|
|
reg.run(
|
|
"comment.add",
|
|
session,
|
|
&json!({ "path": "note.md", "quote": QUOTE, "body": "Can we soften this claim?", "start": start, "end": start + QUOTE.len() }),
|
|
)
|
|
.expect("comment.add");
|
|
}
|
|
|
|
/// The threads of the sidecar of `path`, as the file holds them.
|
|
fn threads_on_disk(vault: &common::TempVault, path: &str) -> Vec<Value> {
|
|
let bytes = std::fs::read(vault.path().join(format!("{path}.comments.json"))).expect("the sidecar of the note");
|
|
let parsed: Value = serde_json::from_slice(&bytes).expect("the sidecar is JSON");
|
|
parsed.get("comments").and_then(Value::as_array).expect("the threads of the note").clone()
|
|
}
|
|
|
|
/// A renamed note takes its sidecar with it.
|
|
#[test]
|
|
fn a_renamed_note_keeps_its_sidecar() {
|
|
let (reg, mut session, vault) = fixture("rename");
|
|
add(®, &mut session);
|
|
|
|
reg.run("note.rename", &mut session, &json!({ "from": "note.md", "to": "moved.md" })).expect("note.rename");
|
|
|
|
assert!(!vault.path().join("note.md.comments.json").exists(), "the old sidecar is gone");
|
|
let threads = threads_on_disk(&vault, "moved.md");
|
|
assert_eq!(threads.len(), 1, "the threads follow the note");
|
|
assert_eq!(threads.first().and_then(|thread| thread.get("body")), Some(&json!("Can we soften this claim?")));
|
|
let bytes = std::fs::read(vault.path().join("moved.md.comments.json")).expect("the sidecar of the moved note");
|
|
let parsed: Value = serde_json::from_slice(&bytes).expect("the sidecar is JSON");
|
|
assert_eq!(parsed.get("note"), Some(&json!("moved.md")), "the sidecar points at the new path");
|
|
}
|
|
|
|
/// A deleted note takes its sidecar with it.
|
|
#[test]
|
|
fn a_deleted_note_removes_its_sidecar() {
|
|
let (reg, mut session, vault) = fixture("delete");
|
|
add(®, &mut session);
|
|
assert!(vault.path().join("note.md.comments.json").exists(), "the sidecar is there before the delete");
|
|
|
|
reg.run("note.delete", &mut session, &json!({ "path": "note.md" })).expect("note.delete");
|
|
|
|
assert!(!vault.path().join("note.md").exists(), "the note is gone");
|
|
assert!(!vault.path().join("note.md.comments.json").exists(), "the sidecar went with it");
|
|
}
|
|
|
|
/// A note without threads has no sidecar, and deleting it is not an error.
|
|
#[test]
|
|
fn a_note_without_threads_has_no_sidecar() {
|
|
let (reg, mut session, vault) = fixture("empty");
|
|
|
|
reg.run("note.delete", &mut session, &json!({ "path": "note.md" })).expect("note.delete");
|
|
|
|
assert!(!vault.path().join("note.md").exists(), "the note is gone");
|
|
assert!(!vault.path().join("note.md.comments.json").exists(), "and there was no sidecar to go");
|
|
}
|