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

gripmock / grpctestify-rust / 24905917607

24 Apr 2026 06:38PM UTC coverage: 78.02% (+0.3%) from 77.729%
24905917607

Pull #43

github

web-flow
Merge f552abec5 into 017e47d15
Pull Request #43: new command gen grpcurl & call

741 of 993 new or added lines in 24 files covered. (74.62%)

3 existing lines in 3 files now uncovered.

19594 of 25114 relevant lines covered (78.02%)

39580.44 hits per line

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

99.41
/src/parser/document_splitter.rs
1
// Split GCTF sections into multiple documents based on ENDPOINT boundaries.
2
//
3
// "Preamble" sections (ADDRESS, TLS, PROTO, OPTIONS, REQUEST_HEADERS) that appear
4
// immediately before ENDPOINT are moved to the new document — they configure the
5
// next request, not the previous one.
6

7
use crate::parser::ast::{Section, SectionType};
8

9
/// Sections that are "preambles" — they belong to the next document, not the current one.
10
fn is_preamble(t: &SectionType) -> bool {
29,110✔
11
    matches!(
29,103✔
12
        t,
29,110✔
13
        SectionType::Address
14
            | SectionType::Tls
15
            | SectionType::Proto
16
            | SectionType::Options
17
            | SectionType::RequestHeaders
18
    )
19
}
29,110✔
20

21
fn is_content_section(t: &SectionType) -> bool {
132,741✔
22
    matches!(
40,669✔
23
        t,
132,741✔
24
        SectionType::Request
25
            | SectionType::Response
26
            | SectionType::Error
27
            | SectionType::Asserts
28
            | SectionType::Extract
29
    )
30
}
132,741✔
31

32
/// Split owned sections into documents based on ENDPOINT boundaries.
33
///
34
/// Each ENDPOINT with meaningful content before it starts a new document.
35
pub fn split_sections_by_boundary_owned(sections: Vec<Section>) -> Vec<Vec<Section>> {
11,482✔
36
    if sections.is_empty() {
11,482✔
37
        return Vec::new();
2✔
38
    }
11,480✔
39

40
    let mut docs: Vec<Vec<Section>> = Vec::new();
11,480✔
41
    let mut current: Vec<Section> = Vec::new();
11,480✔
42
    let mut current_has_content = false;
11,480✔
43

44
    for section in sections {
132,741✔
45
        if section.section_type == SectionType::Endpoint && current_has_content {
132,741✔
46
            let mut preamble: Vec<Section> = Vec::new();
29,103✔
47
            while let Some(last) = current.last() {
29,110✔
48
                if is_preamble(&last.section_type) {
29,110✔
49
                    if let Some(section) = current.pop() {
7✔
50
                        preamble.push(section);
7✔
51
                    } else {
7✔
NEW
52
                        break;
×
53
                    }
54
                } else {
55
                    break;
29,103✔
56
                }
57
            }
58
            preamble.reverse();
29,103✔
59
            docs.push(std::mem::take(&mut current));
29,103✔
60
            current = preamble;
29,103✔
61
            current_has_content = false;
29,103✔
62
        }
103,638✔
63

64
        if is_content_section(&section.section_type) {
132,741✔
65
            current_has_content = true;
92,072✔
66
        }
92,072✔
67
        current.push(section);
132,741✔
68
    }
69

70
    if !current.is_empty() {
11,480✔
71
        docs.push(current);
11,480✔
72
    }
11,480✔
73

74
    docs
11,480✔
75
}
11,482✔
76

77
/// Split sections into documents based on ENDPOINT boundaries.
78
///
79
/// Convenience wrapper for borrowed input.
80
pub fn split_sections_by_boundary(sections: &[Section]) -> Vec<Vec<Section>> {
57✔
81
    split_sections_by_boundary_owned(sections.to_vec())
57✔
82
}
57✔
83

