Skip to main content

cloud_sdk_testkit/mock/
local.rs

1use core::{cell::Cell, fmt};
2
3use cloud_sdk::authentication::{AuthenticatedRequest, LocalAsyncAuthenticatedTransport};
4use cloud_sdk::transport::{
5    AsyncResponseStaging, BoundTransport, EndpointIdentity, EndpointIdentityError,
6    LocalAsyncTransport, ResponseCompletion, TransportRequest,
7};
8
9use super::{MockError, MockExchange, MockTransport};
10
11/// Ordered mock transport whose futures are intentionally local-only.
12///
13/// The `Cell` marker makes this type `!Sync`, so futures borrowing it cannot be
14/// sent between threads. It exercises browser, embedded, and single-threaded
15/// executor integrations without adding an allocator or runtime dependency.
16///
17/// ```compile_fail
18/// use cloud_sdk_testkit::LocalMockTransport;
19/// fn require_sync<T: Sync>() {}
20/// require_sync::<LocalMockTransport<'static>>();
21/// ```
22pub struct LocalMockTransport<'a> {
23    inner: MockTransport<'a>,
24    local_marker: Cell<()>,
25}
26
27impl<'a> LocalMockTransport<'a> {
28    /// Creates a local-only mock over an ordered exchange slice.
29    #[must_use]
30    pub const fn new(exchanges: &'a [MockExchange<'a>]) -> Self {
31        Self {
32            inner: MockTransport::new(exchanges),
33            local_marker: Cell::new(()),
34        }
35    }
36
37    /// Binds the mock permanently to one normalized endpoint identity.
38    #[must_use]
39    pub const fn with_endpoint(mut self, endpoint: EndpointIdentity<'a>) -> Self {
40        self.inner = self.inner.with_endpoint(endpoint);
41        self
42    }
43
44    /// Returns the number of exchanges not yet consumed.
45    #[must_use]
46    pub fn remaining(&self) -> usize {
47        self.inner.remaining()
48    }
49
50    /// Reports whether every expected exchange was consumed.
51    #[must_use]
52    pub fn is_complete(&self) -> bool {
53        self.inner.is_complete()
54    }
55}
56
57impl LocalAsyncTransport for LocalMockTransport<'_> {
58    type Error = MockError;
59
60    async fn send_local<'transport, 'request, 'writer, 'buffer>(
61        &'transport self,
62        request: TransportRequest<'request>,
63        mut response: AsyncResponseStaging<'writer, 'buffer>,
64    ) -> Result<ResponseCompletion, Self::Error>
65    where
66        'transport: 'writer,
67        'request: 'writer,
68        'buffer: 'writer,
69    {
70        self.local_marker.get();
71        self.inner.stage_inner(request, &mut response)
72    }
73}
74
75impl LocalAsyncAuthenticatedTransport for LocalMockTransport<'_> {
76    type Error = MockError;
77
78    async fn send_authenticated_local<'transport, 'request, 'policy, 'writer, 'buffer>(
79        &'transport self,
80        request: AuthenticatedRequest<'request, 'policy>,
81        mut response: AsyncResponseStaging<'writer, 'buffer>,
82    ) -> Result<ResponseCompletion, Self::Error>
83    where
84        'transport: 'writer,
85        'request: 'writer,
86        'policy: 'writer,
87        'buffer: 'writer,
88    {
89        self.local_marker.get();
90        self.inner
91            .stage_inner(request.transport_request(), &mut response)
92    }
93}
94
95impl BoundTransport for LocalMockTransport<'_> {
96    fn endpoint_identity(&self) -> Result<EndpointIdentity<'_>, EndpointIdentityError> {
97        self.inner.endpoint_identity()
98    }
99}
100
101impl fmt::Debug for LocalMockTransport<'_> {
102    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
103        formatter
104            .debug_struct("LocalMockTransport")
105            .field("remaining", &self.remaining())
106            .finish_non_exhaustive()
107    }
108}