use super::*;
fn cs(text: &str) -> ChangeSet {
ChangeSet {
edits: vec![Replacement::new(raw_range(0, 0), text)],
undo_open: false,
}
}
fn observation(e: &Editor, doc: DocumentId) -> (String, BufferRevision, usize, usize) {
let buf = &e.doc(doc).buf;
(
buf.text().to_string(),
buf.revision(),
buf.changes().len(),
buf.history().depth(),
)
}
#[test]
fn deliver_on_moved_revision_drops_without_publication() {
let mut e = Editor::new(Buffer::from_text("héllo\n"));
let doc = e.current();
let base = e.buf().revision();
e.apply(doc, base, cs("hi ")).expect("fresh publish lands");
let moved = observation(&e, doc);
assert_eq!(moved.0, "hi héllo\n");
let got = e.apply(doc, base, cs("x"));
assert!(
got == Err(ApplyError::Edit(EditError::StaleRevision {
expected: base,
found: moved.1,
})),
"the stale ticket must be refused with its exact base, got {got:?}"
);
assert_eq!(observation(&e, doc), moved, "a dropped ticket published");
}
#[test]
fn deliver_on_dead_document_is_no_document() {
let mut e = Editor::new(Buffer::from_text("one\n"));
let survivor = e.current();
let dead = e
.docs
.try_insert(Document::new(Buffer::from_text("two\n")))
.unwrap();
let base = e.doc(dead).buf.revision();
e.docs.remove(dead);
let survivor_before = observation(&e, survivor);
let got = e.apply(dead, base, cs("x"));
assert!(
got == Err(ApplyError::NoDocument),
"a dead id must be NoDocument, got {got:?}"
);
assert_eq!(
observation(&e, survivor),
survivor_before,
"NoMisapply: a dead delivery touched a survivor"
);
}
#[test]
fn reincarnation_never_accepts_the_old_ticket() {
let mut e = Editor::new(Buffer::from_text("one\n"));
let dead = e
.docs
.try_insert(Document::new(Buffer::from_text("two\n")))
.unwrap();
let base = e.doc(dead).buf.revision();
e.docs.remove(dead);
let fresh = e
.docs
.try_insert(Document::new(Buffer::from_text("again\n")))
.unwrap();
if fresh.index() == dead.index() {
assert_ne!(
fresh.generation(),
dead.generation(),
"slot reuse must bump the generation"
);
let before = observation(&e, fresh);
let got = e.apply(dead, base, cs("x"));
assert!(
got == Err(ApplyError::NoDocument),
"the old incarnation's ticket landed on the new tenant: {got:?}"
);
assert_eq!(
observation(&e, fresh),
before,
"NoWrongDocument: the reincarnation was mutated by a stale ticket"
);
}
}
#[test]
fn fresh_deliver_lands_on_exactly_the_asked_document() {
let mut e = Editor::new(Buffer::from_text("d1\n"));
let d1 = e.current();
let d2 = e
.docs
.try_insert(Document::new(Buffer::from_text("d2\n")))
.unwrap();
let base = e.doc(d1).buf.revision();
e.apply(d2, e.doc(d2).buf.revision(), cs("w"))
.expect("the interleaved publication is fresh");
let d2_before = observation(&e, d2);
let focus = (e.current(), e.active_pane);
let committed = e.apply(d1, base, cs("v")).expect("fresh ticket lands");
assert_eq!(committed.revision, e.doc(d1).buf.revision());
assert_eq!(e.doc(d1).buf.text().to_string(), "vd1\n");
assert!(
e.doc(d1).buf.changes().is_empty(),
"the mutation lease consumes its journal"
);
assert!(
e.doc(d1).buf.history().depth() > 0,
"the landed batch is journaled for undo"
);
assert_eq!(observation(&e, d2), d2_before, "Deliver touched d2");
assert_eq!((e.current(), e.active_pane), focus, "Deliver moved focus");
}
#[test]
fn consumed_ticket_never_lands_twice() {
let mut e = Editor::new(Buffer::from_text("base\n"));
let doc = e.current();
let base = e.buf().revision();
e.apply(doc, base, cs("1")).expect("first delivery lands");
let landed = observation(&e, doc);
let got = e.apply(doc, base, cs("2"));
assert!(
got == Err(ApplyError::Edit(EditError::StaleRevision {
expected: base,
found: landed.1,
})),
"a consumed ticket replays as stale, got {got:?}"
);
assert_eq!(
observation(&e, doc),
landed,
"TicketOneShot: a consumed ticket published again"
);
}
#[test]
fn readonly_delivery_refused_before_freshness() {
let mut e = Editor::new(Buffer::from_text("locked\n"));
let doc = e.current();
e.buf_mut()
.set_readonly(strop_core::ReadonlyReason::Command);
let before = observation(&e, doc);
let fresh_base = e.buf().revision();
let got = e.apply(doc, fresh_base, cs("x"));
assert!(
got == Err(ApplyError::Edit(EditError::ReadOnly)),
"readonly refusal precedes a fresh base, got {got:?}"
);
let got = e.apply(doc, BufferRevision::new(99), cs("y"));
assert!(
got == Err(ApplyError::Edit(EditError::ReadOnly)),
"readonly refusal precedes the freshness check, got {got:?}"
);
assert_eq!(
observation(&e, doc),
before,
"a readonly document moved under a delivery"
);
}