mockforge_recorder/
replay.rs

1//! Replay functionality for recorded requests
2
3use crate::{
4    database::RecorderDatabase,
5    diff::{ComparisonResult, ResponseComparator},
6    Result,
7};
8use std::collections::HashMap;
9
10/// Replay engine for executing recorded requests
11pub struct ReplayEngine {
12    db: RecorderDatabase,
13}
14
15impl ReplayEngine {
16    /// Create a new replay engine
17    pub fn new(db: RecorderDatabase) -> Self {
18        Self { db }
19    }
20
21    /// Replay a single request by ID
22    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    /// Replay multiple requests matching a filter
41    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    /// Compare original and replayed responses
64    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                // Parse original headers from JSON string
80                let original_headers = response.headers_map();
81
82                // Decode original body
83                let original_body = response.decoded_body().unwrap_or_default();
84
85                // Use the ResponseComparator to perform the comparison
86                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/// Result of a replay operation
101#[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}