test(sync): prove deterministic reconciliation
req: sync/022
This commit is contained in:
@@ -2077,6 +2077,90 @@ async fn keep_local_retry_preserves_conflicted_command_and_suffix_order() -> Web
|
||||
result.and(quit)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn identical_sync_inputs_reconcile_deterministically() -> WebDriverResult<()> {
|
||||
// test req: sync/022
|
||||
let app_port = available_port();
|
||||
let app_addr = format!("127.0.0.1:{app_port}");
|
||||
let mut app_command = Command::new(env!("CARGO_BIN_EXE_hemx-kanban-example"));
|
||||
app_command.env("HEMX_KANBAN_ADDR", &app_addr);
|
||||
let _app = TestProcess::start(app_command, "hemx-kanban", &app_addr, STARTUP_TIMEOUT)
|
||||
.expect("start hemx-kanban");
|
||||
|
||||
let webdriver_port = available_port();
|
||||
let webdriver_addr = format!("127.0.0.1:{webdriver_port}");
|
||||
let mut webdriver = Command::new("geckodriver");
|
||||
webdriver.arg("--port").arg(webdriver_port.to_string());
|
||||
let _webdriver = TestProcess::start(webdriver, "geckodriver", &webdriver_addr, STARTUP_TIMEOUT)
|
||||
.expect("start ready geckodriver");
|
||||
let mut caps = DesiredCapabilities::firefox();
|
||||
caps.set_headless()?;
|
||||
let driver = WebDriver::new(&format!("http://{webdriver_addr}"), caps).await?;
|
||||
|
||||
let result = async {
|
||||
driver.goto(&format!("http://{app_addr}/sync-demo")).await?;
|
||||
wait_until(
|
||||
&driver,
|
||||
"return document.querySelector('[data-kanban-sync]')?.hasAttribute('data-sync-database-version')",
|
||||
)
|
||||
.await?;
|
||||
let proof = driver
|
||||
.execute_async(
|
||||
r#"
|
||||
const done = arguments[arguments.length - 1];
|
||||
(async () => {
|
||||
const acceptedResponse = await fetch('/sync/commands?command_id=deterministic%3A1&card_id=1&column=done', { method: 'POST' });
|
||||
const accepted = await acceptedResponse.json();
|
||||
const snapshotResponse = await fetch('/sync/snapshot');
|
||||
const snapshot = await snapshotResponse.json();
|
||||
const commands = [{ id: accepted.commandId, cardId: String(accepted.cardId), kind: 'reorder_card' }];
|
||||
const results = [accepted];
|
||||
const before = JSON.stringify({ snapshot, commands, results });
|
||||
const { reconcileServerAuthoritative } = await import('/sync.js');
|
||||
const first = reconcileServerAuthoritative(snapshot, commands, results);
|
||||
const second = reconcileServerAuthoritative(
|
||||
structuredClone(snapshot),
|
||||
structuredClone(commands),
|
||||
structuredClone(results),
|
||||
);
|
||||
done({
|
||||
acceptedStatus: acceptedResponse.status,
|
||||
snapshotStatus: snapshotResponse.status,
|
||||
first,
|
||||
second,
|
||||
inputsUnchanged: before === JSON.stringify({ snapshot, commands, results }),
|
||||
});
|
||||
})().catch((error) => done({ error: String(error), stack: error?.stack }));
|
||||
"#,
|
||||
Vec::new(),
|
||||
)
|
||||
.await?
|
||||
.json()
|
||||
.clone();
|
||||
assert!(proof["error"].is_null(), "reconciliation failed: {proof}");
|
||||
assert_eq!(proof["acceptedStatus"], 200);
|
||||
assert_eq!(proof["snapshotStatus"], 200);
|
||||
assert_eq!(proof["first"], proof["second"]);
|
||||
assert_eq!(proof["inputsUnchanged"], true);
|
||||
assert_eq!(proof["first"]["model"], "server-authoritative-v1");
|
||||
assert_eq!(proof["first"]["snapshotSequence"], 1);
|
||||
assert_eq!(proof["first"]["serverResultCursor"], 1);
|
||||
assert_eq!(proof["first"]["serverResultCount"], 1);
|
||||
assert_eq!(proof["first"]["commandCount"], 1);
|
||||
assert_eq!(proof["first"]["retainedCommandCount"], 0);
|
||||
assert_eq!(proof["first"]["decision"]["kind"], "converged");
|
||||
assert_eq!(
|
||||
proof["first"]["decision"]["reason"],
|
||||
"intent-already-canonical"
|
||||
);
|
||||
assert_eq!(proof["first"]["decision"]["canonicalColumn"], "done");
|
||||
Ok(())
|
||||
}
|
||||
.await;
|
||||
let quit = driver.quit().await;
|
||||
result.and(quit)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn canonical_acknowledgement_survives_server_restart() -> WebDriverResult<()> {
|
||||
// test req: sync/001 req: sync/005 req: sync/007 req: sync/008 req: sync/013
|
||||
|
||||
Reference in New Issue
Block a user