fix(client-rust): correct outer MxValue data_type and add end-to-end write_array_elements test
This commit is contained in:
@@ -584,8 +584,6 @@ impl Session {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let sparse_value = ProtoMxValue {
|
let sparse_value = ProtoMxValue {
|
||||||
data_type: element_data_type as i32,
|
|
||||||
variant_type: String::new(),
|
|
||||||
kind: Some(Kind::SparseArrayValue(MxSparseArray {
|
kind: Some(Kind::SparseArrayValue(MxSparseArray {
|
||||||
element_data_type: element_data_type as i32,
|
element_data_type: element_data_type as i32,
|
||||||
total_length,
|
total_length,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use tonic::{Request, Response, Status};
|
|||||||
use zb_mom_ww_mxgateway_client::generated::mxaccess_gateway::v1::mx_access_gateway_server::{
|
use zb_mom_ww_mxgateway_client::generated::mxaccess_gateway::v1::mx_access_gateway_server::{
|
||||||
MxAccessGateway, MxAccessGatewayServer,
|
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_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::mx_value::Kind;
|
||||||
use zb_mom_ww_mxgateway_client::generated::mxaccess_gateway::v1::{
|
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,
|
MxEventFamily, MxSparseArray, MxSparseElement, MxStatusCategory, MxStatusProxy, MxStatusSource,
|
||||||
MxValue, OnAlarmTransitionEvent, OpenSessionReply, OpenSessionRequest, ProtocolStatus,
|
MxValue, OnAlarmTransitionEvent, OpenSessionReply, OpenSessionRequest, ProtocolStatus,
|
||||||
ProtocolStatusCode, QueryActiveAlarmsRequest, RegisterReply, SessionState, StreamAlarmsRequest,
|
ProtocolStatusCode, QueryActiveAlarmsRequest, RegisterReply, SessionState, StreamAlarmsRequest,
|
||||||
StreamEventsRequest, SubscribeResult, Write2BulkEntry, WriteBulkEntry, WriteSecured2BulkEntry,
|
StreamEventsRequest, SubscribeResult, Write2BulkEntry, WriteBulkEntry, WriteCommand,
|
||||||
WriteSecuredBulkEntry,
|
WriteSecured2BulkEntry, WriteSecuredBulkEntry,
|
||||||
};
|
};
|
||||||
use zb_mom_ww_mxgateway_client::{
|
use zb_mom_ww_mxgateway_client::{
|
||||||
next_correlation_id, ApiKey, ClientOptions, CommandError, Error, GatewayClient, MxStatus,
|
next_correlation_id, ApiKey, ClientOptions, CommandError, Error, GatewayClient, MxStatus,
|
||||||
@@ -659,6 +660,9 @@ struct FakeState {
|
|||||||
authorization: Mutex<Option<String>>,
|
authorization: Mutex<Option<String>>,
|
||||||
last_command_kind: Mutex<Option<i32>>,
|
last_command_kind: Mutex<Option<i32>>,
|
||||||
last_correlation_id: Mutex<Option<String>>,
|
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>,
|
stream_dropped: Arc<AtomicBool>,
|
||||||
/// Optional per-test override that pins the fake's `Invoke` handler to
|
/// Optional per-test override that pins the fake's `Invoke` handler to
|
||||||
/// a specific reply shape (or `Err(Status)`). The default of `None`
|
/// 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
|
/// Fail the unary call with `Status::unavailable(...)` so the client's
|
||||||
/// `Code::Unavailable` -> `Error::Unavailable` mapping is exercised.
|
/// `Code::Unavailable` -> `Error::Unavailable` mapping is exercised.
|
||||||
Unavailable(String),
|
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)]
|
#[derive(Clone)]
|
||||||
@@ -764,6 +772,23 @@ impl MxAccessGateway for FakeGateway {
|
|||||||
..MxCommandReply::default()
|
..MxCommandReply::default()
|
||||||
})),
|
})),
|
||||||
InvokeOverride::Unavailable(message) => Err(Status::unavailable(message)),
|
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}"))
|
.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
|
// write_array_elements — proto shape unit tests
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user