fix(client-rust): correct outer MxValue data_type and add end-to-end write_array_elements test

This commit is contained in:
Joseph Doherty
2026-06-18 03:14:14 -04:00
parent 474b7bd0ff
commit 72cf2f4091
2 changed files with 98 additions and 4 deletions
+98 -2
View File
@@ -17,6 +17,7 @@ use tonic::{Request, Response, Status};
use zb_mom_ww_mxgateway_client::generated::mxaccess_gateway::v1::mx_access_gateway_server::{
MxAccessGateway, MxAccessGatewayServer,
};
use zb_mom_ww_mxgateway_client::generated::mxaccess_gateway::v1::mx_command;
use zb_mom_ww_mxgateway_client::generated::mxaccess_gateway::v1::mx_command_reply;
use zb_mom_ww_mxgateway_client::generated::mxaccess_gateway::v1::mx_value::Kind;
use zb_mom_ww_mxgateway_client::generated::mxaccess_gateway::v1::{
@@ -27,8 +28,8 @@ use zb_mom_ww_mxgateway_client::generated::mxaccess_gateway::v1::{
MxEventFamily, MxSparseArray, MxSparseElement, MxStatusCategory, MxStatusProxy, MxStatusSource,
MxValue, OnAlarmTransitionEvent, OpenSessionReply, OpenSessionRequest, ProtocolStatus,
ProtocolStatusCode, QueryActiveAlarmsRequest, RegisterReply, SessionState, StreamAlarmsRequest,
StreamEventsRequest, SubscribeResult, Write2BulkEntry, WriteBulkEntry, WriteSecured2BulkEntry,
WriteSecuredBulkEntry,
StreamEventsRequest, SubscribeResult, Write2BulkEntry, WriteBulkEntry, WriteCommand,
WriteSecured2BulkEntry, WriteSecuredBulkEntry,
};
use zb_mom_ww_mxgateway_client::{
next_correlation_id, ApiKey, ClientOptions, CommandError, Error, GatewayClient, MxStatus,
@@ -659,6 +660,9 @@ struct FakeState {
authorization: Mutex<Option<String>>,
last_command_kind: Mutex<Option<i32>>,
last_correlation_id: Mutex<Option<String>>,
/// Captures the last `WriteCommand` payload received, populated when the
/// `WriteOk` override is active. Used by `write_array_elements` e2e test.
last_write_command: Mutex<Option<WriteCommand>>,
stream_dropped: Arc<AtomicBool>,
/// Optional per-test override that pins the fake's `Invoke` handler to
/// a specific reply shape (or `Err(Status)`). The default of `None`
@@ -683,6 +687,10 @@ enum InvokeOverride {
/// Fail the unary call with `Status::unavailable(...)` so the client's
/// `Code::Unavailable` -> `Error::Unavailable` mapping is exercised.
Unavailable(String),
/// Accept a `Write` command (return `protocol_status = Ok`, no payload)
/// and capture the decoded `WriteCommand` in
/// `FakeState::last_write_command` for inspection.
WriteOk,
}
#[derive(Clone)]
@@ -764,6 +772,23 @@ impl MxAccessGateway for FakeGateway {
..MxCommandReply::default()
})),
InvokeOverride::Unavailable(message) => Err(Status::unavailable(message)),
InvokeOverride::WriteOk => {
// Extract and capture the WriteCommand payload so the test
// can assert on server_handle, item_handle, user_id, and value.
if let Some(mx_command::Payload::Write(write_cmd)) =
request.command.and_then(|c| c.payload)
{
*self.state.last_write_command.lock().await = Some(write_cmd);
}
Ok(Response::new(MxCommandReply {
session_id: request.session_id,
correlation_id: "fake-correlation".to_owned(),
kind,
protocol_status: Some(ok_status("write ok")),
payload: None,
..MxCommandReply::default()
}))
}
};
}
@@ -1092,6 +1117,77 @@ fn case_by_id<'a>(cases: &'a [Value], id: &str) -> &'a Value {
.unwrap_or_else(|| panic!("missing fixture case {id}"))
}
// ---------------------------------------------------------------------------
// write_array_elements — end-to-end fake-server test
// ---------------------------------------------------------------------------
#[tokio::test]
async fn write_array_elements_routes_sparse_array_write_through_fake_gateway() {
// Arrange: stand up the fake gateway with WriteOk so the Write command
// succeeds and the sent WriteCommand is captured for inspection.
let state = Arc::new(FakeState::default());
*state.invoke_override.lock().await = Some(InvokeOverride::WriteOk);
let endpoint = spawn_fake_gateway(state.clone()).await;
let client = GatewayClient::connect(ClientOptions::new(endpoint))
.await
.unwrap();
let session = client.session("session-fixture");
// Act: call the public write_array_elements helper.
session
.write_array_elements(
12,
34,
MxDataType::Integer,
10,
[(2u32, ClientMxValue::int32(42))],
7,
)
.await
.unwrap();
// Assert: the fake captured a Write command with the expected handles and
// a SparseArrayValue whose total_length and element index/value are correct.
let captured = state
.last_write_command
.lock()
.await
.take()
.expect("fake should have captured a WriteCommand");
assert_eq!(captured.server_handle, 12, "server_handle must round-trip");
assert_eq!(captured.item_handle, 34, "item_handle must round-trip");
assert_eq!(captured.user_id, 7, "user_id must round-trip");
let value = captured.value.expect("WriteCommand must carry a value");
assert_eq!(
value.data_type, 0,
"outer MxValue.data_type must be Unspecified (0), not the element type"
);
let Kind::SparseArrayValue(ref sparse) = value.kind.as_ref().unwrap() else {
panic!(
"expected SparseArrayValue kind on the outer MxValue, got {:?}",
value.kind
);
};
assert_eq!(
sparse.element_data_type,
MxDataType::Integer as i32,
"element_data_type must carry the element type"
);
assert_eq!(sparse.total_length, 10, "total_length must round-trip");
assert_eq!(sparse.elements.len(), 1, "one element supplied");
let elem = &sparse.elements[0];
assert_eq!(elem.index, 2, "element index must round-trip");
assert_eq!(
elem.value.as_ref().unwrap().kind,
Some(Kind::Int32Value(42)),
"element value must round-trip"
);
}
// ---------------------------------------------------------------------------
// write_array_elements — proto shape unit tests
// ---------------------------------------------------------------------------