Skip to main content

rightsize_modules/
wiremock.rs

1//! A WireMock server container for stubbing HTTP dependencies in integration tests.
2//!
3//! ### Readiness — verified against a real 3.13.2 boot
4//!
5//! WireMock 3.x ships a dedicated `/__admin/health` endpoint (unlike some older 2.x
6//! builds, where `/__admin/mappings` was the only reliable 200). Verified directly
7//! against a real container:
8//!
9//! ```text
10//! $ curl http://127.0.0.1:<port>/__admin/health
11//! {"status":"healthy","message":"Wiremock is ok","version":"3.13.2","uptimeInSeconds":9,...}
12//! ```
13//!
14//! so this module waits on that endpoint rather than falling back to
15//! `/__admin/mappings`.
16//!
17//! No control characters were found in the image's baked env (checked via
18//! `docker image inspect`), and no `with_memory_limit` override was
19//! needed — the JVM boots comfortably on msb's default ~450M microVM RAM (observed
20//! ~5.5s IT round-trip on msb; a small embedded-Jetty app, not a JVM-heavy cluster
21//! like Pinot — no memory-ladder escalation was needed).
22//!
23//! This is the Rust ecosystem's missing piece too: there is no first-class
24//! in-process WireMock story for Rust integration tests, so this module fills a
25//! real gap.
26
27use rightsize::{Container, ContainerGuard, Result, Wait};
28
29const PORT: u16 = 8080;
30
31/// A WireMock stub-server container.
32pub struct WireMockContainer(Container);
33
34impl WireMockContainer {
35    /// Builds a container from the pinned default image (`wiremock/wiremock:3.13.2`).
36    pub fn new() -> Self {
37        Self::with_image("wiremock/wiremock:3.13.2")
38    }
39
40    /// Builds a container from a caller-chosen image.
41    pub fn with_image(image: &str) -> Self {
42        Self(
43            Container::new(image)
44                .with_exposed_ports(&[PORT])
45                .waiting_for(Wait::for_http("/__admin/health").for_port(PORT)),
46        )
47    }
48
49    /// Boots the container.
50    pub async fn start(self) -> Result<WireMockGuard> {
51        crate::register_default_backends();
52        Ok(WireMockGuard(self.0.start().await?))
53    }
54}
55
56impl Default for WireMockContainer {
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62/// The running guard for a [`WireMockContainer`].
63pub struct WireMockGuard(ContainerGuard);
64
65impl WireMockGuard {
66    /// The stub server's base URI (mount stubbed paths under this).
67    pub fn base_url(&self) -> String {
68        format!(
69            "http://{}:{}",
70            self.0.host(),
71            self.0.get_mapped_port(PORT).unwrap()
72        )
73    }
74
75    /// The `/__admin` management API's base URI (stub CRUD, request journal, health).
76    pub fn admin_url(&self) -> String {
77        format!("{}/__admin", self.base_url())
78    }
79
80    /// Stops and removes the container, releasing its host port.
81    pub async fn stop(self) -> Result<()> {
82        self.0.stop().await
83    }
84}
85
86impl std::ops::Deref for WireMockGuard {
87    type Target = ContainerGuard;
88    fn deref(&self) -> &ContainerGuard {
89        &self.0
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn with_image_smoke() {
99        let _ = WireMockContainer::new();
100        let _ = WireMockContainer::with_image("wiremock/wiremock:3.13.2");
101    }
102}