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

gripmock / grpctestify-rust / 24368153097

13 Apr 2026 09:36PM UTC coverage: 75.096% (-0.3%) from 75.445%
24368153097

Pull #35

github

web-flow
Merge 97a02fd78 into 4ba0f08f1
Pull Request #35: feat: meta section & refactoring

2518 of 3592 new or added lines in 47 files covered. (70.1%)

155 existing lines in 9 files now uncovered.

16781 of 22346 relevant lines covered (75.1%)

2495.37 hits per line

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

36.75
/src/execution/runner_helpers.rs
1
//! Helper utilities for the test runner.
2
//!
3
//! Contains pure functions and static helpers used by the test runner
4
//! that don't require `self` access: variable substitution, TLS defaults,
5
//! JSON formatting, and metadata conversion.
6

7
use crate::polyfill::runtime;
8
use crate::utils::file::FileUtils;
9
use serde_json::Value;
10
use std::collections::HashMap;
11
use std::path::Path;
12

13
/// Buffer size for the request message channel.
14
/// Controls back-pressure for client streaming: larger values allow more
15
/// buffered requests but consume more memory.
16
pub const REQUEST_CHANNEL_BUFFER: usize = 100;
17

18
/// Default TLS configuration from environment variables.
NEW
19
pub fn tls_env_defaults() -> HashMap<String, String> {
×
NEW
20
    let mut defaults = HashMap::new();
×
21

NEW
22
    if let Ok(value) = std::env::var(crate::config::ENV_GRPCTESTIFY_TLS_CA_FILE)
×
NEW
23
        && !value.trim().is_empty()
×
NEW
24
    {
×
NEW
25
        defaults.insert("ca_cert".to_string(), value);
×
NEW
26
    }
×
NEW
27
    if let Ok(value) = std::env::var(crate::config::ENV_GRPCTESTIFY_TLS_CERT_FILE)
×
NEW
28
        && !value.trim().is_empty()
×
NEW
29
    {
×
NEW
30
        defaults.insert("client_cert".to_string(), value);
×
NEW
31
    }
×
NEW
32
    if let Ok(value) = std::env::var(crate::config::ENV_GRPCTESTIFY_TLS_KEY_FILE)
×
NEW
33
        && !value.trim().is_empty()
×
NEW
34
    {
×
NEW
35
        defaults.insert("client_key".to_string(), value);
×
NEW
36
    }
×
NEW
37
    if let Ok(value) = std::env::var(crate::config::ENV_GRPCTESTIFY_TLS_SERVER_NAME)
×
NEW
38
        && !value.trim().is_empty()
×
NEW
39
    {
×
NEW
40
        defaults.insert("server_name".to_string(), value);
×
NEW
41
    }
×
42

NEW
43
    defaults
×
NEW
44
}
×
45

46
/// Resolve a TLS file path relative to document or CWD.
NEW
47
pub fn resolve_tls_path(value: &str, from_env: bool, document_path: &Path) -> String {
×
NEW
48
    let path = Path::new(value);
×
NEW
49
    if path.is_absolute() {
×
NEW
50
        return path.to_string_lossy().to_string();
×
NEW
51
    }
×
52

NEW
53
    if from_env {
×
NEW
54
        if runtime::supports(runtime::Capability::IsolatedFsIo)
×
NEW
55
            && let Ok(cwd) = std::env::current_dir()
×
56
        {
NEW
57
            return cwd.join(path).to_string_lossy().to_string();
×
NEW
58
        }
×
NEW
59
        return path.to_string_lossy().to_string();
×
NEW
60
    }
×
61

NEW
62
    FileUtils::resolve_relative_path(document_path, value)
×
NEW
63
        .to_string_lossy()
×
NEW
64
        .to_string()
×
NEW
65
}
×
66

67
/// Build full service name from package and service.
68
pub fn full_service_name(package: &str, service: &str) -> String {
3✔
69
    if package.is_empty() {
3✔
70
        service.to_string()
1✔
71
    } else {
72
        format!("{}.{}", package, service)
2✔
73
    }
74
}
3✔
75

76
/// Format JSON value for display.
77
pub fn format_json_pretty(value: &Value) -> String {
2✔
78
    serde_json::to_string_pretty(value).unwrap_or_else(|_| value.to_string())
2✔
79
}
2✔
80

