• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gripmock / grpctestify-rust / 24582789334

17 Apr 2026 07:23PM UTC coverage: 76.58% (+0.1%) from 76.438%
24582789334

push

github

web-flow
Merge pull request #39 from gripmock/refactoring-v3

refactoring part3

579 of 719 new or added lines in 11 files covered. (80.53%)

5 existing lines in 3 files now uncovered.

17585 of 22963 relevant lines covered (76.58%)

2430.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.95
/src/execution/assertion_handler.rs
1
// Assertion Handler - handles assertion evaluation
2

3
use crate::assert::AssertionEngine;
4
#[cfg(test)]
5
use crate::parser::ast::{Section, SectionContent, SectionType};
6
use crate::plugins::AssertionTiming;
7
use crate::utils::section_content_line;
8
use serde_json::Value;
9
use std::collections::HashMap;
10

11
/// Assertion evaluation result
12
#[derive(Debug, Clone)]
13
pub struct AssertionResult {
14
    pub passed: bool,
15
    pub failure_messages: Vec<String>,
16
}
17

18
/// Assertion Handler - evaluates assertions
19
pub struct AssertionHandler {
20
    engine: AssertionEngine,
21
}
22

23
impl AssertionHandler {
24
    /// Create new assertion handler
25
    pub fn new(_verbose: bool) -> Self {
28✔
26
        Self {
28✔
27
            engine: AssertionEngine::new(),
28✔
28
        }
28✔
29
    }
28✔
30

31
    /// Evaluate assertions for a document (test-only)
32
    #[cfg(test)]
33
    pub fn evaluate_assertions(
2✔
34
        &self,
2✔
35
        sections: &[Section],
2✔
36
        target_value: &Value,
2✔
37
        headers: &HashMap<String, String>,
2✔
38
        trailers: &HashMap<String, String>,
2✔
39
    ) -> AssertionResult {
2✔
40
        let mut failure_messages = Vec::new();
2✔
41

42
        for section in sections {
2✔
43
            if section.section_type == SectionType::Asserts
2✔
44
                && let SectionContent::Assertions(lines) = &section.content
2✔
45
            {
46
                let results =
2✔
47
                    self.engine
2✔
48
                        .evaluate_all(lines, target_value, Some(headers), Some(trailers));
2✔
49

50
                for (idx, result) in results.iter().enumerate() {
2✔
51
                    let line_num = section_content_line(section.start_line, idx);
2✔
52
                    let context = format!("at line {}", line_num);
2✔
53
                    append_single_failure(result, &context, &mut failure_messages);
2✔
54
                }
2✔
UNCOV
55
            }
×
56
        }
57

58
        AssertionResult {
2✔
59
            passed: failure_messages.is_empty(),
2✔
60
            failure_messages,
2✔
61
        }
2✔
62
    }
2✔
63

64
    /// Evaluate assertions for a specific section (test-only)
65
    #[cfg(test)]
66
    pub fn evaluate_section_assertions(
1✔
67
        &self,
1✔
68
        section: &Section,
1✔
69
        target_value: &Value,
1✔
70
        headers: &HashMap<String, String>,
1✔
71
        trailers: &HashMap<String, String>,
1✔
72
    ) -> AssertionResult {
1✔
73
        let mut failure_messages = Vec::new();
1✔
74

75
        if section.section_type == SectionType::Asserts
1✔
76
            && let SectionContent::Assertions(lines) = &section.content
1✔
77
        {
78
            let results =
1✔
79
                self.engine
1✔
80
                    .evaluate_all(lines, target_value, Some(headers), Some(trailers));
1✔
81

82
            for (idx, result) in results.iter().enumerate() {
1✔
NEW
83
                let line_num = section_content_line(section.start_line, idx);
×
NEW
84
                let context = format!("at line {}", line_num);
×
NEW
85
                append_single_failure(result, &context, &mut failure_messages);
×
NEW
86
            }
×
UNCOV
87
        }
×
88

89
        AssertionResult {
1✔
90
            passed: failure_messages.is_empty(),
1✔
91
            failure_messages,
1✔
92
        }
1✔
93
    }
1✔
94

95
    /// Check if section has assertions (test-only)
96
    #[cfg(test)]
97
    pub fn has_assertions(&self, section: &Section) -> bool {
2✔
98
        section.section_type == SectionType::Asserts
2✔
99
    }
2✔
100

101
    /// Get assertion lines from section (test-only)
102
    #[cfg(test)]
103
    pub fn get_assertion_lines<'a>(&self, section: &'a Section) -> Vec<&'a String> {
