From 16617142090f0c2b796df503449f205d4d1d2237 Mon Sep 17 00:00:00 2001 From: Yuanhao Li Date: Thu, 8 Oct 2026 18:57:11 +0200 Subject: [PATCH 1/2] test(rutis): handler-name uniqueness test waits for both rows to fail The test read both rows' failures from the reconcile report, but reconcile settles only rows already starting: a row whose runtime is still coming up counts as settled. On CI the report once held only the Python row's failure. Poll each row's status (up to 60 s) instead. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01T7iq5hpndSiHQcnAsywKuG --- .../yoagent-rutis/tests/languages_test.rs | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/integrations/yoagent-rutis/tests/languages_test.rs b/integrations/yoagent-rutis/tests/languages_test.rs index 1aa56e3..f8370d7 100644 --- a/integrations/yoagent-rutis/tests/languages_test.rs +++ b/integrations/yoagent-rutis/tests/languages_test.rs @@ -20,12 +20,12 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; use common::*; -use rutis::Ctx; +use rutis::{Ctx, FiberState}; use rutis_bridge::runtime::LocalRuntime; use rutis_bridge::session::{host_key, HostDispatch, Reply as RpcReply, Value as RpcValue}; use rutis_loader::{ - Chain, Layer, Loader, LoaderOptions, LoaderPlugin, Patch, RuntimeResolver, RuntimeRowsPlugin, - ServiceCatalog, + Chain, EntryStatus, Layer, Loader, LoaderOptions, LoaderPlugin, Patch, RuntimeResolver, + RuntimeRowsPlugin, ServiceCatalog, }; use serde_json::{json, Value}; use yoagent::extension::RunContext; @@ -912,23 +912,38 @@ async fn handler_names_are_unique_across_languages() { py_row("py", "py_hooks", json!({"name": "taken"})), ]; let patches: Vec = serde_json::from_value(json!([{ "insert": rows }])).unwrap(); - let report = host - .loader + host.loader .reconcile(vec![Layer::new("rows", patches)], None) .await .unwrap(); - // `register` threw in both languages, so both rows failed to load. - let failed: Vec<(&str, &str)> = report - .failures - .iter() - .map(|f| (f.id.as_str(), f.error.as_str())) - .collect(); - assert_eq!(failed.len(), 2, "{report:?}"); + // `register` threw in both languages, so both rows fail to load. Polled, + // not read from the reconcile report: `reconcile` settles the rows already + // starting, and a row whose runtime is still coming up counts as settled + // (seen on CI: the report held only Python's failure). + let failed = tokio::time::timeout(Duration::from_secs(60), async { + loop { + let failed: Vec<(&str, String)> = ["js", "py"] + .into_iter() + .filter_map(|id| match host.loader.get(id)?.status { + EntryStatus::Running(s) if s.state == FiberState::Failed => { + Some((id, s.error.map_or_else(String::new, |e| e.to_string()))) + } + EntryStatus::Unresolved(e) => Some((id, e.to_string())), + _ => None, + }) + .collect(); + if failed.len() == 2 { + return failed; + } + tokio::time::sleep(Duration::from_millis(20)).await; + } + }) + .await + .expect("both rows fail to load"); for (id, error) in failed { - assert!(["js", "py"].contains(&id)); assert!( error.contains("yoagent handler `taken`") && error.contains("already registered"), - "{error}" + "{id}: {error}" ); } let handlers = host.bridge.registry().handlers(); From 7ab62e0ae80ebb654bdca2687075b5f67ce5f32c Mon Sep 17 00:00:00 2001 From: Yuanhao Li Date: Thu, 8 Oct 2026 18:59:40 +0200 Subject: [PATCH 2/2] test(rutis): show both rows' states when the handler-name wait times out Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01T7iq5hpndSiHQcnAsywKuG --- integrations/yoagent-rutis/tests/languages_test.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/integrations/yoagent-rutis/tests/languages_test.rs b/integrations/yoagent-rutis/tests/languages_test.rs index f8370d7..b392bc6 100644 --- a/integrations/yoagent-rutis/tests/languages_test.rs +++ b/integrations/yoagent-rutis/tests/languages_test.rs @@ -939,7 +939,14 @@ async fn handler_names_are_unique_across_languages() { } }) .await - .expect("both rows fail to load"); + .unwrap_or_else(|_| { + let status = |id| host.loader.get(id).map(|e| format!("{:?}", e.status)); + panic!( + "both rows fail to load; js: {:?}, py: {:?}", + status("js"), + status("py") + ) + }); for (id, error) in failed { assert!( error.contains("yoagent handler `taken`") && error.contains("already registered"),