WIP red: a retry after RecordGone, and a rename's unchecked destination (improvise-20ek, improvise-milr)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user