81
/// Interpolate variables in a string template.
82
/// Replaces `{{var}}` patterns with values from the variables map.
83
/// Returns `None` if no substitutions were made.
84
pub fn interpolate_variables(template: &str, variables: &HashMap<String, Value>) -> Option<String> {
2✔
85
    let mut out = String::with_capacity(template.len());
2✔
86
    let mut cursor = 0usize;
2✔
87
    let mut changed = false;
2✔
88

89
    while let Some(open_rel) = template[cursor..].find("{{") {
7✔
90
        let open = cursor + open_rel;
5✔
91
        out.push_str(&template[cursor..open]);
5✔
92

93
        let after_open = open + 2;
5✔
94
        if let Some(close_rel) = template[after_open..].find("}}") {
5✔
95
            let close = after_open + close_rel;
5✔
96
            let var_name = template[after_open..close].trim();
5✔
97

98
            if let Some(var_value) = variables.get(var_name) {
5✔
99
                if let Value::String(s) = var_value {
4✔
100
                    out.push_str(s);
2✔
101
                } else {
2✔
102
                    out.push_str(&var_value.to_string());
2✔
103
                }
2✔
104
                changed = true;
4✔
105
            } else {
1✔
106
                out.push_str(&template[open..close + 2]);
1✔
107
            }
1✔
108
            cursor = close + 2;
5✔
109
        } else {
NEW
110
            out.push_str(&template[cursor..]);
×
NEW
111
            break;
×
112
        }
113
    }
114

115
    if cursor < template.len() {
2✔
NEW
116
        out.push_str(&template[cursor..]);
×
117
    }
2✔
118

119
    if changed { Some(out) } else { None }
2✔
120
}
2✔
121

122
/// Recursively substitute variables in a JSON value.
123
/// If a string is exactly `{{var}}`, it's replaced with the actual Value type.
124
/// Otherwise, string interpolation is performed.
NEW
125
pub fn substitute_variables(value: &mut Value, variables: &HashMap<String, Value>) {
×
NEW
126
    match value {
×
NEW
127
        Value::String(s) => {
×
NEW
128
            let original = s.clone();
×
NEW
129
            if s.starts_with("{{") && s.ends_with("}}") {
×
NEW
130
                let inner = s[2..s.len() - 2].trim();
×
NEW
131
                if !inner.contains("{{")
×
NEW
132
                    && let Some(val) = variables.get(inner)
×
133
                {
NEW
134
                    *value = val.clone();
×
NEW
135
                    return;
×
NEW
136
                }
×
NEW
137
            }
×
NEW
138
            if let Some(replaced) = interpolate_variables(s, variables) {
×
NEW
139
                *s = replaced;
×
NEW
140
            }
×
141
            // If nothing changed, restore original (type-preserving)
NEW
142
            if *s == original {
×
NEW
143
                // No change
×
NEW
144
            }
×
145
        }
NEW
146
        Value::Array(items) => {
×
NEW
147
            for item in items {
×
NEW
148
                substitute_variables(item, variables);
×
NEW
149
            }
×
150
        }
NEW
151
        Value::Object(map) => {
×
NEW
152
            for (_, val) in map.iter_mut() {
×
NEW
153
                substitute_variables(val, variables);
×
NEW
154
            }
×
155
        }
NEW
156
        _ => {}
×
157
    }
NEW
158
}
×
159

160
/// Convert tonic metadata map to HashMap.
161
pub fn metadata_map_to_hashmap(metadata: &tonic::metadata::MetadataMap) -> HashMap<String, String> {
1✔
162
    let mut out = HashMap::new();
1✔
163
    for kv in metadata.iter() {
2✔
164
        if let tonic::metadata::KeyAndValueRef::Ascii(key, value) = kv {
2✔
165
            if let Ok(v) = value.to_str() {
2✔
166
                out.insert(key.to_string(), v.to_string());
2✔
167
            }
2✔
NEW
168
        } else if let tonic::metadata::KeyAndValueRef::Binary(key, value) = kv {
×
NEW
169
            out.insert(key.to_string(), format!("{:?}", value));
×
NEW
170
        }
×
171
    }
172
    out
1✔
173
}
1✔
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