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

gripmock / grpctestify-rust / 29047753916

09 Jul 2026 08:23PM UTC coverage: 71.985% (-6.2%) from 78.205%
29047753916

Pull #46

github

web-flow
Merge 9074e6e2a into c7b8c50f3
Pull Request #46: [1.7] add BENCH section

27127 of 37684 relevant lines covered (71.99%)

26911.62 hits per line

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

95.54
/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, PluginManager};
7
use crate::utils::section_content_line;
8
use serde_json::Value;
9
use std::collections::HashMap;
10
use std::sync::Arc;
11
use std::sync::LazyLock;
12

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

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

25
/// Global plugin registry for assertion evaluation.
26
static PLUGIN_REGISTRY: LazyLock<Arc<dyn apif_assert::registry::PluginRegistry>> =
27
    LazyLock::new(|| Arc::new(PluginManager::new()));
3✔
28

29
impl AssertionHandler {
30
    /// Create new assertion handler
31
    pub fn new(_verbose: bool) -> Self {
29✔
32
        Self {
29✔
33
            engine: AssertionEngine::with_registry(PLUGIN_REGISTRY.clone()),
29✔
34
        }
29✔
35
    }
29✔
36

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

48
        for section in sections {
2✔
49
            if section.section_type == SectionType::Asserts
2✔
50
                && let SectionContent::Assertions(lines) = &section.content
2✔
51
            {
52
                let results =
2✔
53
                    self.engine
2✔
54
                        .evaluate_all(lines, target_value, Some(headers), Some(trailers));
2✔
55

56
                for (idx, result) in results.iter().enumerate() {
2✔
57
                    let line_num = section_content_line(section.start_line, idx);
2✔
58
                    let context = format!("at line {}", line_num);
2✔
59
                    append_single_failure(result, &context, &mut failure_messages);
2✔
60
                }
2✔
61
            }
×
62
        }
63

64
        AssertionResult {
2✔
65
            passed: failure_messages.is_empty(),
2✔
66
            failure_messages,
2✔
67
        }
2✔
68
    }
2✔
69

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

81
        if section.section_type == SectionType::Asserts
1✔
82
            && let SectionContent::Assertions(lines) = &section.content
1✔
83
        {
84
            let results =
1✔
85
                self.engine
1✔
86
                    .evaluate_all(lines, target_value, Some(headers), Some(trailers));
1✔
87

88
            for (idx, result) in results.iter().enumerate() {
1✔
89
                let line_num = section_content_line(section.start_line, idx);
×
90
                let context = format!("at line {}", line_num);
×
91
                append_single_failure(result, &context, &mut failure_messages);
×
92
            }
×
93
        }
×
94

95
        AssertionResult {
1✔
96
            passed: failure_messages.is_empty(),
1✔
97
            failure_messages,
1✔
98
        }
1✔
99
    }
1✔
100

101
    /// Check if section has assertions (test-only)
102
    #[cfg(test)]
103
    pub fn has_assertions(&self, section: &Section) -> bool {
2✔
104
        section.section_type == SectionType::Asserts
2✔
105
    }
2✔
106

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

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

131
    /// Evaluate assertions for a section (convenience method for runner.rs)
132
    #[expect(clippy::too_many_arguments)]
133
    pub fn evaluate_assertions_for_section(
1✔
134
        &self,
1✔
135
        lines: &[String],
1✔
136
        target_value: &Value,
1✔
137
        headers: &HashMap<String, String>,
1✔
138
        trailers: &HashMap<String, String>,
1✔
139
        section_context: &str,
1✔
140
        start_line: usize,
1✔
141
        timing: Option<&AssertionTiming>,
1✔
142
    ) -> AssertionResult {
1✔
143
        let mut failure_messages = Vec::new();
1✔
144

145
        let results = self.engine.evaluate_all_with_timing(
1✔
146
            lines,
1✔
147
            target_value,
1✔
148
            Some(headers),
1✔
149
            Some(trailers),
1✔
150
            timing,
1✔
151
        );
152

153
        for (idx, result) in results.iter().enumerate() {
3✔
154
            let line_num = section_content_line(start_line, idx);
3✔
155
            let context = format!("{} (assertion at line {})", section_context, line_num);
3✔
156
            append_single_failure(result, &context, &mut failure_messages);
3✔
157
        }
3✔
158

159
        AssertionResult {
1✔
160
            passed: failure_messages.is_empty(),
1✔
161
            failure_messages,
1✔
162
        }
1✔
163
    }
1✔
164
}
165

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

