#![allow(clippy::redundant_pub_crate)]
use crate::error::{Error, Result};
use crate::shared::transport::TransportMessage;
#[cfg_attr(not(any(test, target_arch = "wasm32")), allow(dead_code))]
#[derive(Debug, Clone, Default)]
pub(crate) struct PendingSlot {
slot: Option<TransportMessage>,
}
#[cfg_attr(not(any(test, target_arch = "wasm32")), allow(dead_code))]
impl PendingSlot {
pub(crate) fn new() -> Self {
Self { slot: None }
}
pub(crate) fn put(&mut self, message: TransportMessage) -> Result<()> {
if self.slot.is_some() {
return Err(Error::internal(
"send() called before the prior response was received on HTTP transport",
));
}
self.slot = Some(message);
Ok(())
}
pub(crate) fn take(&mut self) -> Result<TransportMessage> {
self.slot
.take()
.ok_or_else(|| Error::internal("receive() called before send() on HTTP transport"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::jsonrpc::{JSONRPCResponse, ResponsePayload};
use crate::types::RequestId;
use serde_json::json;
fn sample_response(id: i64, value: serde_json::Value) -> TransportMessage {
TransportMessage::Response(JSONRPCResponse {
jsonrpc: "2.0".to_string(),
id: RequestId::from(id),
payload: ResponsePayload::Result(value),
})
}
fn same_message(a: &TransportMessage, b: &TransportMessage) -> bool {
serde_json::to_value(a).unwrap() == serde_json::to_value(b).unwrap()
}
#[test]
fn pending_slot_put_then_take_returns_message() {
let mut slot = PendingSlot::new();
let msg = sample_response(1, json!({"ok": true}));
let expected = sample_response(1, json!({"ok": true}));
assert!(slot.put(msg).is_ok(), "put into an empty slot must succeed");
let taken = slot.take().expect("take after put must return the message");
assert!(
same_message(&taken, &expected),
"take() must return exactly the message that was put"
);
}
#[test]
fn pending_slot_second_take_is_error() {
let mut slot = PendingSlot::new();
slot.put(sample_response(2, json!({"v": 1})))
.expect("first put succeeds");
slot.take().expect("first take returns the message");
assert!(
slot.take().is_err(),
"take() on an empty slot must error (receive before send)"
);
}
#[test]
fn pending_slot_put_on_occupied_is_error() {
let mut slot = PendingSlot::new();
slot.put(sample_response(3, json!({"first": true})))
.expect("first put into empty slot succeeds");
let err = slot.put(sample_response(4, json!({"second": true})));
assert!(
err.is_err(),
"put() into an occupied slot must error (no silent overwrite)"
);
let survived = slot.take().expect("first response still buffered");
assert!(
same_message(&survived, &sample_response(3, json!({"first": true}))),
"the originally-buffered response must survive a rejected double put()"
);
}
#[test]
fn transport_send_stores_and_receive_returns() {
let canned = sample_response(42, json!({"result": "from do_request"}));
let expected = sample_response(42, json!({"result": "from do_request"}));
let mut slot = PendingSlot::new();
slot.put(canned)
.expect("send() buffers the do_request output");
let received = slot
.take()
.expect("receive() returns the buffered response");
assert!(
same_message(&received, &expected),
"receive() must return exactly what send() stored from do_request"
);
}
}