//! 测试共享的 mock transport:录制请求并回放录制的 SSE 流。 //! Shared test mock transport: records requests and replays recorded SSE //! streams. //! //! 回放时把流按固定小尺寸切块,刻意让 SSE 事件跨 chunk 边界, //! 以验证 provider 的增量解析。 //! Replays are split into small fixed-size chunks to deliberately straddle //! SSE event boundaries, exercising the providers' incremental parsing. use focus_transport::{ChunkStream, HttpRequest, HttpResponse, Transport, TransportError}; use std::collections::{HashMap, VecDeque}; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::time::Duration; /// 录制请求并回放响应体的 mock transport。 /// A mock transport that records requests and replays response bodies. #[derive(Debug, Clone, Default)] pub struct MockTransport { /// 待回放的响应体(每个消费一个);流式接口按小块回放。 /// Bodies to replay (one consumed per request); the streaming interface /// replays them in small chunks. streams: Arc>>>, /// 待返回的传输层错误(每个消费一个,优先于 streams)。 /// Transport errors to return (one consumed per request; take precedence). errors: Arc>>, /// 已发出的请求记录。 /// Requests sent so far. requests: Arc>>, } impl MockTransport { /// 创建一个空的 mock transport。 /// Create an empty mock transport. pub fn new() -> Self { Self::default() } /// 加入一条待回放的响应体。 /// Queue a response body to replay. pub fn push_body(&mut self, body: impl Into>) { self.streams.lock().unwrap().push_back(body.into()); } /// 加入一个待返回的传输错误。 /// Queue a transport error to return. #[allow(dead_code)] pub fn push_error(&mut self, err: TransportError) { self.errors.lock().unwrap().push_back(err); } /// 最近一次请求的克隆。 /// Clone of the most recent request. #[allow(dead_code)] pub fn last_request(&self) -> HttpRequest { self.requests .lock() .unwrap() .last() .cloned() .expect("no request recorded") } /// 已记录的请求数量。 /// Number of recorded requests. #[allow(dead_code)] pub fn request_count(&self) -> usize { self.requests.lock().unwrap().len() } } impl Transport for MockTransport { fn request<'a>( &'a self, req: &'a HttpRequest, ) -> Pin< Box< dyn std::future::Future> + Send + 'a, >, > { Box::pin(async move { self.requests.lock().unwrap().push(req.clone()); if let Some(err) = self.errors.lock().unwrap().pop_front() { return Err(err); } let body = self.streams.lock().unwrap().pop_front().unwrap_or_default(); Ok(HttpResponse { status: 200, headers: HashMap::new(), body, }) }) } fn stream_request<'a>( &'a self, req: &'a HttpRequest, _timeout: Duration, ) -> Pin< Box< dyn std::future::Future< Output = focus_transport::TransportResult>, > + Send + 'a, >, > { Box::pin(async move { self.requests.lock().unwrap().push(req.clone()); if let Some(err) = self.errors.lock().unwrap().pop_front() { return Err(err); } let body = self.streams.lock().unwrap().pop_front().unwrap_or_default(); Ok(Box::new(ReplayChunkStream { data: body, pos: 0, chunk_size: 7, }) as Box) }) } } /// 按固定小尺寸逐块回放的 chunk 流。 /// A chunk stream that replays its data in small fixed-size chunks. struct ReplayChunkStream { data: Vec, pos: usize, chunk_size: usize, } impl ChunkStream for ReplayChunkStream { fn next_chunk<'a>( &'a mut self, ) -> Pin< Box< dyn std::future::Future>>> + Send + 'a, >, > { Box::pin(async move { if self.pos >= self.data.len() { Ok(None) } else { let end = (self.pos + self.chunk_size).min(self.data.len()); let out = self.data[self.pos..end].to_vec(); self.pos = end; Ok(Some(out)) } }) } } /// 把一个 provider 的全部流事件收集成 Vec。 /// Collect all stream events from a provider into a Vec. #[allow(dead_code)] pub fn collect( provider: &dyn focus_core::provider::StreamProvider, request: &focus_core::provider::ProviderRequest, ) -> Vec { let mut iter = provider .stream(request) .expect("provider.stream must not return Err for a well-formed request"); let mut out = Vec::new(); while let Some(ev) = iter.next_event() { out.push(ev); } out }