189
#[cfg(test)]
190
mod tests {
191
    use super::*;
192
    use serde_json::json;
193

194
    #[test]
195
    fn test_evaluate_assertions_pass() {
1✔
196
        let handler = AssertionHandler::new(false);
1✔
197
        let sections = vec![Section {
1✔
198
            section_type: SectionType::Asserts,
1✔
199
            content: SectionContent::Assertions(vec![".id == 123".to_string()]),
1✔
200
            inline_options: Default::default(),
1✔
201
            raw_content: "".to_string(),
1✔
202
            start_line: 0,
1✔
203
            end_line: 0,
1✔
204
            attributes: Vec::new(),
1✔
205
        }];
1✔
206

207
        let target = json!({"id": 123, "name": "test"});
1✔
208
        let headers = HashMap::new();
1✔
209
        let trailers = HashMap::new();
1✔
210

211
        let result = handler.evaluate_assertions(&sections, &target, &headers, &trailers);
1✔
212
        assert!(result.passed);
1✔
213
        assert!(result.failure_messages.is_empty());
1✔
214
    }
1✔
215

216
    #[test]
217
    fn test_evaluate_assertions_fail() {
1✔
218
        let handler = AssertionHandler::new(false);
1✔
219
        let sections = vec![Section {
1✔
220
            section_type: SectionType::Asserts,
1✔
221
            content: SectionContent::Assertions(vec![".id == 456".to_string()]),
1✔
222
            inline_options: Default::default(),
1✔
223
            raw_content: "".to_string(),
1✔
224
            start_line: 0,
1✔
225
            end_line: 0,
1✔
226
            attributes: Vec::new(),
1✔
227
        }];
1✔
228

229
        let target = json!({"id": 123, "name": "test"});
1✔
230
        let headers = HashMap::new();
1✔
231
        let trailers = HashMap::new();
1✔
232

233
        let result = handler.evaluate_assertions(&sections, &target, &headers, &trailers);
1✔
234
        assert!(!result.passed);
1✔
235
        assert!(!result.failure_messages.is_empty());
1✔
236
    }
1✔
237

238
    #[test]
239
    fn test_has_assertions() {
1✔
240
        let handler = AssertionHandler::new(false);
1✔
241
        let asserts_section = Section {
1✔
242
            section_type: SectionType::Asserts,
1✔
243
            content: SectionContent::Assertions(vec![]),
1✔
244
            inline_options: Default::default(),
1✔
245
            raw_content: "".to_string(),
1✔
246
            start_line: 0,
1✔
247
            end_line: 0,
1✔
248
            attributes: Vec::new(),
1✔
249
        };
1✔
250

251
        let other_section = Section {
1✔
252
            section_type: SectionType::Response,
1✔
253
            content: SectionContent::Json(json!({})),
1✔
254
            inline_options: Default::default(),
1✔
255
            raw_content: "".to_string(),
1✔
256
            start_line: 0,
1✔
257
            end_line: 0,
1✔
258
            attributes: Vec::new(),
1✔
259
        };
1✔
260

261
        assert!(handler.has_assertions(&asserts_section));
1✔
262
        assert!(!handler.has_assertions(&other_section));
1✔
263
    }
1✔
264

265
    #[test]
266
    fn test_get_assertion_lines() {
1✔
267
        let handler = AssertionHandler::new(false);
1✔
268
        let section = Section {
1✔
269
            section_type: SectionType::Asserts,
1✔
270
            content: SectionContent::Assertions(vec![
1✔
271
                ".id == 123".to_string(),
1✔
272
                ".name == \"test\"".to_string(),
1✔
273
            ]),
1✔
274
            inline_options: Default::default(),
1✔
275
            raw_content: "".to_string(),
1✔
276
            start_line: 0,
1✔
277
            end_line: 0,
1✔
278
            attributes: Vec::new(),
1✔
279
        };
1✔
280

281
        let lines = handler.get_assertion_lines(&section);
1✔
282
        assert_eq!(lines.len(), 2);
1✔
283
    }
1✔
284

285
    #[test]
286
    fn test_evaluate_single_assertion() {
1✔
287
        let handler = AssertionHandler::new(false);
1✔
288
        let target = json!({"id": 123, "name": "test"});
1✔
289
        let headers = HashMap::new();
1✔
290
        let trailers = HashMap::new();
1✔
291

292
        let result = handler.evaluate_single_assertion(
1✔
293
            ".id == 123",
1✔
294
            &target,
1✔
295
            Some(&headers),
1✔
296
            Some(&trailers),
1✔
297
        );
298
        assert!(result.is_ok());
1✔
299
    }
1✔
300

301
    #[test]
302
    fn test_evaluate_section_assertions_empty() {
1✔
303
        let handler = AssertionHandler::new(false);
1✔
304
        let section = Section {
1✔
305
            section_type: SectionType::Asserts,
1✔
306
            content: SectionContent::Assertions(vec![]),
1✔
307
            inline_options: Default::default(),
1✔
308
            raw_content: "".to_string(),
1✔
309
            start_line: 0,
1✔
310
            end_line: 0,
1✔
311
            attributes: Vec::new(),
1✔
312
        };
1✔
313

314
        let target = json!({"id": 123});
1✔
315
        let headers = HashMap::new();
1✔
316
        let trailers = HashMap::new();
1✔
317

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

© 2026 Coveralls, Inc