Spawn a Unix-socket HTTP stub that serves the given `responses` in order. Returns: - the socket path (already bound and listening) - a shared log of `"METHOD /path"` strings, one per request received - a join handle that resolves once all expected requests have been served
(
test_name: &str,
responses: Vec<StubResponse>,
)
| 54 | /// - a shared log of `"METHOD /path"` strings, one per request received |
| 55 | /// - a join handle that resolves once all expected requests have been served |
| 56 | pub fn spawn_podman_stub( |
| 57 | test_name: &str, |
| 58 | responses: Vec<StubResponse>, |
| 59 | ) -> ( |
| 60 | PathBuf, |
| 61 | Arc<Mutex<Vec<String>>>, |
| 62 | tokio::task::JoinHandle<()>, |
| 63 | ) { |
| 64 | let socket_path = unique_socket_path(test_name); |
| 65 | let _ = std::fs::remove_file(&socket_path); |
| 66 | let listener = UnixListener::bind(&socket_path).expect("test socket should bind"); |
| 67 | let request_log = Arc::new(Mutex::new(Vec::new())); |
| 68 | let response_queue = Arc::new(Mutex::new(VecDeque::from(responses))); |
| 69 | let expected = response_queue |
| 70 | .lock() |
| 71 | .expect("response queue lock should not be poisoned") |
| 72 | .len(); |
| 73 | let socket_path_for_task = socket_path.clone(); |
| 74 | let log_for_task = request_log.clone(); |
| 75 | let queue_for_task = response_queue; |
| 76 | let handle = tokio::spawn(async move { |
| 77 | for _ in 0..expected { |
| 78 | let (stream, _) = listener.accept().await.expect("test stub should accept"); |
| 79 | let log = log_for_task.clone(); |
| 80 | let queue = queue_for_task.clone(); |
| 81 | let result = http1::Builder::new() |
| 82 | .serve_connection( |
| 83 | TokioIo::new(stream), |
| 84 | service_fn(move |req| { |
| 85 | let log = log.clone(); |
| 86 | let queue = queue.clone(); |
| 87 | async move { |
| 88 | let path = req.uri().path_and_query().map_or_else( |
| 89 | || req.uri().path().to_string(), |
| 90 | |pq| pq.as_str().to_string(), |
| 91 | ); |
| 92 | log.lock() |
| 93 | .expect("request log lock should not be poisoned") |
| 94 | .push(format!("{} {}", req.method(), path)); |
| 95 | let response = queue |
| 96 | .lock() |
| 97 | .expect("response queue lock should not be poisoned") |
| 98 | .pop_front() |
| 99 | .expect("stub response should exist"); |
| 100 | Ok::<_, Infallible>( |
| 101 | hyper::Response::builder() |
| 102 | .status(response.status) |
| 103 | .body(Full::new(Bytes::from(response.body))) |
| 104 | .expect("stub response should build"), |
| 105 | ) |
| 106 | } |
| 107 | }), |
| 108 | ) |
| 109 | .await; |
| 110 | // The one-shot test client can close the Unix socket after the |
| 111 | // response, which Hyper reports as a shutdown error. Let the |
| 112 | // request log assertions below decide whether the stub served |
| 113 | // the expected API calls. |
no test coverage detected