WIP red: a retry after RecordGone, and a rename's unchecked destination (improvise-20ek, improvise-milr)

This commit is contained in:
Edward Langley
2026-09-23 08:15:38 -07:00
parent 42aeeb6b03
commit 0d14f49e51
@@ -1195,3 +1195,167 @@ fn a_second_rename_after_a_reorder_renames_the_row_the_host_shows() {
assert_eq!(region(&host, &shown), None);
assert_eq!(region(&host, "Zed"), Some(CellValue::Number(2.0)));
}
// ── races on a coordinate rename (improvise-20ek, improvise-milr) ───────
type Sid = improvise_command::ui::project::SessionId;
/// A client driven into records mode through the tile bar, its cursor on the
/// coordinate column of row 0. Returns the session, the client and the column.
fn records_client_at_coordinate(host: &mut Host) -> (Sid, Client, usize) {
let mut a = Client::new();
a.set_viewport(10, 10);
let (id, snapshot) = host.connect(10, 10);
deliver(&mut a, &[snapshot]);
press(host, id, &mut a, "T");
tile_to(host, id, &mut a, "_Index", "r");
tile_to(host, id, &mut a, "_Dim", "c");
press(host, id, &mut a, "Escape");
assert!(a.state().grid.records, "setup: records mode");
let coordinate = (0..a.state().grid.extent.cols)
.find(|&c| {
format!("{:?}", a.state().grid.cell(0, c).and_then(|x| x.id.clone()))
.starts_with("Some(Coordinate")
})
.expect("a coordinate column");
while a.state().view.cursor().selected.1 < coordinate {
press(host, id, &mut a, "l");
}
(id, a, coordinate)
}
fn retype(host: &mut Host, id: Sid, a: &mut Client, text: &str) {
for _ in 0..8 {
press(host, id, a, "Backspace");
}
for ch in text.chars() {
press(host, id, a, &ch.to_string());
}
}
/// Another session's frame, applied, with what it owes `a` delivered to `a`.
fn from_another_client(host: &mut Host, a_id: Sid, a: &mut Client, cmd: ModelCommandArg) {
let (b_id, _) = host.connect(10, 10);
let frame = improvise_command::ui::reduce::caught_up(host.app(), cmd);
let relayed = host.handle(b_id, ClientFrame::command(frame));
assert!(
relayed.iter().any(|f| f.to == b_id
&& matches!(f.msg, improvise_protocol::ServerFrame::Accepted)),
"the other client's write must land: {relayed:?}"
);
let for_a: Vec<Addressed> = relayed.into_iter().filter(|f| f.to == a_id).collect();
deliver(a, &for_a);
}
type ModelCommandArg = improvise_protocol::ModelCommand;
/// improvise-20ek: the record an editor opened on is gone, and a retry must not
/// rename whatever moved under the cursor. Evidence: `context/test-evidence/clients.md`.
#[test]
fn a_retry_after_record_gone_renames_no_other_record() {
use improvise_protocol::{CellId, ModelCommand, Rejection, ServerFrame};
let mut host = three_records_host();
let (a_id, mut a, coordinate) = records_client_at_coordinate(&mut host);
assert_eq!(a.state().grid.cell(0, coordinate).unwrap().text, "North");
let Some(CellId::Coordinate { record: north, .. }) =
a.state().grid.cell(0, coordinate).unwrap().id.clone()
else {
panic!("setup: a coordinate id")
};
press(&mut host, a_id, &mut a, "i");
retype(&mut host, a_id, &mut a, "Zed");
from_another_client(
&mut host,
a_id,
&mut a,
ModelCommand::RenameRecordCoordinate {
record: north,
category: "Region".into(),
item: "Yak".into(),
},
);
let frame = a.key(&BrowserKey::new("Enter")).expect("Enter composes");
let answer = send(&mut host, a_id, &serde_json::to_string(&frame).unwrap());
deliver(&mut a, &answer);
assert!(
answer
.iter()
.any(|f| matches!(f.msg, ServerFrame::Rejected(Rejection::RecordGone(_)))),
"the first Enter is RecordGone: {answer:?}"
);
let row = a.state().view.cursor().selected.0;
assert!(
a.state().view.mode.is_editing()
&& a.state().grid.cell(row, coordinate).unwrap().text != "Yak",
"setup: the editor is back, over a record other than the one renamed away"
);
press(&mut host, a_id, &mut a, "Enter");
assert_eq!(region(&host, "Zed"), None, "the retry renamed a record never opened");
assert_eq!(region(&host, "South"), Some(CellValue::Number(2.0)));
assert_eq!(region(&host, "West"), Some(CellValue::Number(3.0)));
assert_eq!(region(&host, "Yak"), Some(CellValue::Number(1.0)));
}
/// improvise-milr: the rename's destination is a record too, and a write to it
/// this client never saw must refuse the rename. Evidence file.
#[test]
fn a_rename_onto_a_record_another_client_just_wrote_is_refused() {
use improvise_protocol::{ModelCommand, Rejection, ServerFrame};
let mut host = three_records_host();
let (a_id, mut a, coordinate) = records_client_at_coordinate(&mut host);
let revenue = (0..a.state().grid.extent.cols)
.find(|&c| {
a.record_cell_at(1, c)
.is_some_and(|x| x.measure.as_deref() == Some("Revenue"))
})
.expect("South's revenue");
let south = a.record_cell_at(1, revenue).unwrap();
assert_eq!(a.state().grid.cell(1, coordinate).unwrap().text, "South");
press(&mut host, a_id, &mut a, "i");
retype(&mut host, a_id, &mut a, "South");
from_another_client(
&mut host,
a_id,
&mut a,
ModelCommand::SetCell {
cell: south.clone(),
value: CellValue::Number(999.0),
},
);
let frame = a.key(&BrowserKey::new("Enter")).expect("Enter composes");
let answer = send(&mut host, a_id, &serde_json::to_string(&frame).unwrap());
assert!(
answer.iter().any(|f| matches!(
f.msg,
ServerFrame::Rejected(Rejection::RecordMoved(h)) if h == south.record
)),
"the destination moved under this client: {answer:?}"
);
assert_eq!(region(&host, "South"), Some(CellValue::Number(999.0)));
assert_eq!(region(&host, "North"), Some(CellValue::Number(1.0)));
}
/// The control for the test above: a destination this client HAS seen at its
/// current version is overwritten, which is improvise-kndq's policy, unchanged.
#[test]
fn a_rename_onto_a_record_this_client_has_seen_still_overwrites_it() {
let mut host = three_records_host();
let (a_id, mut a, _) = records_client_at_coordinate(&mut host);
press(&mut host, a_id, &mut a, "i");
retype(&mut host, a_id, &mut a, "South");
let frame = a.key(&BrowserKey::new("Enter")).expect("Enter composes");
let answer = send(&mut host, a_id, &serde_json::to_string(&frame).unwrap());
assert!(
answer
.iter()
.any(|f| matches!(f.msg, improvise_protocol::ServerFrame::Accepted)),
"{answer:?}"
);
assert_eq!(region(&host, "South"), Some(CellValue::Number(1.0)), "North's value");
assert_eq!(region(&host, "North"), None);
}