1✔
104
        if let SectionContent::Assertions(lines) = &section.content {
1✔
105
            lines.iter().collect()
1✔
106
        } else {
107
            Vec::new()
×
108
        }
109
    }
1✔
110

111
    /// Evaluate a single assertion (test-only)
112
    #[cfg(test)]
113
    pub fn evaluate_single_assertion(
1✔
114
        &self,
1✔
115
        assertion: &str,
1✔
116
        target_value: &Value,
1✔
117
        headers: Option<&HashMap<String, String>>,
1✔
118
        trailers: Option<&HashMap<String, String>>,
1✔
119
    ) -> Result<crate::assert::AssertionResult, String> {
1✔
120
        self.engine
1✔
121
            .evaluate(assertion, target_value, headers, trailers)
1✔
122
            .map_err(|e| e.to_string())
1✔
123
    }
1✔
124

125
    /// Evaluate assertions for a section (convenience method for runner.rs)
126
    #[allow(clippy::too_many_arguments)]
127
    pub fn evaluate_assertions_for_section(
×
128
        &self,
×
129
        lines: &[String],
×
130
        target_value: &Value,
×
131
        headers: &HashMap<String, String>,
×
132
        trailers: &HashMap<String, String>,
×
NEW
133
        section_context: &str,
×
NEW
134
        start_line: usize,
×
135
        timing: Option<&AssertionTiming>,
×
136
    ) -> AssertionResult {
×
137
        let mut failure_messages = Vec::new();
×
138

139
        let results = self.engine.evaluate_all_with_timing(
×
140
            lines,
×
141
            target_value,
×
142
            Some(headers),
×
143
            Some(trailers),
×
144
            timing,
×
145
        );
146

NEW
147
        for (idx, result) in results.iter().enumerate() {
×
NEW
148
            let line_num = section_content_line(start_line, idx);
×
NEW
149
            let context = format!("{} (assertion at line {})", section_context, line_num);
×
NEW
150
            append_single_failure(result, &context, &mut failure_messages);
×
NEW
151
        }
×
152

153
        AssertionResult {
×
154
            passed: failure_messages.is_empty(),
×
155
            failure_messages,
×
156
        }
×
157
    }
×
158
}
159

160
fn append_single_failure(
2✔
161
    result: &crate::assert::AssertionResult,
2✔
162
    context: &str,
2✔
163
    failure_messages: &mut Vec<String>,
2✔
164
) {
2✔
165
    match result {
2✔
166
        crate::assert::AssertionResult::Fail {
167
            message,
1✔
168
            expected,
1✔
169
            actual,
1✔
170
        } => {
171
            failure_messages.push(format!("Assertion failed {}: {}", context, message));
1✔
172
            if let (Some(exp), Some(act)) = (expected, actual) {
1✔
173
                failure_messages.push(format!("    Expected: {}\n    Actual:   {}", exp, act));
1✔
174
            }
1✔
175
        }
NEW
176
        crate::assert::AssertionResult::Error(msg) => {
×
NEW
177
            failure_messages.push(format!("Assertion error {}: {}", context, msg));
×
NEW
178
        }
×
179
        _ => {}
1✔
180
    }
181
}
2✔
182

