broker: add stopped-preserving upgrade without actor execution
ci / check (push) Failing after 1m47s
ci / check (push) Failing after 1m47s
This commit is contained in:
@@ -436,6 +436,113 @@ async fn upgrades_test_candidates_keep_old_versions_and_queue_messages_in_order(
|
||||
host.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stopped_preserving_upgrade_installs_without_execution_and_keeps_all_mail() {
|
||||
let world = create_world("stopped-upgrade");
|
||||
let wasm = toolbox_wasm().clone();
|
||||
let (host, mut mailbox) = start(&world).await;
|
||||
let source = passing_source(&world, "initial");
|
||||
spawn_type(&host, &mut mailbox, "stopped-upgrade", &wasm, &source).await;
|
||||
ask_type(&host, &mut mailbox, "stopped-upgrade", json!({"type": "version"}), "version").await;
|
||||
let stopped = request(&host, &mut mailbox, json!({"type": "stop", "actor": "stopped-upgrade"})).await;
|
||||
assert_eq!(stopped.body["type"], "stopped");
|
||||
|
||||
// A distinct valid component: append an empty custom section named x.
|
||||
let mut bytes = std::fs::read(&wasm).unwrap();
|
||||
bytes.extend_from_slice(&[0, 2, 1, b'x']);
|
||||
let new_wasm = world.public_dir("console").join("new.wasm");
|
||||
std::fs::write(&new_wasm, &bytes).unwrap();
|
||||
// This would fail if candidate main/tests were executed.
|
||||
let unrun = source_with_test(&world, "unrun", json!({"steps": [{
|
||||
"send": {"type": "echo"}, "expect": {"type": "must-not-run"}
|
||||
}]}));
|
||||
std::fs::write(unrun.join("new-source"), "version two").unwrap();
|
||||
std::fs::write(world.public_dir("stopped-upgrade").join("keep"), "public state").unwrap();
|
||||
|
||||
let mut ids = Vec::new();
|
||||
for n in 0..sausage::broker::MAILBOX_SIZE {
|
||||
ids.push(host.broker.send("console", Some("stopped-upgrade"), None, None, json!({"type": "ordered", "n": n})));
|
||||
}
|
||||
let upgrade_id = host.broker.send("console", Some("broker"), None, None, json!({
|
||||
"type": "upgrade", "actor": "stopped-upgrade", "wasm": new_wasm.display().to_string(),
|
||||
"source": unrun.display().to_string(), "preserve_stopped": true
|
||||
}));
|
||||
// Do not silently discard unexpected actor mail while awaiting the reply.
|
||||
let upgraded = receive_message(&mut mailbox, Duration::from_secs(10)).await;
|
||||
assert_eq!(upgraded.reply_to.as_deref(), Some(upgrade_id.as_str()));
|
||||
assert_eq!(upgraded.body["type"], "upgraded");
|
||||
assert_eq!(upgraded.body["version"], 2);
|
||||
assert_eq!(upgraded.body["previous"], 1);
|
||||
assert_eq!(upgraded.body["tests"], json!({"skipped": "preserve_stopped"}));
|
||||
let meta = world.read_meta("stopped-upgrade");
|
||||
assert_eq!(meta.version, Some(2));
|
||||
assert!(!meta.running);
|
||||
assert_eq!(std::fs::read(world.wasm_path("stopped-upgrade")).unwrap(), bytes);
|
||||
assert_eq!(std::fs::read(world.version_dir("stopped-upgrade", 2).join("actor.wasm")).unwrap(), bytes);
|
||||
assert_ne!(std::fs::read(world.version_dir("stopped-upgrade", 1).join("actor.wasm")).unwrap(), bytes);
|
||||
assert_eq!(std::fs::read_to_string(world.source_dir("stopped-upgrade").join("new-source")).unwrap(), "version two");
|
||||
assert_eq!(std::fs::read_to_string(world.data_dir("stopped-upgrade").join("main-version")).unwrap(), "1");
|
||||
assert_eq!(std::fs::read_to_string(world.public_dir("stopped-upgrade").join("keep")).unwrap(), "public state");
|
||||
assert!(mailbox.try_recv().is_err());
|
||||
|
||||
// The full mailbox is still full: nothing was consumed or added by the upgrade.
|
||||
let overflow_id = host.broker.send("console", Some("stopped-upgrade"), None, None, json!({"type": "echo"}));
|
||||
let overflow = receive_message(&mut mailbox, Duration::from_secs(10)).await;
|
||||
assert_eq!(overflow.reply_to.as_deref(), Some(overflow_id.as_str()));
|
||||
assert_eq!(overflow.body["type"], "undeliverable");
|
||||
assert!(overflow.body["reason"].as_str().unwrap().contains("full"));
|
||||
|
||||
// Only an explicit start executes v2. Every original envelope survives in order.
|
||||
let start_id = host.broker.send("console", Some("broker"), None, None, json!({"type": "start", "actor": "stopped-upgrade"}));
|
||||
let mut started = false;
|
||||
let mut count = 0;
|
||||
tokio::time::timeout(Duration::from_secs(10), async {
|
||||
while !started || count < ids.len() {
|
||||
let reply = mailbox.recv().await.unwrap();
|
||||
if reply.reply_to.as_deref() == Some(start_id.as_str()) {
|
||||
assert_eq!(reply.body["type"], "started");
|
||||
started = true;
|
||||
} else {
|
||||
assert_eq!(reply.from, "stopped-upgrade");
|
||||
assert_eq!(reply.reply_to.as_deref(), Some(ids[count].as_str()));
|
||||
assert_eq!(reply.body, json!({"type": "ordered", "n": count, "version": "2"}));
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}).await.expect("queued mail was not delivered after start");
|
||||
assert_eq!(std::fs::read_to_string(world.data_dir("stopped-upgrade").join("main-version")).unwrap(), "2");
|
||||
host.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stopped_preserving_option_keeps_running_upgrades_tested_and_default_upgrades_start() {
|
||||
let world = create_world("preserve-running");
|
||||
let wasm = toolbox_wasm().clone();
|
||||
let (host, mut mailbox) = start(&world).await;
|
||||
let passing = passing_source(&world, "passing");
|
||||
let failing = source_with_test(&world, "failing", json!({"steps": [{
|
||||
"send": {"type": "echo"}, "expect": {"type": "wrong"}
|
||||
}]}));
|
||||
spawn_type(&host, &mut mailbox, "running", &wasm, &passing).await;
|
||||
for (option, source, expected) in [(json!("true"), &passing, "error"), (json!(true), &failing, "error"), (json!(true), &passing, "upgraded")] {
|
||||
let reply = request(&host, &mut mailbox, json!({
|
||||
"type": "upgrade", "actor": "running", "wasm": wasm.display().to_string(),
|
||||
"source": source.display().to_string(), "preserve_stopped": option
|
||||
})).await;
|
||||
assert_eq!(reply.body["type"], expected);
|
||||
}
|
||||
assert!(world.read_meta("running").running);
|
||||
let version = ask_type(&host, &mut mailbox, "running", json!({"type": "version"}), "version").await;
|
||||
assert_eq!(version.body["version"], "2");
|
||||
request(&host, &mut mailbox, json!({"type": "stop", "actor": "running"})).await;
|
||||
let default = upgrade_actor(&host, &mut mailbox, "running", &wasm, &passing).await;
|
||||
assert_eq!(default.body["type"], "upgraded");
|
||||
assert!(world.read_meta("running").running);
|
||||
let version = ask_type(&host, &mut mailbox, "running", json!({"type": "version"}), "version").await;
|
||||
assert_eq!(version.body["version"], "3");
|
||||
host.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rolls_back_a_crash_loop_to_the_previous_version_without_restoring_data() {
|
||||
let world = create_world("crash-loop");
|
||||
|
||||
Reference in New Issue
Block a user