84
#[cfg(test)]
85
mod tests {
86
    use super::*;
87
    use crate::parser::ast::{Section, SectionContent, SectionType};
88

89
    fn section(stype: SectionType, line: usize) -> Section {
53✔
90
        Section {
53✔
91
            section_type: stype,
53✔
92
            content: SectionContent::Empty,
53✔
93
            inline_options: Default::default(),
53✔
94
            raw_content: String::new(),
53✔
95
            start_line: line,
53✔
96
            end_line: line,
53✔
97
        }
53✔
98
    }
53✔
99

100
    #[test]
101
    fn test_split_single_document() {
1✔
102
        let sections = vec![
1✔
103
            section(SectionType::Endpoint, 0),
1✔
104
            section(SectionType::Request, 2),
1✔
105
            section(SectionType::Response, 4),
1✔
106
        ];
107
        let docs = split_sections_by_boundary(&sections);
1✔
108
        assert_eq!(docs.len(), 1);
1✔
109
        assert_eq!(docs[0].len(), 3);
1✔
110
    }
1✔
111

112
    #[test]
113
    fn test_split_two_documents_with_preamble() {
1✔
114
        let sections = vec![
1✔
115
            section(SectionType::Endpoint, 0),
1✔
116
            section(SectionType::Request, 2),
1✔
117
            section(SectionType::Response, 4),
1✔
118
            section(SectionType::Address, 7), // preamble for next
1✔
119
            section(SectionType::Endpoint, 9),
1✔
120
            section(SectionType::Request, 11),
1✔
121
            section(SectionType::Response, 13),
1✔
122
        ];
123
        let docs = split_sections_by_boundary(&sections);
1✔
124
        assert_eq!(docs.len(), 2);
1✔
125
        assert_eq!(docs[0].len(), 3); // endpoint + request + response
1✔
126
        assert_eq!(docs[1].len(), 4); // address + endpoint + request + response
1✔
127
    }
1✔
128

129
    #[test]
130
    fn test_split_empty_input() {
1✔
131
        let sections: Vec<Section> = vec![];
1✔
132
        let docs = split_sections_by_boundary(&sections);
1✔
133
        assert!(docs.is_empty());
1✔
134
    }
1✔
135

136
    #[test]
137
    fn test_split_no_endpoint_single_doc() {
1✔
138
        let sections = vec![
1✔
139
            section(SectionType::Address, 0),
1✔
140
            section(SectionType::Request, 2),
1✔
141
            section(SectionType::Response, 4),
1✔
142
        ];
143
        let docs = split_sections_by_boundary(&sections);
1✔
144
        assert_eq!(docs.len(), 1);
1✔
145
        assert_eq!(docs[0].len(), 3);
1✔
146
    }
1✔
147

148
    #[test]
149
    fn test_split_only_preamble_sections() {
1✔
150
        let sections = vec![
1✔
151
            section(SectionType::Address, 0),
1✔
152
            section(SectionType::Tls, 2),
1✔
153
            section(SectionType::Proto, 4),
1✔
154
        ];
155
        let docs = split_sections_by_boundary(&sections);
1✔
156
        assert_eq!(docs.len(), 1);
1✔
157
        assert_eq!(docs[0].len(), 3);
1✔
158
    }
1✔
159

160
    #[test]
161
    fn test_split_preambles_without_content_no_split() {
1✔
162
        // Without content before first endpoint, preambles stay in single doc
163
        let sections = vec![
1✔
164
            section(SectionType::Address, 0),
1✔
165
            section(SectionType::Tls, 2),
1✔
166
            section(SectionType::Proto, 4),
1✔
167
            section(SectionType::Options, 6),
1✔
168
            section(SectionType::RequestHeaders, 8),
1✔
169
            section(SectionType::Endpoint, 10),
1✔
170
            section(SectionType::Request, 12),
1✔
171
            section(SectionType::Response, 14),
1✔
172
        ];
173
        let docs = split_sections_by_boundary(&sections);
1✔
174
        // No split - no content before first endpoint
175
        assert_eq!(docs.len(), 1);
1✔
176
        assert_eq!(docs[0].len(), 8); // all sections in one doc
1✔
177
    }
1✔
178

179
    #[test]
180
    fn test_split_endpoint_at_start() {
1✔
181
        let sections = vec![
1✔
182
            section(SectionType::Endpoint, 0),
1✔
183
            section(SectionType::Request, 2),
1✔
184
            section(SectionType::Response, 4),
1✔
185
            section(SectionType::Endpoint, 6),
1✔
186
            section(SectionType::Request, 8),
1✔
187
        ];
188
        let docs = split_sections_by_boundary(&sections);
1✔
189
        assert_eq!(docs.len(), 2);
1✔
190
        assert_eq!(docs[0].len(), 3);
1✔
191
        assert_eq!(docs[1].len(), 2);
1✔
192
    }
1✔
193

194
    #[test]
195
    fn test_split_extract_as_terminal() {
1✔
196
        let sections = vec![
1✔
197
            section(SectionType::Endpoint, 0),
1✔
198
            section(SectionType::Request, 2),
1✔
199
            section(SectionType::Response, 4),
1✔
200
            section(SectionType::Extract, 6), // terminal
1✔
201
            section(SectionType::Endpoint, 8),
1✔
202
            section(SectionType::Request, 10),
1✔
203
        ];
204
        let docs = split_sections_by_boundary(&sections);
1✔
205
        assert_eq!(docs.len(), 2);
1✔
206
    }
1✔
207

208
    #[test]
209
    fn test_split_asserts_as_terminal() {
1✔
210
        let sections = vec![
1✔
211
            section(SectionType::Endpoint, 0),
1✔
212
            section(SectionType::Request, 2),
1✔
213
            section(SectionType::Asserts, 4), // terminal
1✔
214
            section(SectionType::Endpoint, 6),
1✔
215
            section(SectionType::Request, 8),
1✔
216
        ];
217
        let docs = split_sections_by_boundary(&sections);
1✔
218
        assert_eq!(docs.len(), 2);
1✔
219
    }
1✔
220

221
    #[test]
222
    fn test_split_error_as_terminal() {
1✔
223
        let sections = vec![
1✔
224
            section(SectionType::Endpoint, 0),
1✔
225
            section(SectionType::Request, 2),
1✔
226
            section(SectionType::Error, 4), // terminal
1✔
227
            section(SectionType::Endpoint, 6),
1✔
228
            section(SectionType::Request, 8),
1✔
229
        ];
230
        let docs = split_sections_by_boundary(&sections);
1✔
231
        assert_eq!(docs.len(), 2);
1✔
232
    }
1✔
233

234
    #[test]
235
    fn test_preamble_selection() {
1✔
236
        let sections = vec![
1✔
237
            section(SectionType::Endpoint, 0),
1✔
238
            section(SectionType::Request, 2),
1✔
239
            section(SectionType::Response, 4),
1✔
240
            section(SectionType::Address, 6),         // preamble
1✔
241
            section(SectionType::Tls, 8),             // preamble
1✔
242
            section(SectionType::RequestHeaders, 10), // preamble
1✔
243
            section(SectionType::Endpoint, 12),
1✔
244
            section(SectionType::Request, 14),
1✔
245
        ];
246
        let docs = split_sections_by_boundary(&sections);
1✔
247
        assert_eq!(docs.len(), 2);
1✔
248
        assert_eq!(docs[0].len(), 3);
1✔
249
        assert_eq!(docs[1].len(), 5); // 3 preambles + 2 new sections
1✔
250
    }
1✔
251
}
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