1pub mod config;
29pub mod core_banking;
30pub mod engine;
31pub mod mapping_service;
32pub mod models;
33pub mod regulator_endpoint;
34pub mod rulepack_service;
35
36pub use config::*;
37pub use core_banking::CoreBankingSimulator;
38pub use engine::{EngineSimulator, AdminSimulator};
39pub use mapping_service::MappingServiceSimulator;
40pub use regulator_endpoint::RegulatorEndpointSimulator;
41pub use rulepack_service::RulepackServiceSimulator;
42
43use std::collections::HashMap;
44
45use std::sync::Arc;
46use tokio::sync::{oneshot, RwLock};
47
48pub type SimulatorResult<T> = Result<T, SimulatorError>;
54
55#[derive(Debug, Clone)]
57pub enum SimulatorError {
58 BindError(String),
60 StartError(String),
62 ConfigError(String),
64 InternalError(String),
66 NotFound(String),
68 Timeout,
70 ServiceUnavailable,
72}
73
74impl std::fmt::Display for SimulatorError {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 match self {
77 Self::BindError(msg) => write!(f, "Bind error: {}", msg),
78 Self::StartError(msg) => write!(f, "Start error: {}", msg),
79 Self::ConfigError(msg) => write!(f, "Config error: {}", msg),
80 Self::InternalError(msg) => write!(f, "Internal error: {}", msg),
81 Self::NotFound(msg) => write!(f, "Not found: {}", msg),
82 Self::Timeout => write!(f, "Timeout"),
83 Self::ServiceUnavailable => write!(f, "Service unavailable"),
84 }
85 }
86}
87
88impl std::error::Error for SimulatorError {}
89
90#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
92pub struct HealthStatus {
93 pub service: String,
94 pub status: String,
95 pub version: String,
96 pub uptime_secs: u64,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub details: Option<serde_json::Value>,
99}
100
101impl HealthStatus {
102 pub fn healthy(service: &str, version: &str, uptime_secs: u64) -> Self {
103 Self {
104 service: service.to_string(),
105 status: "healthy".to_string(),
106 version: version.to_string(),
107 uptime_secs,
108 details: None,
109 }
110 }
111
112 pub fn unhealthy(service: &str, reason: &str) -> Self {
113 let mut details = serde_json::Map::new();
114 details.insert(
115 "reason".to_string(),
116 serde_json::Value::String(reason.to_string()),
117 );
118 Self {
119 service: service.to_string(),
120 status: "unhealthy".to_string(),
121 version: "unknown".to_string(),
122 uptime_secs: 0,
123 details: Some(serde_json::Value::Object(details)),
124 }
125 }
126
127 pub fn with_details(mut self, key: &str, value: serde_json::Value) -> Self {
128 if self.details.is_none() {
129 self.details = Some(serde_json::Value::Object(serde_json::Map::new()));
130 }
131
132 if let Some(serde_json::Value::Object(map)) = &mut self.details {
133 map.insert(key.to_string(), value);
134 }
135 self
136 }
137}
138
139#[derive(Debug, Clone, Default, serde::Deserialize)]
141pub struct SimulatorStats {
142 pub requests_total: u64,
143 pub requests_success: u64,
144 pub requests_failed: u64,
145 pub requests_timeout: u64,
146 pub bytes_sent: u64,
147 pub bytes_received: u64,
148 pub total_latency_ms: f64,
149 #[serde(skip_serializing_if = "HashMap::is_empty")]
150 pub endpoint_counts: HashMap<String, u64>,
151}
152
153impl serde::Serialize for SimulatorStats {
154 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
155 where
156 S: serde::Serializer,
157 {
158 use serde::ser::SerializeStruct;
159 let mut state = serializer.serialize_struct("SimulatorStats", 8)?;
160 state.serialize_field("requests_total", &self.requests_total)?;
161 state.serialize_field("requests_success", &self.requests_success)?;
162 state.serialize_field("requests_failed", &self.requests_failed)?;
163 state.serialize_field("requests_timeout", &self.requests_timeout)?;
164 state.serialize_field("bytes_sent", &self.bytes_sent)?;
165 state.serialize_field("bytes_received", &self.bytes_received)?;
166 state.serialize_field("avg_latency_ms", &self.avg_latency_ms())?;
167 if !self.endpoint_counts.is_empty() {
168 state.serialize_field("endpoint_counts", &self.endpoint_counts)?;
169 }
170 state.end()
171 }
172}
173
174impl SimulatorStats {
175 pub fn avg_latency_ms(&self) -> f64 {
176 if self.requests_total > 0 {
177 self.total_latency_ms / self.requests_total as f64
178 } else {
179 0.0
180 }
181 }
182
183 pub fn record_request(&mut self, endpoint: &str, success: bool, latency_ms: f64) {
184 self.requests_total += 1;
185 if success {
186 self.requests_success += 1;
187 } else {
188 self.requests_failed += 1;
189 }
190
191 self.total_latency_ms += latency_ms;
192
193 *self.endpoint_counts.entry(endpoint.to_string()).or_insert(0) += 1;
194 }
195
196 pub fn record_timeout(&mut self) {
197 self.requests_total += 1;
198 self.requests_timeout += 1;
199 }
200
201 pub fn record_bytes(&mut self, sent: u64, received: u64) {
202 self.bytes_sent += sent;
203 self.bytes_received += received;
204 }
205}
206
207#[async_trait::async_trait]
213pub trait Simulator: Send + Sync {
214 fn name(&self) -> &str;
216
217 fn port(&self) -> u16;
219
220 fn base_url(&self) -> String {
222 format!("http://127.0.0.1:{}", self.port())
223 }
224
225 async fn health(&self) -> HealthStatus;
227
228 async fn stats(&self) -> SimulatorStats;
230
231 async fn reset_stats(&self);
233
234 async fn is_ready(&self) -> bool {
236 self.health().await.status == "healthy"
237 }
238}
239
240pub struct SimulatorServer {
246 config: SimulatorConfig,
247 core_banking: Option<Arc<CoreBankingSimulator>>,
248 mapping_service: Option<Arc<MappingServiceSimulator>>,
249 rulepack_service: Option<Arc<RulepackServiceSimulator>>,
250 regulator_endpoint: Option<Arc<RegulatorEndpointSimulator>>,
251 shutdown_txs: Vec<oneshot::Sender<()>>,
252 started_at: std::time::Instant,
253}
254
255impl SimulatorServer {
256 pub fn new(config: SimulatorConfig) -> Self {
258 Self {
259 config,
260 core_banking: None,
261 mapping_service: None,
262 rulepack_service: None,
263 regulator_endpoint: None,
264 shutdown_txs: Vec::new(),
265 started_at: std::time::Instant::now(),
266 }
267 }
268
269 pub async fn start(config: SimulatorConfig) -> SimulatorResult<Self> {
271 let mut server = Self::new(config.clone());
272
273 if config.core_banking.enabled {
275 let simulator = CoreBankingSimulator::new(config.core_banking.clone());
276 let (shutdown_tx, shutdown_rx) = oneshot::channel();
277 let sim = Arc::new(simulator);
278 let sim_clone = sim.clone();
279
280 tokio::spawn(async move {
281 if let Err(e) = sim_clone.run(shutdown_rx).await {
282 tracing::error!("Core Banking Simulator error: {}", e);
283 }
284 });
285
286 server.core_banking = Some(sim);
287 server.shutdown_txs.push(shutdown_tx);
288 tracing::info!(
289 "Core Banking Simulator started on port {}",
290 config.core_banking.port
291 );
292 }
293
294 if config.mapping_service.enabled {
296 let simulator = MappingServiceSimulator::new(config.mapping_service.clone());
297 let (shutdown_tx, shutdown_rx) = oneshot::channel();
298 let sim = Arc::new(simulator);
299 let sim_clone = sim.clone();
300
301 tokio::spawn(async move {
302 if let Err(e) = sim_clone.run(shutdown_rx).await {
303 tracing::error!("Mapping Service Simulator error: {}", e);
304 }
305 });
306
307 server.mapping_service = Some(sim);
308 server.shutdown_txs.push(shutdown_tx);
309 tracing::info!(
310 "Mapping Service Simulator started on port {}",
311 config.mapping_service.port
312 );
313 }
314
315 if config.rulepack_service.enabled {
317 let simulator = RulepackServiceSimulator::new(config.rulepack_service.clone());
318 let (shutdown_tx, shutdown_rx) = oneshot::channel();
319 let sim = Arc::new(simulator);
320 let sim_clone = sim.clone();
321
322 tokio::spawn(async move {
323 if let Err(e) = sim_clone.run(shutdown_rx).await {
324 tracing::error!("Rulepack Service Simulator error: {}", e);
325 }
326 });
327
328 server.rulepack_service = Some(sim);
329 server.shutdown_txs.push(shutdown_tx);
330 tracing::info!(
331 "Rulepack Service Simulator started on port {}",
332 config.rulepack_service.port
333 );
334 }
335
336 if config.regulator_endpoint.enabled {
338 let simulator = RegulatorEndpointSimulator::new(config.regulator_endpoint.clone());
339 let (shutdown_tx, shutdown_rx) = oneshot::channel();
340 let sim = Arc::new(simulator);
341 let sim_clone = sim.clone();
342
343 tokio::spawn(async move {
344 if let Err(e) = sim_clone.run(shutdown_rx).await {
345 tracing::error!("Regulator Endpoint Simulator error: {}", e);
346 }
347 });
348
349 server.regulator_endpoint = Some(sim);
350 server.shutdown_txs.push(shutdown_tx);
351 tracing::info!(
352 "Regulator Endpoint Simulator started on port {}",
353 config.regulator_endpoint.port
354 );
355 }
356
357 server.wait_for_ready(std::time::Duration::from_secs(10)).await?;
359
360 Ok(server)
361 }
362
363 pub async fn wait_for_ready(&self, timeout: std::time::Duration) -> SimulatorResult<()> {
365 let start = std::time::Instant::now();
366
367 loop {
368 if start.elapsed() > timeout {
369 return Err(SimulatorError::Timeout);
370 }
371
372 let mut all_ready = true;
373
374 if let Some(ref sim) = self.core_banking {
375 if !sim.is_ready().await {
376 all_ready = false;
377 }
378 }
379 if let Some(ref sim) = self.mapping_service {
380 if !sim.is_ready().await {
381 all_ready = false;
382 }
383 }
384 if let Some(ref sim) = self.rulepack_service {
385 if !sim.is_ready().await {
386 all_ready = false;
387 }
388 }
389 if let Some(ref sim) = self.regulator_endpoint {
390 if !sim.is_ready().await {
391 all_ready = false;
392 }
393 }
394
395 if all_ready {
396 return Ok(());
397 }
398
399 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
400 }
401 }
402
403 pub async fn shutdown(self) {
405 for tx in self.shutdown_txs {
406 let _ = tx.send(());
407 }
408 tracing::info!("All simulators shut down");
409 }
410
411 pub fn core_banking(&self) -> Option<&Arc<CoreBankingSimulator>> {
413 self.core_banking.as_ref()
414 }
415
416 pub fn mapping_service(&self) -> Option<&Arc<MappingServiceSimulator>> {
418 self.mapping_service.as_ref()
419 }
420
421 pub fn rulepack_service(&self) -> Option<&Arc<RulepackServiceSimulator>> {
423 self.rulepack_service.as_ref()
424 }
425
426 pub fn regulator_endpoint(&self) -> Option<&Arc<RegulatorEndpointSimulator>> {
428 self.regulator_endpoint.as_ref()
429 }
430
431 pub fn uptime_secs(&self) -> u64 {
433 self.started_at.elapsed().as_secs()
434 }
435
436 pub async fn combined_stats(&self) -> HashMap<String, SimulatorStats> {
438 let mut stats = HashMap::new();
439
440 if let Some(ref sim) = self.core_banking {
441 stats.insert(sim.name().to_string(), sim.stats().await);
442 }
443 if let Some(ref sim) = self.mapping_service {
444 stats.insert(sim.name().to_string(), sim.stats().await);
445 }
446 if let Some(ref sim) = self.rulepack_service {
447 stats.insert(sim.name().to_string(), sim.stats().await);
448 }
449 if let Some(ref sim) = self.regulator_endpoint {
450 stats.insert(sim.name().to_string(), sim.stats().await);
451 }
452
453 stats
454 }
455
456 pub async fn reset_all_stats(&self) {
458 if let Some(ref sim) = self.core_banking {
459 sim.reset_stats().await;
460 }
461 if let Some(ref sim) = self.mapping_service {
462 sim.reset_stats().await;
463 }
464 if let Some(ref sim) = self.rulepack_service {
465 sim.reset_stats().await;
466 }
467 if let Some(ref sim) = self.regulator_endpoint {
468 sim.reset_stats().await;
469 }
470 }
471
472 pub fn endpoints(&self) -> SimulatorEndpoints {
474 SimulatorEndpoints {
475 core_banking: self.core_banking.as_ref().map(|s| s.base_url()),
476 mapping_service: self.mapping_service.as_ref().map(|s| s.base_url()),
477 rulepack_service: self.rulepack_service.as_ref().map(|s| s.base_url()),
478 regulator_endpoint: self.regulator_endpoint.as_ref().map(|s| s.base_url()),
479 }
480 }
481}
482
483#[derive(Debug, Clone, serde::Serialize)]
485pub struct SimulatorEndpoints {
486 pub core_banking: Option<String>,
487 pub mapping_service: Option<String>,
488 pub rulepack_service: Option<String>,
489 pub regulator_endpoint: Option<String>,
490}
491
492pub async fn start_all_simulators() -> SimulatorResult<SimulatorServer> {
498 let config = SimulatorConfig::default();
499 SimulatorServer::start(config).await
500}
501
502pub async fn start_simulators_on_ports(
504 core_banking_port: u16,
505 mapping_port: u16,
506 rulepack_port: u16,
507 regulator_port: u16,
508) -> SimulatorResult<SimulatorServer> {
509 let mut config = SimulatorConfig::default();
510 config.core_banking.port = core_banking_port;
511 config.mapping_service.port = mapping_port;
512 config.rulepack_service.port = rulepack_port;
513 config.regulator_endpoint.port = regulator_port;
514 SimulatorServer::start(config).await
515}
516
517pub async fn find_available_ports(count: usize) -> Vec<u16> {
519 let mut ports = Vec::with_capacity(count);
520 for _ in 0..count {
521 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
522 let port = listener.local_addr().unwrap().port();
523 ports.push(port);
524 drop(listener);
525 }
526 ports
527}
528
529pub type SharedState<T> = Arc<RwLock<T>>;
535
536pub fn shared_state<T>(value: T) -> SharedState<T> {
538 Arc::new(RwLock::new(value))
539}
540
541#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
547pub struct ApiResponse<T> {
548 pub success: bool,
549 #[serde(skip_serializing_if = "Option::is_none")]
550 pub data: Option<T>,
551 #[serde(skip_serializing_if = "Option::is_none")]
552 pub error: Option<ApiError>,
553 #[serde(skip_serializing_if = "Option::is_none")]
554 pub meta: Option<ResponseMeta>,
555}
556
557impl<T> ApiResponse<T> {
558 pub fn success(data: T) -> Self {
559 Self {
560 success: true,
561 data: Some(data),
562 error: None,
563 meta: None,
564 }
565 }
566
567 pub fn success_with_meta(data: T, meta: ResponseMeta) -> Self {
568 Self {
569 success: true,
570 data: Some(data),
571 error: None,
572 meta: Some(meta),
573 }
574 }
575
576 pub fn error(code: &str, message: &str) -> ApiResponse<T> {
577 ApiResponse {
578 success: false,
579 data: None,
580 error: Some(ApiError {
581 code: code.to_string(),
582 message: message.to_string(),
583 details: None,
584 }),
585 meta: None,
586 }
587 }
588}
589
590#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
592pub struct ApiError {
593 pub code: String,
594 pub message: String,
595 #[serde(skip_serializing_if = "Option::is_none")]
596 pub details: Option<serde_json::Value>,
597}
598
599#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
601pub struct ResponseMeta {
602 #[serde(skip_serializing_if = "Option::is_none")]
603 pub page: Option<u32>,
604 #[serde(skip_serializing_if = "Option::is_none")]
605 pub page_size: Option<u32>,
606 #[serde(skip_serializing_if = "Option::is_none")]
607 pub total_count: Option<u64>,
608 #[serde(skip_serializing_if = "Option::is_none")]
609 pub total_pages: Option<u32>,
610 #[serde(skip_serializing_if = "Option::is_none")]
611 pub processing_time_ms: Option<u64>,
612 #[serde(flatten, skip_serializing_if = "Option::is_none")]
613 pub extra: Option<HashMap<String, serde_json::Value>>,
614}
615
616impl ResponseMeta {
617 pub fn paginated(page: u32, page_size: u32, total_count: u64) -> Self {
618 let total_pages = if page_size == 0 {
619 0
620 } else {
621 ((total_count + page_size as u64 - 1) / page_size as u64) as u32
622 };
623 Self {
624 page: Some(page),
625 page_size: Some(page_size),
626 total_count: Some(total_count),
627 total_pages: Some(total_pages),
628 processing_time_ms: None,
629 extra: None,
630 }
631 }
632
633 pub fn with_timing(mut self, ms: u64) -> Self {
634 self.processing_time_ms = Some(ms);
635 self
636 }
637
638 pub fn with_extra(mut self, key: &str, value: serde_json::Value) -> Self {
639 if self.extra.is_none() {
640 self.extra = Some(HashMap::new());
641 }
642 if let Some(map) = &mut self.extra {
643 map.insert(key.to_string(), value);
644 }
645 self
646 }
647}
648
649#[cfg(test)]
654mod tests {
655 use super::*;
656
657 #[test]
658 fn test_health_status_healthy() {
659 let status = HealthStatus::healthy("test-service", "1.0.0", 100);
660 assert_eq!(status.status, "healthy");
661 assert_eq!(status.service, "test-service");
662 assert_eq!(status.version, "1.0.0");
663 assert_eq!(status.uptime_secs, 100);
664 }
665
666 #[test]
667 fn test_health_status_unhealthy() {
668 let status = HealthStatus::unhealthy("test-service", "connection failed");
669 assert_eq!(status.status, "unhealthy");
670 assert!(status.details.is_some());
671 }
672
673 #[test]
674 fn test_simulator_stats_record() {
675 let mut stats = SimulatorStats::default();
676 stats.record_request("/api/test", true, 10.0);
677 stats.record_request("/api/test", true, 20.0);
678 stats.record_request("/api/other", false, 30.0);
679
680 assert_eq!(stats.requests_total, 3);
681 assert_eq!(stats.requests_success, 2);
682 assert_eq!(stats.requests_failed, 1);
683 assert_eq!(stats.endpoint_counts.get("/api/test"), Some(&2));
684 assert_eq!(stats.endpoint_counts.get("/api/other"), Some(&1));
685 }
686
687 #[test]
688 fn test_api_response_success() {
689 let response: ApiResponse<String> = ApiResponse::success("test data".to_string());
690 assert!(response.success);
691 assert_eq!(response.data, Some("test data".to_string()));
692 assert!(response.error.is_none());
693 }
694
695 #[test]
696 fn test_api_response_error() {
697 let response: ApiResponse<String> = ApiResponse::error("ERR001", "Something went wrong");
698 assert!(!response.success);
699 assert!(response.data.is_none());
700 assert!(response.error.is_some());
701 assert_eq!(response.error.as_ref().unwrap().code, "ERR001");
702 }
703
704 #[test]
705 fn test_response_meta_paginated() {
706 let meta = ResponseMeta::paginated(1, 10, 95);
707 assert_eq!(meta.page, Some(1));
708 assert_eq!(meta.page_size, Some(10));
709 assert_eq!(meta.total_count, Some(95));
710 assert_eq!(meta.total_pages, Some(10)); }
712}