feat: migrate protocol and HTTP to structured interface references
T-20: identify interfaces by exact scope/name pairs; add optional opaque scope_ref metadata and separate shape validation from path-dependent scope compatibility. Preserve call error precedence, strict JSON, validators and transport boundaries. Mechanically migrate consumers and document API/wire compatibility and deferred lifecycle semantics. Validation: cargo fmt --all --check; git diff --check; cargo check --workspace --all-targets; cargo test -p wip-protocol -p wip-http; cargo test --workspace (221 passing tests including doctests); cargo test --workspace --all-targets; nix develop --command cargo clippy --workspace --all-targets -- -D warnings; RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps. All pass. cargo/rustc 1.95.0; development-shell Clippy/rustc 1.97.1.
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
fn root_reference(name: &str) -> wip_protocol::InterfaceReference {
|
||||
wip_protocol::InterfaceReference {
|
||||
scope: "/".into(),
|
||||
name: name.into(),
|
||||
}
|
||||
}
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use wip_client::{
|
||||
@@ -65,7 +72,7 @@ fn object(name: &str, reference: Option<&str>, validator: &[u8]) -> Object {
|
||||
Object {
|
||||
name: name.into(),
|
||||
description: Some(format!("object {name}")),
|
||||
interfaces: vec!["math".into()],
|
||||
interfaces: vec![root_reference("math")],
|
||||
r#ref: reference.map(str::to_owned),
|
||||
validator: Some(validator.to_vec()),
|
||||
}
|
||||
@@ -296,12 +303,18 @@ fn ensure_interface_deduplicates_loading_fresh_and_retained_failure() {
|
||||
.unwrap();
|
||||
|
||||
let request = client
|
||||
.ensure_interface(&session, "math")
|
||||
.ensure_interface(&session, root_reference("math"))
|
||||
.unwrap()
|
||||
.expect("missing descriptor starts a fetch");
|
||||
assert!(client.ensure_interface(&session, "math").unwrap().is_none());
|
||||
assert!(
|
||||
client
|
||||
.ensure_interface(&session, root_reference("math"))
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
let value = FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"math-v1".to_vec()),
|
||||
};
|
||||
@@ -310,7 +323,7 @@ fn ensure_interface_deduplicates_loading_fresh_and_retained_failure() {
|
||||
request.id,
|
||||
encode_fetch_interface_response(
|
||||
&FetchInterfaceRequest {
|
||||
interface: "math".into(),
|
||||
interface: root_reference("math"),
|
||||
},
|
||||
&value,
|
||||
wire_limits(),
|
||||
@@ -318,10 +331,15 @@ fn ensure_interface_deduplicates_loading_fresh_and_retained_failure() {
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(client.ensure_interface(&session, "math").unwrap().is_none());
|
||||
assert!(
|
||||
client
|
||||
.ensure_interface(&session, root_reference("math"))
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
|
||||
let failed = client
|
||||
.ensure_interface(&session, "broken")
|
||||
.ensure_interface(&session, root_reference("broken"))
|
||||
.unwrap()
|
||||
.expect("new descriptor starts a fetch");
|
||||
assert_eq!(
|
||||
@@ -333,7 +351,7 @@ fn ensure_interface_deduplicates_loading_fresh_and_retained_failure() {
|
||||
);
|
||||
assert!(
|
||||
client
|
||||
.ensure_interface(&session, "broken")
|
||||
.ensure_interface(&session, root_reference("broken"))
|
||||
.unwrap()
|
||||
.is_none(),
|
||||
"retained interface failures require an explicit refresh"
|
||||
@@ -348,7 +366,10 @@ fn ensure_interface_waits_for_request_and_cache_capacity_without_evicting() {
|
||||
.unwrap();
|
||||
let pending_object = client.refresh_object(&session, "/busy").unwrap();
|
||||
assert!(
|
||||
client.ensure_interface(&session, "math").unwrap().is_none(),
|
||||
client
|
||||
.ensure_interface(&session, root_reference("math"))
|
||||
.unwrap()
|
||||
.is_none(),
|
||||
"lookahead waits while the in-flight bound is full"
|
||||
);
|
||||
client
|
||||
@@ -359,11 +380,12 @@ fn ensure_interface_waits_for_request_and_cache_capacity_without_evicting() {
|
||||
.unwrap();
|
||||
|
||||
let request = client
|
||||
.ensure_interface(&session, "math")
|
||||
.ensure_interface(&session, root_reference("math"))
|
||||
.unwrap()
|
||||
.expect("lookahead resumes after capacity becomes available");
|
||||
let value = FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: None,
|
||||
};
|
||||
@@ -372,7 +394,7 @@ fn ensure_interface_waits_for_request_and_cache_capacity_without_evicting() {
|
||||
request.id,
|
||||
encode_fetch_interface_response(
|
||||
&FetchInterfaceRequest {
|
||||
interface: "math".into(),
|
||||
interface: root_reference("math"),
|
||||
},
|
||||
&value,
|
||||
wire_limits(),
|
||||
@@ -383,18 +405,32 @@ fn ensure_interface_waits_for_request_and_cache_capacity_without_evicting() {
|
||||
|
||||
assert!(
|
||||
client
|
||||
.ensure_interface(&session, "other")
|
||||
.ensure_interface(&session, root_reference("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());
|
||||
assert!(
|
||||
client
|
||||
.interface(&session, &root_reference("math"))
|
||||
.is_some()
|
||||
);
|
||||
assert!(
|
||||
client
|
||||
.interface(&session, &root_reference("other"))
|
||||
.is_none()
|
||||
);
|
||||
|
||||
let explicit = client.prepare_interface(&session, "other").unwrap();
|
||||
assert!(client.interface(&session, "math").is_none());
|
||||
let explicit = client
|
||||
.prepare_interface(&session, root_reference("other"))
|
||||
.unwrap();
|
||||
assert!(
|
||||
client
|
||||
.interface(&session, &root_reference("math"))
|
||||
.is_none()
|
||||
);
|
||||
assert!(matches!(
|
||||
client.interface(&session, "other").unwrap().state,
|
||||
client.interface(&session, &root_reference("other")).unwrap().state,
|
||||
ObservationState::Loading(id) if id == explicit.id
|
||||
));
|
||||
}
|
||||
@@ -408,7 +444,9 @@ fn independent_retrievals_run_concurrently_and_complete_out_of_order() {
|
||||
|
||||
let first = client.refresh_observed(&session, "/one", 0).unwrap();
|
||||
let second = client.refresh_observed(&session, "/two", 0).unwrap();
|
||||
let interface = client.prepare_interface(&session, "math").unwrap();
|
||||
let interface = client
|
||||
.prepare_interface(&session, root_reference("math"))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(first.request.uri().path(), "/wip/v1/observe");
|
||||
assert_eq!(second.request.uri().path(), "/wip/v1/observe");
|
||||
@@ -426,10 +464,11 @@ fn independent_retrievals_run_concurrently_and_complete_out_of_order() {
|
||||
interface.id,
|
||||
encode_fetch_interface_response(
|
||||
&FetchInterfaceRequest {
|
||||
interface: "math".into(),
|
||||
interface: root_reference("math"),
|
||||
},
|
||||
&FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"math-v1".to_vec()),
|
||||
},
|
||||
@@ -454,7 +493,10 @@ fn independent_retrievals_run_concurrently_and_complete_out_of_order() {
|
||||
ObservationState::Fresh
|
||||
);
|
||||
assert_eq!(
|
||||
client.interface(&session, "math").unwrap().state,
|
||||
client
|
||||
.interface(&session, &root_reference("math"))
|
||||
.unwrap()
|
||||
.state,
|
||||
ObservationState::Fresh
|
||||
);
|
||||
assert_eq!(client.in_flight_len(), 0);
|
||||
@@ -941,14 +983,21 @@ fn call_context_fixes_membership_validators_descriptor_and_result_validation() {
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"interface-v1".to_vec()),
|
||||
},
|
||||
);
|
||||
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(7))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(7),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client
|
||||
@@ -965,7 +1014,8 @@ fn call_context_fixes_membership_validators_descriptor_and_result_validation() {
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::String),
|
||||
validator: Some(b"interface-v2".to_vec()),
|
||||
},
|
||||
@@ -981,7 +1031,8 @@ fn call_context_fixes_membership_validators_descriptor_and_result_validation() {
|
||||
validator: Some(b"object-v1".to_vec()),
|
||||
},
|
||||
interface: wip_protocol::InterfaceTarget {
|
||||
reference: "math".into(),
|
||||
scope_ref: None,
|
||||
reference: root_reference("math"),
|
||||
validator: Some(b"interface-v1".to_vec()),
|
||||
},
|
||||
operation: "calculate".into(),
|
||||
@@ -1015,7 +1066,10 @@ fn call_context_fixes_membership_validators_descriptor_and_result_validation() {
|
||||
);
|
||||
assert!(matches!(history_context.outcome, CallOutcome::Success(_)));
|
||||
assert_eq!(
|
||||
client.interface(&session, "math").unwrap().descriptor,
|
||||
client
|
||||
.interface(&session, &root_reference("math"))
|
||||
.unwrap()
|
||||
.descriptor,
|
||||
Some(descriptor(TypeExpr::String))
|
||||
);
|
||||
let observation = client.object(&session, "/item").unwrap();
|
||||
@@ -1039,14 +1093,21 @@ fn successful_call_without_validator_preserves_observed_validator() {
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: None,
|
||||
},
|
||||
);
|
||||
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(1))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(1),
|
||||
)
|
||||
.unwrap();
|
||||
let request = client
|
||||
.pending_call_context(call.id)
|
||||
@@ -1080,7 +1141,13 @@ fn successful_call_without_validator_preserves_observed_validator() {
|
||||
assert_eq!(observation.validator, Some(b"object-v1".to_vec()));
|
||||
|
||||
let next = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(2))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(2),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client
|
||||
@@ -1109,14 +1176,21 @@ fn newer_observation_prevents_older_success_validator_overwrite() {
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: None,
|
||||
},
|
||||
);
|
||||
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(1))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(1),
|
||||
)
|
||||
.unwrap();
|
||||
let request = client
|
||||
.pending_call_context(call.id)
|
||||
@@ -1173,7 +1247,8 @@ fn older_retrieval_cannot_overwrite_success_validator() {
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"interface-v1".to_vec()),
|
||||
},
|
||||
@@ -1181,7 +1256,13 @@ fn older_retrieval_cannot_overwrite_success_validator() {
|
||||
|
||||
let older_alias = client.refresh_observed(&session, "/alias/item", 0).unwrap();
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(7))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(7),
|
||||
)
|
||||
.unwrap();
|
||||
let request = client
|
||||
.pending_call_context(call.id)
|
||||
@@ -1227,7 +1308,13 @@ fn older_retrieval_cannot_overwrite_success_validator() {
|
||||
assert_eq!(alias.validator, Some(b"object-v2".to_vec()));
|
||||
assert_eq!(
|
||||
client
|
||||
.prepare_call(&session, "/alias/item", "math", "calculate", args(1))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/alias/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(1)
|
||||
)
|
||||
.unwrap_err()
|
||||
.kind,
|
||||
ClientErrorKind::MissingObservation
|
||||
@@ -1243,7 +1330,13 @@ fn older_retrieval_cannot_overwrite_success_validator() {
|
||||
.refresh_observed(&session, "/second/item", 0)
|
||||
.unwrap();
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(8))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(8),
|
||||
)
|
||||
.unwrap();
|
||||
client.mark_dispatched(call.id).unwrap();
|
||||
let unknown = ProtocolError {
|
||||
@@ -1292,13 +1385,20 @@ fn in_flight_calls_pin_target_identity_until_staleness_is_applied() {
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"interface-v1".to_vec()),
|
||||
},
|
||||
);
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(7))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(7),
|
||||
)
|
||||
.unwrap();
|
||||
client.mark_dispatched(call.id).unwrap();
|
||||
|
||||
@@ -1350,13 +1450,20 @@ fn validator_mismatch_stales_only_its_observation_and_never_retries() {
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"i1".to_vec()),
|
||||
},
|
||||
);
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(1))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(1),
|
||||
)
|
||||
.unwrap();
|
||||
client.mark_dispatched(call.id).unwrap();
|
||||
let error = ProtocolError {
|
||||
@@ -1369,7 +1476,10 @@ fn validator_mismatch_stales_only_its_observation_and_never_retries() {
|
||||
client.complete(call.id, response).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
client.interface(&session, "math").unwrap().state,
|
||||
client
|
||||
.interface(&session, &root_reference("math"))
|
||||
.unwrap()
|
||||
.state,
|
||||
ObservationState::Stale
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -1384,7 +1494,8 @@ fn validator_mismatch_stales_only_its_observation_and_never_retries() {
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"i2".to_vec()),
|
||||
},
|
||||
@@ -1404,14 +1515,21 @@ fn protocol_and_transport_unknown_outcomes_stale_without_retry() {
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: None,
|
||||
},
|
||||
);
|
||||
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(1))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(1),
|
||||
)
|
||||
.unwrap();
|
||||
client.mark_dispatched(call.id).unwrap();
|
||||
let unknown = ProtocolError {
|
||||
@@ -1447,7 +1565,13 @@ fn protocol_and_transport_unknown_outcomes_stale_without_retry() {
|
||||
|
||||
observe_object(&mut client, &session, "/item", object("item", None, b"v2"));
|
||||
let second = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(2))
|
||||
.prepare_call(
|
||||
&session,
|
||||
"/item",
|
||||
&root_reference("math"),
|
||||
"calculate",
|
||||
args(2),
|
||||
)
|
||||
.unwrap();
|
||||
client.mark_dispatched(second.id).unwrap();
|
||||
let completion = client
|
||||
@@ -1695,8 +1819,12 @@ fn superseded_transport_failure_does_not_poison_newer_refresh() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let old = client.prepare_interface(&session, "math").unwrap();
|
||||
let newer = client.prepare_interface(&session, "math").unwrap();
|
||||
let old = client
|
||||
.prepare_interface(&session, root_reference("math"))
|
||||
.unwrap();
|
||||
let newer = client
|
||||
.prepare_interface(&session, root_reference("math"))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client
|
||||
.fail_transport(old.id, "old connection failed")
|
||||
@@ -1704,11 +1832,12 @@ fn superseded_transport_failure_does_not_poison_newer_refresh() {
|
||||
Completion::StaleResponseRejected(old.id)
|
||||
);
|
||||
assert!(matches!(
|
||||
client.interface(&session, "math").unwrap().state,
|
||||
client.interface(&session, &root_reference("math")).unwrap().state,
|
||||
ObservationState::Loading(id) if id == newer.id
|
||||
));
|
||||
let value = FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"current".to_vec()),
|
||||
};
|
||||
@@ -1717,7 +1846,7 @@ fn superseded_transport_failure_does_not_poison_newer_refresh() {
|
||||
newer.id,
|
||||
encode_fetch_interface_response(
|
||||
&FetchInterfaceRequest {
|
||||
interface: "math".into(),
|
||||
interface: root_reference("math"),
|
||||
},
|
||||
&value,
|
||||
wire_limits(),
|
||||
@@ -1737,15 +1866,19 @@ fn validator_free_interface_observation_remains_immutable() {
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: None,
|
||||
},
|
||||
);
|
||||
|
||||
let refresh = client.prepare_interface(&session, "math").unwrap();
|
||||
let refresh = client
|
||||
.prepare_interface(&session, root_reference("math"))
|
||||
.unwrap();
|
||||
let contradictory = FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
scope_ref: None,
|
||||
interface: root_reference("math"),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"became-mutable".to_vec()),
|
||||
};
|
||||
@@ -1754,7 +1887,7 @@ fn validator_free_interface_observation_remains_immutable() {
|
||||
refresh.id,
|
||||
encode_fetch_interface_response(
|
||||
&FetchInterfaceRequest {
|
||||
interface: "math".into(),
|
||||
interface: root_reference("math"),
|
||||
},
|
||||
&contradictory,
|
||||
wire_limits(),
|
||||
@@ -1763,7 +1896,13 @@ fn validator_free_interface_observation_remains_immutable() {
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(error.kind, ClientErrorKind::InvalidResponse);
|
||||
assert_eq!(client.interface(&session, "math").unwrap().validator, None);
|
||||
assert_eq!(
|
||||
client
|
||||
.interface(&session, &root_reference("math"))
|
||||
.unwrap()
|
||||
.validator,
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1876,3 +2015,64 @@ fn in_flight_and_history_are_bounded() {
|
||||
.unwrap_err();
|
||||
assert_eq!(client.diagnostics(&session).unwrap().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structured_interface_keys_membership_and_scope_ref_are_preserved() {
|
||||
let mut client = client(8, 8, 8, 8);
|
||||
let session = client
|
||||
.open_session("https://example.test/wip", SecurityContext::new("subject"))
|
||||
.unwrap();
|
||||
let root = root_reference("same#::操作");
|
||||
let local = wip_protocol::InterfaceReference {
|
||||
scope: "/item".into(),
|
||||
name: root.name.clone(),
|
||||
};
|
||||
let mut value = object("item", None, b"object-v1");
|
||||
value.interfaces = vec![local.clone()];
|
||||
observe_object(&mut client, &session, "/item", value);
|
||||
for (reference, scope_ref) in [
|
||||
(root.clone(), None),
|
||||
(local.clone(), Some("scope-identity".into())),
|
||||
] {
|
||||
observe_interface(
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: reference,
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
scope_ref,
|
||||
validator: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
assert!(client.interface(&session, &root).is_some());
|
||||
assert_eq!(
|
||||
client
|
||||
.interface(&session, &local)
|
||||
.unwrap()
|
||||
.scope_ref
|
||||
.as_deref(),
|
||||
Some("scope-identity")
|
||||
);
|
||||
assert_eq!(
|
||||
client
|
||||
.prepare_call(&session, "/item", &root, "calculate", args(1))
|
||||
.unwrap_err()
|
||||
.kind,
|
||||
ClientErrorKind::InvalidRequest
|
||||
);
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", &local, "calculate", args(1))
|
||||
.unwrap();
|
||||
let context = client.pending_call_context(call.id).unwrap();
|
||||
assert_eq!(context.request().interface.reference, local);
|
||||
assert_eq!(
|
||||
context.request().interface.scope_ref.as_deref(),
|
||||
Some("scope-identity")
|
||||
);
|
||||
assert_eq!(
|
||||
context.interface().scope_ref.as_deref(),
|
||||
Some("scope-identity")
|
||||
);
|
||||
assert_eq!(client.interface(&session, &root).unwrap().scope_ref, None);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user