183
#[cfg(test)]
184
mod tests {
185
    use super::*;
186
    use serde_json::json;
187

188
    #[test]
189
    fn test_evaluate_assertions_pass() {
1✔
190
        let handler = AssertionHandler::new(false);
1✔
191
        let sections = vec![Section {
1✔
192
            section_type: SectionType::Asserts,
1✔
193
            content: SectionContent::Assertions(vec![".id == 123".to_string()]),
1✔
194
            inline_options: Default::default(),
1✔
195
            raw_content: "".to_string(),
1✔
196
            start_line: 0,
1✔
197
            end_line: 0,
1✔
198
        }];
1✔
199

200
        let target = json!({"id": 123, "name": "test"});
1✔
201
        let headers = HashMap::new();
1✔
202
        let trailers = HashMap::new();
1✔
203

204
        let result = handler.evaluate_assertions(&sections, &target, &headers, &trailers);
1✔
205
        assert!(result.passed);
1✔
206
        assert!(result.failure_messages.is_empty());
1✔
207
    }
1✔
208

209
    #[test]
210
    fn test_evaluate_assertions_fail() {
1✔
211
        let handler = AssertionHandler::new(false);
1✔
212
        let sections = vec![Section {
1✔
213
            section_type: SectionType::Asserts,
1✔
214
            content: SectionContent::Assertions(vec![".id == 456".to_string()]),
1✔
215
            inline_options: Default::default(),
1✔
216
            raw_content: "".to_string(),
1✔
217
            start_line: 0,
1✔
218
            end_line: 0,
1✔
219
        }];
1✔
220

221
        let target = json!({"id": 123, "name": "test"});
1✔
222
        let headers = HashMap::new();
1✔
223
        let trailers = HashMap::new();
1✔
224

225
        let result = handler.evaluate_assertions(&sections, &target, &headers, &trailers);
1✔
226
        assert!(!result.passed);
1✔
227
        assert!(!result.failure_messages.is_empty());
1✔
228
    }
1✔
229

230
    #[test]
231
    fn test_has_assertions() {
1✔
232
        let handler = AssertionHandler::new(false);
1✔
233
        let asserts_section = Section {
1✔
234
            section_type: SectionType::Asserts,
1✔
235
            content: SectionContent::Assertions(vec![]),
1✔
236
            inline_options: Default::default(),
1✔
237
            raw_content: "".to_string(),
1✔
238
            start_line: 0,
1✔
239
            end_line: 0,
1✔
240
        };
1✔
241

242
        let other_section = Section {
1✔
243
            section_type: SectionType::Response,
1✔
244
            content: SectionContent::Json(json!({})),
1✔
245
            inline_options: Default::default(),
1✔
246
            raw_content: "".to_string(),
1✔
247
            start_line: 0,
1✔
248
            end_line: 0,
1✔
249
        };
1✔
250

251
        assert!(handler.has_assertions(&asserts_section));
1✔
252
        assert!(!handler.has_assertions(&other_section));
1✔
253
    }
1✔
254

255
    #[test]
256
    fn test_get_assertion_lines() {
1✔
257
        let handler = AssertionHandler::new(false);
1✔
258
        let section = Section {
1✔
259
            section_type: SectionType::Asserts,
1✔
260
            content: SectionContent::Assertions(vec![
1✔
261
                ".id == 123".to_string(),
1✔
262
                ".name == \"test\"".to_string(),
1✔
263
            ]),
1✔
264
            inline_options: Default::default(),
1✔
265
            raw_content: "".to_string(),
1✔
266
            start_line: 0,
1✔
267
            end_line: 0,
1✔
268
        };
1✔
269

270
        let lines = handler.get_assertion_lines(&section);
1✔
271
        assert_eq!(lines.len(), 2);
1✔
272
    }
1✔
273

274
    #[test]
275
    fn test_evaluate_single_assertion() {
1✔
276
        let handler = AssertionHandler::new(false);
1✔
277
        let target = json!({"id": 123, "name": "test"});
1✔
278
        let headers = HashMap::new();
1✔
279
        let trailers = HashMap::new();
1✔
280

281
        let result = handler.evaluate_single_assertion(
1✔
282
            ".id == 123",
1✔
283
            &target,
1✔
284
            Some(&headers),
1✔
285
            Some(&trailers),
1✔
286
        );
287
        assert!(result.is_ok());
1✔
288
    }
1✔
289

290
    #[test]
291
    fn test_evaluate_section_assertions_empty() {
1✔
292
        let handler = AssertionHandler::new(false);
1✔
293
        let section = Section {
1✔
294
            section_type: SectionType::Asserts,
1✔
295
            content: SectionContent::Assertions(vec![]),
1✔
296
            inline_options: Default::default(),
1✔
297
            raw_content: "".to_string(),
1✔
298
            start_line: 0,
1✔
299
            end_line: 0,
1✔
300
        };
1✔
301

302
        let target = json!({"id": 123});
1✔
303
        let headers = HashMap::new();
1✔
304
        let trailers = HashMap::new();
1✔
305

306
        let result = handler.evaluate_section_assertions(&section, &target, &headers, &trailers);
1✔
307
        assert!(result.passed);
1✔
308
    }
1✔
309
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc