Skip to main content

mockforge_runtime_daemon/
lib.rs

1//! # MockForge Runtime Daemon
2//!
3//! Zero-config mode runtime daemon that automatically creates mocks from 404 responses.
4//!
5//! This crate provides the "invisible mock server" experience - when a user hits an endpoint
6//! that doesn't exist, the daemon automatically:
7//! - Creates a mock endpoint
8//! - Generates a type
9//! - Generates a client stub
10//! - Adds to OpenAPI schema
11//! - Adds an example response
12//! - Sets up a scenario
13//!
14//! This is "mock server in your shadow" — an AI-assisted backend-on-demand.
15
16pub mod auto_generator;
17pub mod config;
18pub mod detector;
19
20pub use auto_generator::AutoGenerator;
21pub use config::RuntimeDaemonConfig;
22pub use detector::NotFoundDetector;
23
24/// Runtime daemon for auto-creating mocks from 404s
25pub struct RuntimeDaemon {
26    /// Configuration for the daemon
27    config: RuntimeDaemonConfig,
28}
29
30impl RuntimeDaemon {
31    /// Create a new runtime daemon with the given configuration
32    pub fn new(config: RuntimeDaemonConfig) -> Self {
33        Self { config }
34    }
35
36    /// Check if the daemon is enabled
37    pub fn is_enabled(&self) -> bool {
38        self.config.enabled
39    }
40
41    /// Get the configuration
42    pub fn config(&self) -> &RuntimeDaemonConfig {
43        &self.config
44    }
45
46    /// Opt-in: spawn a [`mockforge_scenarios::FederationScenarioPoller`]
47    /// using the standard `MOCKFORGE_FEDERATION_POLL_*` env vars.
48    ///
49    /// Returns `Ok(None)` when the env vars aren't configured (the 99%
50    /// zero-config case — the daemon stays pure 404-generation). Returns
51    /// `Ok(Some(handle))` when a poller spawns; drop the handle to stop it.
52    ///
53    /// The default [`mockforge_scenarios::LoggingApplicator`] only logs
54    /// observed overrides. A real embedder wanting to push them to the
55    /// daemon's auto-generator (e.g. bumping chaos level based on a
56    /// scenario) should call [`mockforge_scenarios::spawn_federation_scenario_poller`]
57    /// directly with a custom `ScenarioApplicator` impl.
58    ///
59    /// # Errors
60    ///
61    /// Returns an error string when env vars are malformed (bad UUID,
62    /// non-numeric interval). Missing vars are not an error.
63    pub fn spawn_federation_scenario_poller(
64        &self,
65    ) -> Result<Option<tokio::task::JoinHandle<()>>, String> {
66        mockforge_scenarios::spawn_federation_scenario_poller(
67            mockforge_scenarios::LoggingApplicator,
68        )
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_daemon_creation() {
78        let config = RuntimeDaemonConfig::default();
79        let daemon = RuntimeDaemon::new(config);
80        assert!(!daemon.is_enabled()); // Default should be disabled
81    }
82
83    #[tokio::test]
84    async fn spawn_federation_scenario_poller_returns_none_when_env_missing() {
85        // Clearing env would race with parallel tests, so rely on the fact
86        // that these vars are never set under `cargo test` in CI. If this
87        // test flakes, gate it behind a `serial_test` annotation.
88        let daemon = RuntimeDaemon::new(RuntimeDaemonConfig::default());
89        let handle = daemon.spawn_federation_scenario_poller().unwrap();
90        assert!(handle.is_none(), "expected None when MOCKFORGE_FEDERATION_POLL_URL unset");
91    }
92}