fix(client): preserve operation observation ownership
This commit is contained in:
@@ -288,6 +288,45 @@ fn normalized_aliases_preserve_newer_request_ownership() {
|
||||
client.object(&session, "/a/shared").unwrap().validator,
|
||||
Some(b"newest".to_vec())
|
||||
);
|
||||
|
||||
let older_tree = client.prepare_tree(&session, "/a", 1, 4).unwrap();
|
||||
let newer_alias = client.prepare_fetch(&session, "/b/shared").unwrap();
|
||||
client
|
||||
.complete(
|
||||
newer_alias.id,
|
||||
fetch_response(
|
||||
"/b/shared",
|
||||
&object("shared", Some("shared-ref"), b"newest"),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
let mut contradictory = object("shared", Some("shared-ref"), b"newest");
|
||||
contradictory.description = Some("contradictory representation".into());
|
||||
let error = client
|
||||
.complete(
|
||||
older_tree.id,
|
||||
encode_fetch_tree_response(
|
||||
&request,
|
||||
&FetchTreeResponse {
|
||||
root: ObjectTreeNode {
|
||||
object: object("a", None, b"root"),
|
||||
children: vec![ObjectTreeNode {
|
||||
object: contradictory,
|
||||
children: vec![],
|
||||
}],
|
||||
},
|
||||
truncated: false,
|
||||
},
|
||||
wire_limits(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(error.kind, ClientErrorKind::InvalidResponse);
|
||||
assert_eq!(
|
||||
client.object(&session, "/b/shared").unwrap().validator,
|
||||
Some(b"newest".to_vec())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -445,6 +484,184 @@ fn call_context_fixes_membership_validators_descriptor_and_result_validation() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn older_retrieval_cannot_restore_freshness_after_operation_completion() {
|
||||
let mut client = client(8, 8, 8, 8);
|
||||
let session = client
|
||||
.open_session("https://example.test/wip", SecurityContext::new("subject"))
|
||||
.unwrap();
|
||||
observe_object(
|
||||
&mut client,
|
||||
&session,
|
||||
"/item",
|
||||
object("item", Some("shared-ref"), b"object-v1"),
|
||||
);
|
||||
observe_interface(
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"interface-v1".to_vec()),
|
||||
},
|
||||
);
|
||||
|
||||
let older_alias = client.prepare_fetch(&session, "/alias/item").unwrap();
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(7))
|
||||
.unwrap();
|
||||
let request = client
|
||||
.pending_call_context(call.id)
|
||||
.unwrap()
|
||||
.request()
|
||||
.clone();
|
||||
let fixed_descriptor = client
|
||||
.pending_call_context(call.id)
|
||||
.unwrap()
|
||||
.descriptor()
|
||||
.clone();
|
||||
client.mark_dispatched(call.id).unwrap();
|
||||
client
|
||||
.complete(
|
||||
call.id,
|
||||
encode_call_operation_response(
|
||||
&request,
|
||||
&fixed_descriptor,
|
||||
&CallOperationResponse {
|
||||
result: Value::Integer(14),
|
||||
validator: Some(b"object-v2".to_vec()),
|
||||
},
|
||||
wire_limits(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client.object(&session, "/item").unwrap().state,
|
||||
ObservationState::Stale
|
||||
);
|
||||
|
||||
client
|
||||
.complete(
|
||||
older_alias.id,
|
||||
fetch_response(
|
||||
"/alias/item",
|
||||
&object("item", Some("shared-ref"), b"object-v1"),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
let alias = client.object(&session, "/alias/item").unwrap();
|
||||
assert_eq!(alias.state, ObservationState::Stale);
|
||||
assert_eq!(alias.validator, Some(b"object-v2".to_vec()));
|
||||
assert_eq!(
|
||||
client
|
||||
.prepare_call(&session, "/alias/item", "math", "calculate", args(1))
|
||||
.unwrap_err()
|
||||
.kind,
|
||||
ClientErrorKind::MissingObservation
|
||||
);
|
||||
|
||||
observe_object(
|
||||
&mut client,
|
||||
&session,
|
||||
"/item",
|
||||
object("item", Some("shared-ref"), b"object-v2"),
|
||||
);
|
||||
let older_alias = client.prepare_fetch(&session, "/second/item").unwrap();
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(8))
|
||||
.unwrap();
|
||||
client.mark_dispatched(call.id).unwrap();
|
||||
let unknown = ProtocolError {
|
||||
code: ProtocolErrorCode::OperationOutcomeUnknown,
|
||||
message: "commit uncertain".into(),
|
||||
};
|
||||
client
|
||||
.complete(
|
||||
call.id,
|
||||
encode_protocol_error_response(
|
||||
ProtocolInteraction::CallOperation,
|
||||
&unknown,
|
||||
wire_limits(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
client
|
||||
.complete(
|
||||
older_alias.id,
|
||||
fetch_response(
|
||||
"/second/item",
|
||||
&object("item", Some("shared-ref"), b"object-v2"),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client.object(&session, "/second/item").unwrap().state,
|
||||
ObservationState::Stale
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_flight_calls_pin_target_identity_until_staleness_is_applied() {
|
||||
let mut client = client(1, 8, 8, 8);
|
||||
let session = client
|
||||
.open_session("https://example.test/wip", SecurityContext::new("subject"))
|
||||
.unwrap();
|
||||
observe_object(
|
||||
&mut client,
|
||||
&session,
|
||||
"/item",
|
||||
object("item", Some("shared-ref"), b"object-v1"),
|
||||
);
|
||||
observe_interface(
|
||||
&mut client,
|
||||
&session,
|
||||
FetchInterfaceResponse {
|
||||
interface: "math".into(),
|
||||
descriptor: descriptor(TypeExpr::Integer),
|
||||
validator: Some(b"interface-v1".to_vec()),
|
||||
},
|
||||
);
|
||||
let call = client
|
||||
.prepare_call(&session, "/item", "math", "calculate", args(7))
|
||||
.unwrap();
|
||||
client.mark_dispatched(call.id).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
client.prepare_fetch(&session, "/other").unwrap_err().kind,
|
||||
ClientErrorKind::Capacity
|
||||
);
|
||||
let unknown = ProtocolError {
|
||||
code: ProtocolErrorCode::OperationOutcomeUnknown,
|
||||
message: "commit uncertain".into(),
|
||||
};
|
||||
client
|
||||
.complete(
|
||||
call.id,
|
||||
encode_protocol_error_response(
|
||||
ProtocolInteraction::CallOperation,
|
||||
&unknown,
|
||||
wire_limits(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client.object(&session, "/item").unwrap().state,
|
||||
ObservationState::Stale
|
||||
);
|
||||
|
||||
observe_object(
|
||||
&mut client,
|
||||
&session,
|
||||
"/other",
|
||||
object("other", None, b"other"),
|
||||
);
|
||||
assert!(client.object(&session, "/item").is_none());
|
||||
assert!(client.object(&session, "/other").is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validator_mismatch_stales_only_its_observation_and_never_retries() {
|
||||
let mut client = client(8, 8, 8, 8);
|
||||
|
||||
Reference in New Issue
Block a user