Skip to main content

rusmes_loadtest/
generators.rs

1//! Message generators for load testing
2
3use crate::config::MessageContent;
4use rand::RngExt;
5
6/// Message generator
7#[derive(Clone)]
8pub struct MessageGenerator {
9    min_size: usize,
10    max_size: usize,
11    #[allow(dead_code)]
12    content_type: MessageContent,
13}
14
15impl MessageGenerator {
16    /// Create a new message generator
17    pub fn new(min_size: usize, max_size: usize) -> Self {
18        Self {
19            min_size,
20            max_size,
21            content_type: MessageContent::Random,
22        }
23    }
24
25    /// Create a new message generator with content type
26    pub fn with_content_type(
27        min_size: usize,
28        max_size: usize,
29        content_type: MessageContent,
30    ) -> Self {
31        Self {
32            min_size,
33            max_size,
34            content_type,
35        }
36    }
37
38    /// Generate a random message
39    pub fn generate(&self) -> String {
40        let mut rng = rand::rng();
41        let target_size = rng.random_range(self.min_size..=self.max_size);
42
43        let from = format!("loadtest{}@example.com", rng.random::<u32>());
44        let to = format!("user{}@example.com", rng.random::<u32>());
45        let subject = format!("Load Test Message {}", rng.random::<u32>());
46
47        // Build headers first to know their exact size
48        let headers = format!(
49            "From: {}\r\n\
50             To: {}\r\n\
51             Subject: {}\r\n\
52             \r\n",
53            from, to, subject
54        );
55
56        // Calculate body size to reach target total size
57        let body_size = target_size.saturating_sub(headers.len()).max(1);
58        let body = self.generate_body(body_size);
59
60        format!("{}{}", headers, body)
61    }
62
63    /// Generate random body content
64    fn generate_body(&self, size: usize) -> String {
65        let mut rng = rand::rng();
66        let words = vec![
67            "Lorem",
68            "ipsum",
69            "dolor",
70            "sit",
71            "amet",
72            "consectetur",
73            "adipiscing",
74            "elit",
75            "sed",
76            "do",
77            "eiusmod",
78            "tempor",
79            "incididunt",
80            "ut",
81            "labore",
82            "et",
83            "dolore",
84            "magna",
85            "aliqua",
86            "enim",
87            "ad",
88            "minim",
89            "veniam",
90            "quis",
91        ];
92
93        let mut body = String::with_capacity(size);
94        while body.len() < size {
95            let word = words[rng.random_range(0..words.len())];
96            body.push_str(word);
97            body.push(' ');
98        }
99
100        body.truncate(size);
101        body
102    }
103
104    /// Generate a message with attachment
105    pub fn generate_with_attachment(&self, attachment_size: usize) -> String {
106        let mut rng = rand::rng();
107
108        let from = format!("loadtest{}@example.com", rng.random::<u32>());
109        let to = format!("user{}@example.com", rng.random::<u32>());
110        let subject = format!("Load Test Message with Attachment {}", rng.random::<u32>());
111
112        let boundary = "----=_NextPart_000_0001_01D0A1B2.C3D4E5F6";
113        let attachment_data = "A".repeat(attachment_size);
114
115        format!(
116            "From: {}\r\n\
117             To: {}\r\n\
118             Subject: {}\r\n\
119             MIME-Version: 1.0\r\n\
120             Content-Type: multipart/mixed; boundary=\"{}\"\r\n\
121             \r\n\
122             --{}\r\n\
123             Content-Type: text/plain; charset=\"utf-8\"\r\n\
124             \r\n\
125             This is a load test message with attachment.\r\n\
126             \r\n\
127             --{}\r\n\
128             Content-Type: application/octet-stream\r\n\
129             Content-Transfer-Encoding: base64\r\n\
130             Content-Disposition: attachment; filename=\"test.dat\"\r\n\
131             \r\n\
132             {}\r\n\
133             --{}--\r\n",
134            from, to, subject, boundary, boundary, boundary, attachment_data, boundary
135        )
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_message_generation() {
145        let generator = MessageGenerator::new(1024, 2048);
146        let message = generator.generate();
147
148        assert!(message.len() >= 1024);
149        assert!(message.len() <= 2048);
150        assert!(message.contains("From:"));
151        assert!(message.contains("To:"));
152        assert!(message.contains("Subject:"));
153    }
154
155    #[test]
156    fn test_message_with_attachment() {
157        let generator = MessageGenerator::new(1024, 2048);
158        let message = generator.generate_with_attachment(100);
159
160        assert!(message.contains("MIME-Version: 1.0"));
161        assert!(message.contains("Content-Type: multipart/mixed"));
162        assert!(message.contains("attachment"));
163    }
164
165    #[test]
166    fn test_body_generation() {
167        let generator = MessageGenerator::new(100, 200);
168        let body = generator.generate_body(150);
169
170        assert_eq!(body.len(), 150);
171    }
172}