fix: keep object observations fresh after successful calls

This commit is contained in:
2026-09-30 13:44:54 +09:00
parent de82b66de4
commit 2e4196a1ec
4 changed files with 153 additions and 26 deletions
+7 -4
View File
@@ -1261,11 +1261,14 @@ impl Client {
let session_id = context.session.clone();
let maximum = self.limits.history;
let state = self.session_mut(&session_id)?;
if let Some(object) = state.objects.get_mut(&context.object_key) {
object.object.validator = value.validator.clone();
object.revision = object.revision.max(id);
if let Some(object) = state.objects.get_mut(&context.object_key)
&& object.revision <= id
{
if let Some(validator) = value.validator.clone() {
object.object.validator = Some(validator);
}
object.revision = id;
}
state.mark_object_stale(context.object_key, id);
let record = CallRecord {
request_id: id,
context,
+139 -7
View File
@@ -525,14 +525,147 @@ fn call_context_fixes_membership_validators_descriptor_and_result_validation() {
client.interface(&session, "math").unwrap().descriptor,
Some(descriptor(TypeExpr::String))
);
let observation = client.object(&session, "/item").unwrap();
assert_eq!(observation.state, ObservationState::Fresh);
assert_eq!(observation.validator, Some(b"object-v2".to_vec()));
}
#[test]
fn successful_call_without_validator_preserves_observed_validator() {
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", None, b"object-v1"),
);
observe_interface(
&mut client,
&session,
FetchInterfaceResponse {
interface: "math".into(),
descriptor: descriptor(TypeExpr::Integer),
validator: None,
},
);
let call = client
.prepare_call(&session, "/item", "math", "calculate", args(1))
.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(2),
validator: None,
},
wire_limits(),
)
.unwrap(),
)
.unwrap();
let observation = client.object(&session, "/item").unwrap();
assert_eq!(observation.state, ObservationState::Fresh);
assert_eq!(observation.validator, Some(b"object-v1".to_vec()));
let next = client
.prepare_call(&session, "/item", "math", "calculate", args(2))
.unwrap();
assert_eq!(
client.object(&session, "/item").unwrap().state,
ObservationState::Stale
client
.pending_call_context(next.id)
.unwrap()
.request()
.target
.validator,
Some(b"object-v1".to_vec())
);
}
#[test]
fn older_retrieval_cannot_restore_freshness_after_operation_completion() {
fn newer_observation_prevents_older_success_validator_overwrite() {
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", None, b"object-v1"),
);
observe_interface(
&mut client,
&session,
FetchInterfaceResponse {
interface: "math".into(),
descriptor: descriptor(TypeExpr::Integer),
validator: None,
},
);
let call = client
.prepare_call(&session, "/item", "math", "calculate", args(1))
.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();
observe_object(
&mut client,
&session,
"/item",
object("item", None, b"object-v3"),
);
client
.complete(
call.id,
encode_call_operation_response(
&request,
&fixed_descriptor,
&CallOperationResponse {
result: Value::Integer(2),
validator: Some(b"object-v2".to_vec()),
},
wire_limits(),
)
.unwrap(),
)
.unwrap();
let observation = client.object(&session, "/item").unwrap();
assert_eq!(observation.state, ObservationState::Fresh);
assert_eq!(observation.validator, Some(b"object-v3".to_vec()));
}
#[test]
fn older_retrieval_cannot_overwrite_success_validator() {
let mut client = client(8, 8, 8, 8);
let session = client
.open_session("https://example.test/wip", SecurityContext::new("subject"))
@@ -583,10 +716,9 @@ fn older_retrieval_cannot_restore_freshness_after_operation_completion() {
.unwrap(),
)
.unwrap();
assert_eq!(
client.object(&session, "/item").unwrap().state,
ObservationState::Stale
);
let current = client.object(&session, "/item").unwrap();
assert_eq!(current.state, ObservationState::Fresh);
assert_eq!(current.validator, Some(b"object-v2".to_vec()));
client
.complete(