mockforge_core/lib.rs
1//! # MockForge Core
2//!
3//! Core functionality and shared logic for the MockForge mocking framework.
4//!
5//! This crate provides the foundational building blocks used across all MockForge protocols
6//! (HTTP, WebSocket, gRPC, GraphQL). It can be used as a library to programmatically create
7//! and manage mock servers, or to build custom mocking solutions.
8//!
9//! ## Overview
10//!
11//! MockForge Core includes:
12//!
13//! - **Routing & Validation**: OpenAPI-based route registration and request validation
14//! - **Request/Response Processing**: Template expansion, data generation, and transformation
15//! - **Chaos Engineering**: Latency injection, failure simulation, and traffic shaping
16//! - **Proxy & Hybrid Mode**: Forward requests to real backends with intelligent fallback
17//! - **Request Chaining**: Multi-step request workflows with context passing
18//! - **Workspace Management**: Organize and persist mock configurations
19//! - **Observability**: Request logging, metrics collection, and tracing
20//!
21//! ## Quick Start: Embedding MockForge
22//!
23//! ### Creating a Simple HTTP Mock Server
24//!
25//! ```rust,no_run
26//! use mockforge_core::{
27//! Config, LatencyProfile, OpenApiRouteRegistry, OpenApiSpec, Result, ValidationOptions,
28//! };
29//!
30//! #[tokio::main]
31//! async fn main() -> Result<()> {
32//! // Load OpenAPI specification
33//! let spec = OpenApiSpec::from_file("api.json").await?;
34//!
35//! // Create route registry with validation
36//! let registry = OpenApiRouteRegistry::new_with_options(spec, ValidationOptions::default());
37//!
38//! // Configure core features
39//! let config = Config {
40//! latency_enabled: true,
41//! failures_enabled: false,
42//! default_latency: LatencyProfile::with_normal_distribution(400, 120.0),
43//! ..Default::default()
44//! };
45//!
46//! // Build your HTTP server with the registry
47//! // (See mockforge-http crate for router building)
48//!
49//! Ok(())
50//! }
51//! ```
52//!
53//! ### Request Chaining
54//!
55//! Chain multiple requests together with shared context:
56//!
57//! ```rust,no_run
58//! use mockforge_core::{
59//! ChainConfig, ChainDefinition, ChainLink, ChainRequest, RequestChainRegistry, Result,
60//! };
61//! use mockforge_core::request_chaining::RequestBody;
62//! use serde_json::json;
63//! use std::collections::HashMap;
64//!
65//! # async fn example() -> Result<()> {
66//! let registry = RequestChainRegistry::new(ChainConfig::default());
67//!
68//! // Define a chain: create user → add to group → verify membership
69//! let chain = ChainDefinition {
70//! id: "user_onboarding".to_string(),
71//! name: "User Onboarding".to_string(),
72//! description: Some("Create user → add to group".to_string()),
73//! config: ChainConfig {
74//! enabled: true,
75//! ..ChainConfig::default()
76//! },
77//! links: vec![
78//! ChainLink {
79//! request: ChainRequest {
80//! id: "create_user".to_string(),
81//! method: "POST".to_string(),
82//! url: "https://api.example.com/users".to_string(),
83//! headers: HashMap::new(),
84//! body: Some(RequestBody::json(json!({"name": "{{faker.name}}"}))),
85//! depends_on: Vec::new(),
86//! timeout_secs: None,
87//! expected_status: None,
88//! scripting: None,
89//! },
90//! extract: HashMap::from([("user_id".to_string(), "create_user.body.id".to_string())]),
91//! store_as: Some("create_user_response".to_string()),
92//! },
93//! ChainLink {
94//! request: ChainRequest {
95//! id: "add_to_group".to_string(),
96//! method: "POST".to_string(),
97//! url: "https://api.example.com/groups/{{user_id}}/members".to_string(),
98//! headers: HashMap::new(),
99//! body: None,
100//! depends_on: vec!["create_user".to_string()],
101//! timeout_secs: None,
102//! expected_status: None,
103//! scripting: None,
104//! },
105//! extract: HashMap::new(),
106//! store_as: None,
107//! },
108//! ],
109//! variables: HashMap::new(),
110//! tags: vec!["onboarding".to_string()],
111//! };
112//!
113//! registry.store().register_chain(chain).await?;
114//! # Ok(())
115//! # }
116//! ```
117//!
118//! ### Latency & Failure Injection
119//!
120//! Simulate realistic network conditions and errors:
121//!
122//! ```rust,no_run
123//! use mockforge_core::{LatencyProfile, FailureConfig, create_failure_injector};
124//!
125//! // Configure latency simulation
126//! let latency = LatencyProfile::with_normal_distribution(400, 120.0)
127//! .with_min_ms(100)
128//! .with_max_ms(800);
129//!
130//! // Configure failure injection
131//! let failure_config = FailureConfig {
132//! global_error_rate: 0.05, // 5% of requests fail
133//! default_status_codes: vec![500, 502, 503],
134//! ..Default::default()
135//! };
136//!
137//! let injector = create_failure_injector(true, Some(failure_config));
138//! ```
139//!
140//! ## Key Modules
141//!
142//! ### OpenAPI Support
143//! - [`openapi`]: Parse and work with OpenAPI specifications
144//! - [`openapi_routes`]: Register routes from OpenAPI specs with validation
145//! - [`validation`]: Request/response validation against schemas
146//!
147//! ### Request Processing
148//! - [`routing`]: Route matching and registration
149//! - [`templating`]: Template variable expansion ({{uuid}}, {{now}}, etc.)
150//! - [`request_chaining`]: Multi-step request workflows
151//! - [`overrides`]: Dynamic request/response modifications
152//!
153//! ### Chaos Engineering
154//! - [`latency`]: Latency injection with configurable profiles
155//! - [`failure_injection`]: Simulate service failures and errors
156//! - [`traffic_shaping`]: Bandwidth limiting and packet loss
157//!
158//! ### Proxy & Hybrid
159//! - [`proxy`]: Forward requests to upstream services
160//! - [`ws_proxy`]: WebSocket proxy with message transformation
161//!
162//! ### Persistence & Import
163//! - [`workspace`]: Workspace management for organizing mocks
164//! - [`workspace_import`]: Import from Postman, Insomnia, cURL, HAR
165//! - [`record_replay`]: Record real requests and replay as fixtures
166//!
167//! ### Observability
168//! - [`request_logger`]: Centralized request logging
169//! - [`performance`]: Performance metrics and profiling
170//!
171//! ## Feature Flags
172//!
173//! This crate supports several optional features:
174//!
175//! - `openapi`: OpenAPI specification support (enabled by default)
176//! - `validation`: Request/response validation (enabled by default)
177//! - `templating`: Template expansion (enabled by default)
178//! - `chaos`: Chaos engineering features (enabled by default)
179//! - `proxy`: Proxy and hybrid mode (enabled by default)
180//! - `workspace`: Workspace management (enabled by default)
181//!
182//! ## Examples
183//!
184//! See the [examples directory](https://github.com/SaaSy-Solutions/mockforge/tree/main/examples)
185//! for complete working examples.
186//!
187//! ## Related Crates
188//!
189//! - [`mockforge-http`](https://docs.rs/mockforge-http): HTTP/REST mock server
190//! - [`mockforge-grpc`](https://docs.rs/mockforge-grpc): gRPC mock server
191//! - [`mockforge-ws`](https://docs.rs/mockforge-ws): WebSocket mock server
192//! - [`mockforge-graphql`](https://docs.rs/mockforge-graphql): GraphQL mock server
193//! - [`mockforge-plugin-core`](https://docs.rs/mockforge-plugin-core): Plugin development
194//! - [`mockforge-data`](https://docs.rs/mockforge-data): Synthetic data generation
195//!
196//! ## Documentation
197//!
198//! - [MockForge Book](https://docs.mockforge.dev/)
199//! - [API Reference](https://docs.rs/mockforge-core)
200//! - [GitHub Repository](https://github.com/SaaSy-Solutions/mockforge)
201
202#![allow(deprecated)]
203
204pub mod ai_contract_diff;
205pub mod ai_response;
206pub mod cache;
207pub mod chain_execution;
208pub mod chaos_utilities;
209pub mod codegen;
210/// Collection export utilities for exporting mock data in various formats
211pub mod collection_export;
212pub mod conditions;
213pub mod config;
214/// Contract validation for ensuring API contracts match specifications
215pub mod contract_validation;
216/// Contract webhooks for notifying external systems about contract changes
217pub mod contract_webhooks;
218/// Data source abstraction for loading test data from multiple sources
219pub mod data_source;
220/// Docker Compose integration for containerized mock deployments
221pub mod docker_compose;
222pub mod encryption;
223pub mod error;
224pub mod failure_injection;
225pub mod generate_config;
226pub mod generative_schema;
227pub mod git_watch;
228pub mod graph;
229pub mod import;
230pub mod intelligent_behavior;
231pub mod latency;
232pub mod lifecycle;
233pub mod multi_tenant;
234pub mod network_profiles;
235pub mod openapi;
236pub mod openapi_routes;
237pub mod output_control;
238pub mod overrides;
239pub mod performance;
240pub mod priority_handler;
241pub mod protocol_abstraction;
242pub mod proxy;
243pub mod reality;
244pub mod reality_continuum;
245pub mod record_replay;
246pub mod request_capture;
247pub mod request_chaining;
248pub mod request_fingerprint;
249pub mod request_logger;
250pub mod request_scripting;
251pub mod route_chaos;
252pub mod routing;
253pub mod schema_diff;
254pub mod server_utils;
255pub mod spec_parser;
256pub mod stateful_handler;
257pub mod sync_watcher;
258/// Template library system for shared templates, versioning, and marketplace
259pub mod template_library;
260pub mod templating;
261pub mod time_travel;
262pub mod time_travel_handler;
263pub mod traffic_shaping;
264pub mod validation;
265pub mod verification;
266pub mod voice;
267pub mod workspace;
268pub mod workspace_import;
269pub mod workspace_persistence;
270pub mod ws_proxy;
271
272pub use chain_execution::{ChainExecutionEngine, ChainExecutionResult, ChainExecutionStatus};
273pub use chaos_utilities::{ChaosConfig, ChaosEngine, ChaosResult, ChaosStatistics};
274pub use conditions::{evaluate_condition, ConditionContext, ConditionError};
275pub use config::{
276 apply_env_overrides, load_config, load_config_with_fallback, save_config, ApiKeyConfig,
277 AuthConfig, ServerConfig,
278};
279pub use data_source::{
280 DataSource, DataSourceConfig, DataSourceContent, DataSourceFactory, DataSourceManager,
281 DataSourceType, GitDataSource, HttpDataSource, LocalDataSource,
282};
283pub use error::{Error, Result};
284pub use failure_injection::{
285 create_failure_injector, FailureConfig, FailureInjector, TagFailureConfig,
286};
287pub use generate_config::{
288 discover_config_file, load_generate_config, load_generate_config_with_fallback,
289 save_generate_config, BarrelType, GenerateConfig, GenerateOptions, InputConfig, OutputConfig,
290 PluginConfig,
291};
292pub use git_watch::{GitWatchConfig, GitWatchService};
293pub use graph::{
294 builder::GraphBuilder, relationships, ClusterType, EdgeType, GraphCluster, GraphData,
295 GraphEdge, GraphNode, NodeType, Protocol as GraphProtocol,
296};
297pub use latency::LatencyProfile;
298pub use lifecycle::{
299 LifecycleHook, LifecycleHookRegistry, MockLifecycleEvent, RequestContext, ResponseContext,
300 ServerLifecycleEvent,
301};
302pub use multi_tenant::{
303 MultiTenantConfig, MultiTenantWorkspaceRegistry, RoutingStrategy, TenantWorkspace,
304 WorkspaceContext, WorkspaceRouter, WorkspaceStats,
305};
306pub use network_profiles::{NetworkProfile, NetworkProfileCatalog};
307pub use openapi::{
308 OpenApiOperation, OpenApiRoute, OpenApiSchema, OpenApiSecurityRequirement, OpenApiSpec,
309};
310pub use openapi_routes::{
311 create_registry_from_file, create_registry_from_json, OpenApiRouteRegistry, ValidationOptions,
312};
313pub use output_control::{
314 apply_banner, apply_extension, apply_file_naming_template, build_file_naming_context,
315 process_generated_file, BarrelGenerator, FileNamingContext, GeneratedFile,
316};
317pub use overrides::{OverrideMode, OverrideRule, Overrides, PatchOp};
318pub use priority_handler::{
319 MockGenerator, MockResponse, PriorityHttpHandler, PriorityResponse, SimpleMockGenerator,
320};
321pub use protocol_abstraction::{
322 MessagePattern, MiddlewareChain, Protocol, ProtocolMiddleware, ProtocolRequest,
323 ProtocolResponse, RequestMatcher, ResponseStatus, SpecOperation, SpecRegistry,
324 ValidationError as ProtocolValidationError, ValidationResult as ProtocolValidationResult,
325};
326pub use proxy::{ProxyConfig, ProxyHandler, ProxyResponse};
327pub use reality::{PresetMetadata, RealityConfig, RealityEngine, RealityLevel, RealityPreset};
328pub use reality_continuum::{
329 ContinuumConfig, ContinuumRule, MergeStrategy, RealityContinuumEngine, ResponseBlender,
330 TimeSchedule, TransitionCurve, TransitionMode,
331};
332pub use record_replay::{
333 clean_old_fixtures, list_fixtures, list_ready_fixtures, list_smoke_endpoints, RecordHandler,
334 RecordReplayHandler, RecordedRequest, ReplayHandler,
335};
336pub use request_chaining::{
337 ChainConfig, ChainContext, ChainDefinition, ChainExecutionContext, ChainLink, ChainRequest,
338 ChainResponse, ChainStore, ChainTemplatingContext, RequestChainRegistry,
339};
340pub use request_fingerprint::{
341 RequestFingerprint, RequestHandlerResult, ResponsePriority, ResponseSource,
342};
343pub use request_logger::{
344 create_grpc_log_entry, create_http_log_entry, create_websocket_log_entry, get_global_logger,
345 init_global_logger, log_request_global, CentralizedRequestLogger, RequestLogEntry,
346};
347pub use route_chaos::{RouteChaosInjector, RouteFaultResponse, RouteMatcher};
348pub use routing::{HttpMethod, Route, RouteRegistry};
349pub use schema_diff::{to_enhanced_422_json, validation_diff, ValidationError};
350pub use server_utils::errors::{json_error, json_success};
351pub use server_utils::{create_socket_addr, localhost_socket_addr, wildcard_socket_addr};
352pub use spec_parser::{GraphQLValidator, OpenApiValidator, SpecFormat};
353pub use stateful_handler::{
354 ResourceIdExtract, StateInfo, StateResponse, StatefulConfig, StatefulResponse,
355 StatefulResponseHandler, TransitionTrigger,
356};
357pub use sync_watcher::{FileChange, SyncEvent, SyncService, SyncWatcher};
358pub use template_library::{
359 TemplateLibrary, TemplateLibraryEntry, TemplateLibraryManager, TemplateMarketplace,
360 TemplateMetadata, TemplateVersion,
361};
362pub use templating::{expand_str, expand_tokens};
363pub use time_travel::{
364 cron::{CronJob, CronJobAction, CronScheduler},
365 get_global_clock, is_time_travel_enabled, now as time_travel_now, register_global_clock,
366 unregister_global_clock, RepeatConfig, ResponseScheduler, ScheduledResponse, TimeScenario,
367 TimeTravelConfig, TimeTravelManager, TimeTravelStatus, VirtualClock,
368};
369pub use time_travel_handler::{
370 time_travel_middleware, ScheduledResponseWrapper, TimeTravelHandler,
371};
372pub use traffic_shaping::{BandwidthConfig, BurstLossConfig, TrafficShaper, TrafficShapingConfig};
373pub use uuid::Uuid;
374pub use validation::{validate_openapi_operation_security, validate_openapi_security, Validator};
375pub use verification::{
376 matches_verification_pattern, verify_at_least, verify_never, verify_requests, verify_sequence,
377 VerificationCount, VerificationRequest, VerificationResult,
378};
379pub use voice::{
380 ConversationContext, ConversationManager, ConversationState, ParsedCommand, VoiceCommandParser,
381 VoiceSpecGenerator,
382};
383pub use workspace::{EntityId, Folder, MockRequest, Workspace, WorkspaceConfig, WorkspaceRegistry};
384pub use workspace_import::{
385 create_workspace_from_curl, create_workspace_from_har, create_workspace_from_insomnia,
386 create_workspace_from_postman, import_postman_to_existing_workspace,
387 import_postman_to_workspace, WorkspaceImportConfig, WorkspaceImportResult,
388};
389pub use workspace_persistence::WorkspacePersistence;
390pub use ws_proxy::{WsProxyConfig, WsProxyHandler, WsProxyRule};
391// Note: ValidationError and ValidationResult from spec_parser conflict with schema_diff::ValidationError
392// Use qualified paths: spec_parser::ValidationError, spec_parser::ValidationResult
393
394/// Core configuration for MockForge
395#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
396#[serde(default)]
397pub struct Config {
398 /// Enable latency simulation
399 pub latency_enabled: bool,
400 /// Enable failure simulation
401 pub failures_enabled: bool,
402 /// Enable response overrides
403 pub overrides_enabled: bool,
404 /// Enable traffic shaping (bandwidth + burst loss)
405 pub traffic_shaping_enabled: bool,
406 /// Failure injection configuration
407 pub failure_config: Option<FailureConfig>,
408 /// Proxy configuration
409 pub proxy: Option<ProxyConfig>,
410 /// Default latency profile
411 pub default_latency: LatencyProfile,
412 /// Traffic shaping configuration
413 pub traffic_shaping: TrafficShapingConfig,
414 /// Random chaos configuration
415 pub chaos_random: Option<ChaosConfig>,
416 /// Maximum number of request logs to keep in memory (default: 1000)
417 /// Helps prevent unbounded memory growth from request logging
418 pub max_request_logs: usize,
419 /// Time travel configuration for temporal testing
420 pub time_travel: TimeTravelConfig,
421}
422
423/// Default configuration
424impl Default for Config {
425 fn default() -> Self {
426 Self {
427 latency_enabled: true,
428 failures_enabled: false,
429 overrides_enabled: true,
430 traffic_shaping_enabled: false,
431 failure_config: None,
432 proxy: None,
433 default_latency: LatencyProfile::default(),
434 traffic_shaping: TrafficShapingConfig::default(),
435 chaos_random: None,
436 max_request_logs: 1000, // Default: keep last 1000 requests
437 time_travel: TimeTravelConfig::default(),
438 }
439 }
440}
441
442impl Config {
443 /// Create a ChaosEngine from the chaos_random configuration if enabled
444 pub fn create_chaos_engine(&self) -> Option<ChaosEngine> {
445 self.chaos_random.as_ref().map(|config| ChaosEngine::new(config.clone()))
446 }
447
448 /// Check if random chaos mode is enabled
449 pub fn is_chaos_random_enabled(&self) -> bool {
450 self.chaos_random.as_ref().map(|c| c.enabled).unwrap_or(false)
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457
458 #[test]
459 fn test_config_default() {
460 let config = Config::default();
461 assert!(config.latency_enabled);
462 assert!(!config.failures_enabled);
463 assert!(config.overrides_enabled);
464 assert!(!config.traffic_shaping_enabled);
465 assert!(config.failure_config.is_none());
466 assert!(config.proxy.is_none());
467 }
468
469 #[test]
470 fn test_config_serialization() {
471 let config = Config::default();
472 let json = serde_json::to_string(&config).unwrap();
473 assert!(json.contains("latency_enabled"));
474 assert!(json.contains("failures_enabled"));
475 }
476
477 #[test]
478 fn test_config_deserialization() {
479 // Use default config and modify
480 let config = Config {
481 latency_enabled: false,
482 failures_enabled: true,
483 ..Default::default()
484 };
485
486 // Serialize and deserialize
487 let json = serde_json::to_string(&config).unwrap();
488 let deserialized: Config = serde_json::from_str(&json).unwrap();
489
490 assert!(!deserialized.latency_enabled);
491 assert!(deserialized.failures_enabled);
492 assert!(deserialized.overrides_enabled);
493 }
494
495 #[test]
496 fn test_config_with_custom_values() {
497 let config = Config {
498 latency_enabled: false,
499 failures_enabled: true,
500 ..Default::default()
501 };
502
503 assert!(!config.latency_enabled);
504 assert!(config.failures_enabled);
505 }
506}