fix(ci): close capability races and green audit
Some checks failed
CI / rust-skia (Rust only) (push) Successful in 2m51s
CI / required (push) Failing after 28m9s

This commit is contained in:
2026-08-13 09:43:42 +00:00
parent 6f67568f47
commit 1ea44e5e42
14 changed files with 224 additions and 229 deletions

View File

@@ -8,10 +8,11 @@ use libremetaverse::{
use libremetaverse_compat_tests::block_on;
use libremetaverse_types::UUID;
use libremetaverse_types::compat::{
CancellationToken, CancellationTokenSource, HttpMessageHandler, HttpRequest, HttpResponse, Uri,
CancellationTokenSource, HttpMessageHandler, HttpRequest, HttpResponse, Uri,
};
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, mpsc};
use std::time::Duration;
const CAP: &str = "http://test.invalid/request-task-inventory";
@@ -35,8 +36,11 @@ fn client_with_cap(response: HttpResponse, expose_capability: bool) -> (GridClie
} else {
"<llsd><map></map></llsd>".into()
};
let release_seed = Arc::new(AtomicBool::new(false));
let handler_release_seed = Arc::clone(&release_seed);
let handler = HttpMessageHandler::new(move |request, _| {
let result = if request.uri.0 == SEED {
let is_seed = request.uri.0 == SEED;
let result = if is_seed {
HttpResponse {
status_code: 200,
headers: BTreeMap::new(),
@@ -52,7 +56,15 @@ fn client_with_cap(response: HttpResponse, expose_capability: bool) -> (GridClie
body: Vec::new(),
})
};
async move { result }
let handler_release_seed = Arc::clone(&handler_release_seed);
async move {
if is_seed {
while !handler_release_seed.load(Ordering::Acquire) {
tokio::task::yield_now().await;
}
}
result
}
});
let mut client = GridClient::new().expect("GridClient constructor");
client.set_http_caps_client(HttpCapsClient::new(handler.clone()).unwrap());
@@ -69,6 +81,15 @@ fn client_with_cap(response: HttpResponse, expose_capability: bool) -> (GridClie
simulator
.set_seed_caps(Some(Uri(SEED.into())), Some(true))
.unwrap();
let caps = simulator.clone().caps.expect("seed capability client");
let (seed_complete, completion) = mpsc::channel();
let _subscription = caps.subscribe_capabilities_received(Some(Arc::new(move |_| {
seed_complete.send(()).unwrap();
})));
release_seed.store(true, Ordering::Release);
completion
.recv_timeout(Duration::from_secs(2))
.expect("seed capability request completed");
let mut network = client.network();
network.set_current_sim(Some(simulator));
client.set_network(network);
@@ -116,16 +137,6 @@ fn item_json(
)
}
fn cancellation_after(duration: Duration) -> CancellationToken {
let source = Arc::new(CancellationTokenSource::new());
let token = source.token();
std::thread::spawn(move || {
std::thread::sleep(duration);
source.cancel();
});
token
}
fn single<T: 'static>(entries: &[Box<dyn InventoryObjectClass>]) -> &T {
let mut matching = entries
.iter()
@@ -212,13 +223,22 @@ fn non_success_status_returns_contents_folder_only() {
#[test]
fn no_capability_does_not_make_http_request() {
let (client, recording) = client_with_cap(response(404, String::new()), false);
let result = block_on(client.inventory().get_task_inventory(
uuid(),
12_345,
None,
Some(cancellation_after(Duration::from_millis(50))),
))
.expect("GetTaskInventoryAsync");
let cancellation = CancellationTokenSource::new();
let token = cancellation.token();
let inventory = client.inventory();
let (result, ()) = block_on(async move {
tokio::join!(
inventory.get_task_inventory(uuid(), 12_345, None, Some(token)),
async move {
// `join!` polls the inventory branch before this branch can
// resume after yielding, so cancellation releases the legacy
// callback wait instead of racing seed discovery or entry.
tokio::task::yield_now().await;
cancellation.cancel();
}
)
});
let result = result.expect("GetTaskInventoryAsync");
assert!(result.is_empty());
assert!(recording.requests().is_empty());
}