Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 24 additions & 11 deletions platform/host-js/tests/application_services/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1536,7 +1536,7 @@ async fn javascript_forwarding_uses_exact_local_imports_original_evidence_and_ac
.unwrap();
}
#[tokio::test]
async fn javascript_catch_cannot_acknowledge_child_cleanup_or_release_parent_and_child_capacity() {
async fn javascript_caught_child_cleanup_retains_custody_without_poisoning_the_caller() {
tokio::time::timeout(Duration::from_secs(15), async {
let (provider, consumer) = attached_packages();
let wrapper = script_forwarder(Realm::Dedicated);
Expand Down Expand Up @@ -1575,7 +1575,7 @@ async fn javascript_catch_cannot_acknowledge_child_cleanup_or_release_parent_and
assert_eq!(
client
.start(
binding,
binding.clone(),
"echo".into(),
json!("fail-cleanup-error"),
scope(&issuer, true),
Expand All @@ -1591,21 +1591,34 @@ async fn javascript_catch_cannot_acknowledge_child_cleanup_or_release_parent_and
assert!(weak.upgrade().is_some());
let snapshot = client.snapshot();
assert!(snapshot.cleanup_error.unwrap().contains("unconfirmed"));
for instance in [id(1), id(4)] {
for (instance, failed) in [(id(1), true), (id(4), false)] {
let state = snapshot
.instances
.iter()
.find(|s| s.activation.instance() == &instance)
.unwrap();
assert_eq!(
state.calls, 1,
"both actual child and parent keep their core work"
);
assert_eq!(state.phase, Phase::Retiring);
assert_eq!(state.failures.is_empty(), !failed);
}
// Use a real binding captured before retirement: input preparation runs
// before final admission, so this proves both actual 32 MiB charges remain.
let (attachments, rejected) = bytes(5);
let (attachments, released) = bytes(5);
assert_eq!(
client
.start(
binding,
"echo".into(),
json!("healthy"),
scope(&issuer, true),
attachments.into(),
)
.await
.unwrap()
.result()
.await
.unwrap(),
json!("xxxxx")
);
assert!(released.upgrade().is_none());
// The failed leaf still holds half of the 64 MiB Host window.
let (attachments, rejected) = bytes(32 * 1024 * 1024 + 1);
assert!(
client
.start(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ async fn failed_resource_mutation_keeps_original_snapshot_and_capacity_after_bus
let completed=client.mutate(prepared,recorded).await.unwrap().completion().await;
assert!(completed.cleanup.is_err());
assert!(matches!(completed.reply.receipt,Receipt::Recorded {outcome:OperationOutcome::Returned {ref value},cleanup:OperationCleanup::Failed {..}} if value==&json!("xxxxx")));
assert!(original_scope.finish().await.is_err());assert!(weak.upgrade().is_some());
original_scope.finish().await.unwrap();assert!(weak.upgrade().is_some());
let (resources,rejected)=bytes(1);
let failure=client.prepare_mutation(binding,"echo".into(),json!("public"),scope(&issuer,true),Uuid::new_v4(),resources.into()).await.err().unwrap();
assert!(failure.contains("budget"));assert!(rejected.upgrade().is_none());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ async fn failed_captured_cleanup_retains_all_payload_capacity_without_a_completi
permit(&f.started).await;
drop(accepted);
f.release.add_permits(1);
assert!(source.finish().await.is_err());
source.finish().await.unwrap();
let next = scope(&f.issuer, true);
assert!(
f.client
Expand Down
32 changes: 2 additions & 30 deletions platform/transport/tests/integration/native_requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ use serde_json::{Value, json};
use std::{
collections::BTreeMap,
sync::{Arc, Mutex},
time::Duration,
};
use tokio_util::sync::CancellationToken;
use uuid::Uuid;
Expand Down Expand Up @@ -534,28 +533,9 @@ async fn native_requests_preserve_unknown_receipts_and_independent_failed_cleanu
cleanup: maka_plugin_core::operation::OperationCleanup::Failed { .. }
}
));
assert!(
job.finish()
.await
.unwrap_err()
.contains("cleanup unconfirmed")
);
job.finish().await.unwrap();
assert!(app.shutdown().await.is_err());
let deadline = std::time::Instant::now() + Duration::from_secs(3);
let snapshot = loop {
let snapshot = app.client().snapshot();
if snapshot.instances.iter().any(|status| {
status.activation.instance() == &support::identity(13)
&& !status.failures.is_empty()
}) {
break snapshot;
}
assert!(
std::time::Instant::now() < deadline,
"native owner close did not publish original request cleanup failure"
);
tokio::task::yield_now().await;
};
let snapshot = app.client().snapshot();
assert!(!snapshot.closed);
assert!(snapshot.cleanup_error.is_some());
assert_eq!(effects.load(std::sync::atomic::Ordering::SeqCst), 1);
Expand All @@ -566,14 +546,6 @@ async fn native_requests_preserve_unknown_receipts_and_independent_failed_cleanu
"retained negative cleanup fixture Root: {}",
retained.display()
);
assert!(
snapshot
.instances
.iter()
.filter(|status| status.activation.instance() == &support::identity(13))
.any(|status| status.resources > 0 && !status.failures.is_empty()),
"original native caller retains its failed owner resource fence"
);
assert!(
snapshot
.instances
Expand Down
39 changes: 33 additions & 6 deletions products/maka/cli/tests/integration/candidate/crash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,16 @@ use crate::terminal::support::{ModelRequest, configure, mutate};
use maka_app_client::{Client as AppClient, Journal};
use maka_host_runtime::application::host::startup;
use serde_json::json;
use std::sync::{Arc, atomic::AtomicUsize};
use tokio::{net::TcpListener, sync::mpsc};
use std::{
os::unix::process::ExitStatusExt,
process::Stdio,
sync::{Arc, atomic::AtomicUsize},
};
use tokio::{
io::{AsyncBufReadExt, BufReader},
net::TcpListener,
sync::mpsc,
};

async fn activity(client: &AppClient, session: &str) -> Value {
client
Expand Down Expand Up @@ -60,7 +68,23 @@ async fn killed_host_leaves_an_ended_turn_and_a_session_that_takes_messages() {
}
});
tokio::time::timeout(Duration::from_secs(90), async {
let host = startup::connect_root(executable, &root).await.unwrap();
let mut first_host = tokio::process::Command::new(executable)
.args(["host", "serve", "--root"])
.arg(&root)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.kill_on_drop(true)
.spawn()
.unwrap();
let mut ready = String::new();
BufReader::new(first_host.stdout.take().unwrap())
.read_line(&mut ready).await.unwrap();
let ready: Value = serde_json::from_str(&ready).unwrap();
let first_epoch = uuid::Uuid::parse_str(ready["hostEpoch"].as_str().unwrap()).unwrap();
let host = Client::connect(
Path::new(ready["socketPath"].as_str().unwrap()), Some(first_epoch),
).await.unwrap();
let client = AppClient::new(
host,
Journal::open(&journal).await.unwrap(),
Expand All @@ -77,13 +101,16 @@ async fn killed_host_leaves_an_ended_turn_and_a_session_that_takes_messages() {
let held = requests.recv().await.unwrap();
assert_ne!(activity(&client, &session).await, "idle");

let pid = discovery::read(&root).unwrap().pid.get();
assert_eq!(unsafe { libc::kill(pid as libc::pid_t, libc::SIGKILL) }, 0);
assert_eq!(discovery::read(&root).unwrap().pid.get(), first_host.id().unwrap());
first_host.start_kill().unwrap();
assert_eq!(first_host.wait().await.unwrap().signal(), Some(libc::SIGKILL));
drop(held);
drop(client);

fixture.begin_admission();
let host = startup::connect_root(executable, &root).await.unwrap();
assert_ne!(discovery::read(&root).unwrap().pid.get(), pid);
fixture.adopt();
assert_ne!(host.epoch(), first_epoch);
let client = AppClient::new(
host,
Journal::open(&journal).await.unwrap(),
Expand Down
Loading