feat: prefetch visible interface descriptors

This commit is contained in:
2026-10-01 08:58:25 +09:00
parent 06edf62d4e
commit 511e9fef9f
7 changed files with 252 additions and 25 deletions
+111
View File
@@ -288,6 +288,117 @@ fn ensure_observed_respects_capacity_and_does_not_loop_on_failure() {
);
}
#[test]
fn ensure_interface_deduplicates_loading_fresh_and_retained_failure() {
let mut client = client(8, 8, 8, 8);
let session = client
.open_session("https://example.test/wip", SecurityContext::new("subject"))
.unwrap();
let request = client
.ensure_interface(&session, "math")
.unwrap()
.expect("missing descriptor starts a fetch");
assert!(client.ensure_interface(&session, "math").unwrap().is_none());
let value = FetchInterfaceResponse {
interface: "math".into(),
descriptor: descriptor(TypeExpr::Integer),
validator: Some(b"math-v1".to_vec()),
};
client
.complete(
request.id,
encode_fetch_interface_response(
&FetchInterfaceRequest {
interface: "math".into(),
},
&value,
wire_limits(),
)
.unwrap(),
)
.unwrap();
assert!(client.ensure_interface(&session, "math").unwrap().is_none());
let failed = client
.ensure_interface(&session, "broken")
.unwrap()
.expect("new descriptor starts a fetch");
assert_eq!(
client
.fail_transport(failed.id, "scripted failure")
.unwrap_err()
.kind,
ClientErrorKind::Transport
);
assert!(
client
.ensure_interface(&session, "broken")
.unwrap()
.is_none(),
"retained interface failures require an explicit refresh"
);
}
#[test]
fn ensure_interface_waits_for_request_and_cache_capacity_without_evicting() {
let mut client = client(8, 1, 1, 8);
let session = client
.open_session("https://example.test/wip", SecurityContext::new("subject"))
.unwrap();
let pending_object = client.refresh_object(&session, "/busy").unwrap();
assert!(
client.ensure_interface(&session, "math").unwrap().is_none(),
"lookahead waits while the in-flight bound is full"
);
client
.complete(
pending_object.id,
observation_response("/busy", &object("busy", None, b"busy")),
)
.unwrap();
let request = client
.ensure_interface(&session, "math")
.unwrap()
.expect("lookahead resumes after capacity becomes available");
let value = FetchInterfaceResponse {
interface: "math".into(),
descriptor: descriptor(TypeExpr::Integer),
validator: None,
};
client
.complete(
request.id,
encode_fetch_interface_response(
&FetchInterfaceRequest {
interface: "math".into(),
},
&value,
wire_limits(),
)
.unwrap(),
)
.unwrap();
assert!(
client
.ensure_interface(&session, "other")
.unwrap()
.is_none(),
"lookahead does not churn a full descriptor cache"
);
assert!(client.interface(&session, "math").is_some());
assert!(client.interface(&session, "other").is_none());
let explicit = client.prepare_interface(&session, "other").unwrap();
assert!(client.interface(&session, "math").is_none());
assert!(matches!(
client.interface(&session, "other").unwrap().state,
ObservationState::Loading(id) if id == explicit.id
));
}
#[test]
fn newer_refresh_rejects_out_of_order_response_for_the_same_subject() {
let mut client = client(8, 8, 8, 8);