mockforge_recorder/
replay.rs1use crate::{
4 database::RecorderDatabase,
5 diff::{ComparisonResult, ResponseComparator},
6 Result,
7};
8use std::collections::HashMap;
9
10pub struct ReplayEngine {
12 db: RecorderDatabase,
13}
14
15impl ReplayEngine {
16 pub fn new(db: RecorderDatabase) -> Self {
18 Self { db }
19 }
20
21 pub async fn replay_request(&self, request_id: &str) -> Result<ReplayResult> {
23 let exchange = self.db.get_exchange(request_id).await?;
24
25 match exchange {
26 Some(exchange) => Ok(ReplayResult {
27 request_id: request_id.to_string(),
28 success: true,
29 message: format!(
30 "Replayed {} {} request",
31 exchange.request.protocol, exchange.request.method
32 ),
33 original_status: exchange.response.map(|r| r.status_code),
34 replay_status: None,
35 }),
36 None => Err(crate::RecorderError::NotFound(request_id.to_string())),
37 }
38 }
39
40 pub async fn replay_batch(&self, limit: i32) -> Result<Vec<ReplayResult>> {
42 let requests = self.db.list_recent(limit).await?;
43 let mut results = Vec::new();
44
45 for request in requests {
46 match self.replay_request(&request.id).await {
47 Ok(result) => results.push(result),
48 Err(e) => {
49 results.push(ReplayResult {
50 request_id: request.id.clone(),
51 success: false,
52 message: format!("Replay failed: {}", e),
53 original_status: None,
54 replay_status: None,
55 });
56 }
57 }
58 }
59
60 Ok(results)
61 }
62
63 pub async fn compare_responses(
65 &self,
66 request_id: &str,
67 replay_response_body: &[u8],
68 replay_status: i32,
69 replay_headers: &HashMap<String, String>,
70 ) -> Result<ComparisonResult> {
71 let exchange = self.db.get_exchange(request_id).await?;
72
73 match exchange {
74 Some(exchange) => {
75 let response = exchange.response.ok_or_else(|| {
76 crate::RecorderError::NotFound(format!("No response for {}", request_id))
77 })?;
78
79 let original_headers = response.headers_map();
81
82 let original_body = response.decoded_body().unwrap_or_default();
84
85 Ok(ResponseComparator::compare(
87 response.status_code,
88 &original_headers,
89 &original_body,
90 replay_status,
91 replay_headers,
92 replay_response_body,
93 ))
94 }
95 None => Err(crate::RecorderError::NotFound(request_id.to_string())),
96 }
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct ReplayResult {
103 pub request_id: String,
104 pub success: bool,
105 pub message: String,
106 pub original_status: Option<i32>,
107 pub replay_status: Option<i32>,
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_replay_result_creation() {
116 let result = ReplayResult {
117 request_id: "test-123".to_string(),
118 success: true,
119 message: "Replayed successfully".to_string(),
120 original_status: Some(200),
121 replay_status: Some(200),
122 };
123
124 assert!(result.success);
125 assert_eq!(result.original_status, Some(200));
126 }
127}