Skip to content

Commit eb9fdd7

Browse files
authored
Merge pull request #14 from gripmock/tls
tls
2 parents 1b056e4 + bfce8c0 commit eb9fdd7

File tree

8 files changed

+222
-40
lines changed

8 files changed

+222
-40
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
resolver = "2"
33

44
[workspace.package]
5-
version = "1.4.1"
5+
version = "1.4.2"
66
edition = "2024"
77
authors = ["bavix"]
88
license = "MIT"

src/commands/reflect.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
use anyhow::{Context, Result};
44

55
use crate::cli::args::ReflectArgs;
6+
use crate::config;
67
use crate::grpc::client::{GrpcClient, GrpcClientConfig};
78

89
pub async fn handle_reflect(args: &ReflectArgs) -> Result<()> {
910
// Determine address
1011
let address = if let Some(addr) = &args.address {
1112
addr.clone()
1213
} else {
13-
std::env::var("GRPCTESTIFY_ADDRESS").unwrap_or_else(|_| "localhost:4770".to_string())
14+
std::env::var(config::ENV_GRPCTESTIFY_ADDRESS).unwrap_or_else(|_| config::default_address())
1415
};
1516

1617
// Build client config

src/config.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,14 @@ pub struct CoverageConfig {
9191
pub output: Option<String>,
9292
}
9393

94-
// Default values
94+
// Environment variable names
9595
pub const ENV_GRPCTESTIFY_ADDRESS: &str = "GRPCTESTIFY_ADDRESS";
96+
pub const ENV_GRPCTESTIFY_COMPRESSION: &str = "GRPCTESTIFY_COMPRESSION";
97+
98+
pub const ENV_GRPCTESTIFY_TLS_CERT_FILE: &str = "GRPCTESTIFY_TLS_CERT_FILE";
99+
pub const ENV_GRPCTESTIFY_TLS_KEY_FILE: &str = "GRPCTESTIFY_TLS_KEY_FILE";
100+
pub const ENV_GRPCTESTIFY_TLS_CA_FILE: &str = "GRPCTESTIFY_TLS_CA_FILE";
101+
pub const ENV_GRPCTESTIFY_TLS_SERVER_NAME: &str = "GRPCTESTIFY_TLS_SERVER_NAME";
96102

97103
pub fn default_address() -> String {
98104
String::from("localhost:4770")

src/execution/runner.rs

Lines changed: 135 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,32 @@ pub struct TestRunner {
461461
assertion_handler: AssertionHandler,
462462
}
463463

464+
fn parse_bool_flag(value: &str) -> bool {
465+
matches!(
466+
value.trim().to_ascii_lowercase().as_str(),
467+
"true" | "1" | "yes" | "on"
468+
)
469+
}
470+
471+
fn tls_env_defaults() -> HashMap<String, String> {
472+
let mut defaults = HashMap::new();
473+
474+
if let Ok(value) = std::env::var(crate::config::ENV_GRPCTESTIFY_TLS_CA_FILE) {
475+
defaults.insert("ca_cert".to_string(), value);
476+
}
477+
if let Ok(value) = std::env::var(crate::config::ENV_GRPCTESTIFY_TLS_CERT_FILE) {
478+
defaults.insert("client_cert".to_string(), value);
479+
}
480+
if let Ok(value) = std::env::var(crate::config::ENV_GRPCTESTIFY_TLS_KEY_FILE) {
481+
defaults.insert("client_key".to_string(), value);
482+
}
483+
if let Ok(value) = std::env::var(crate::config::ENV_GRPCTESTIFY_TLS_SERVER_NAME) {
484+
defaults.insert("server_name".to_string(), value);
485+
}
486+
487+
defaults
488+
}
489+
464490
impl TestRunner {
465491
pub fn full_service_name(package: &str, service: &str) -> String {
466492
if package.is_empty() {
@@ -574,28 +600,42 @@ impl TestRunner {
574600
// Configure Client
575601
let document_path = Path::new(&document.file_path);
576602

577-
let tls_config = document.get_tls_config().map(|tls_map| TlsConfig {
578-
ca_cert_path: tls_map.get("ca_cert").map(|p| {
579-
FileUtils::resolve_relative_path(document_path, p)
580-
.to_string_lossy()
581-
.to_string()
582-
}),
583-
client_cert_path: tls_map.get("client_cert").map(|p| {
584-
FileUtils::resolve_relative_path(document_path, p)
585-
.to_string_lossy()
586-
.to_string()
587-
}),
588-
client_key_path: tls_map.get("client_key").map(|p| {
589-
FileUtils::resolve_relative_path(document_path, p)
590-
.to_string_lossy()
591-
.to_string()
592-
}),
593-
server_name: tls_map.get("server_name").cloned(),
594-
insecure_skip_verify: tls_map
595-
.get("insecure")
596-
.map(|s| s == "true")
597-
.unwrap_or(false),
598-
});
603+
let tls_defaults = tls_env_defaults();
604+
let tls_config = document
605+
.get_tls_config_with_defaults(&tls_defaults)
606+
.map(|tls_map| TlsConfig {
607+
ca_cert_path: tls_map
608+
.get("ca_cert")
609+
.or_else(|| tls_map.get("ca_file"))
610+
.map(|p| {
611+
FileUtils::resolve_relative_path(document_path, p)
612+
.to_string_lossy()
613+
.to_string()
614+
}),
615+
client_cert_path: tls_map
616+
.get("client_cert")
617+
.or_else(|| tls_map.get("cert"))
618+
.or_else(|| tls_map.get("cert_file"))
619+
.map(|p| {
620+
FileUtils::resolve_relative_path(document_path, p)
621+
.to_string_lossy()
622+
.to_string()
623+
}),
624+
client_key_path: tls_map
625+
.get("client_key")
626+
.or_else(|| tls_map.get("key"))
627+
.or_else(|| tls_map.get("key_file"))
628+
.map(|p| {
629+
FileUtils::resolve_relative_path(document_path, p)
630+
.to_string_lossy()
631+
.to_string()
632+
}),
633+
server_name: tls_map.get("server_name").cloned(),
634+
insecure_skip_verify: tls_map
635+
.get("insecure")
636+
.map(|s| parse_bool_flag(s))
637+
.unwrap_or(false),
638+
});
599639

600640
// Check for Proto config in document
601641
let proto_config = if let Some(proto_map) = document.get_proto_config() {
@@ -1616,20 +1656,36 @@ impl TestRunner {
16161656
}
16171657
}
16181658

1619-
// Show TLS config if present
1620-
if let Some(tls_config) = document.get_tls_config() {
1659+
// Show TLS config if present (including environment defaults)
1660+
let tls_defaults = tls_env_defaults();
1661+
if let Some(tls_config) = document.get_tls_config_with_defaults(&tls_defaults) {
16211662
println!();
16221663
println!("🔒 TLS Configuration:");
1623-
if tls_config.contains_key("ca_cert") {
1624-
println!(" CA Cert: {}", tls_config.get("ca_cert").unwrap());
1664+
if let Some(ca_cert) = tls_config
1665+
.get("ca_cert")
1666+
.or_else(|| tls_config.get("ca_file"))
1667+
{
1668+
println!(" CA Cert: {}", ca_cert);
16251669
}
1626-
if tls_config.contains_key("cert") {
1627-
println!(" Client Cert: {}", tls_config.get("cert").unwrap());
1670+
if let Some(client_cert) = tls_config
1671+
.get("client_cert")
1672+
.or_else(|| tls_config.get("cert"))
1673+
.or_else(|| tls_config.get("cert_file"))
1674+
{
1675+
println!(" Client Cert: {}", client_cert);
16281676
}
1629-
if tls_config.contains_key("key") {
1630-
println!(" Client Key: {}", tls_config.get("key").unwrap());
1677+
if let Some(client_key) = tls_config
1678+
.get("client_key")
1679+
.or_else(|| tls_config.get("key"))
1680+
.or_else(|| tls_config.get("key_file"))
1681+
{
1682+
println!(" Client Key: {}", client_key);
16311683
}
1632-
if tls_config.get("insecure") == Some(&"true".to_string()) {
1684+
if tls_config
1685+
.get("insecure")
1686+
.map(|s| parse_bool_flag(s))
1687+
.unwrap_or(false)
1688+
{
16331689
println!(" Insecure Skip Verify: true");
16341690
}
16351691
}
@@ -1691,6 +1747,54 @@ mod tests {
16911747
assert!(runner.write_mode);
16921748
}
16931749

1750+
#[test]
1751+
fn test_parse_bool_flag_truthy_values() {
1752+
assert!(parse_bool_flag("true"));
1753+
assert!(parse_bool_flag("1"));
1754+
assert!(parse_bool_flag("YES"));
1755+
assert!(parse_bool_flag("on"));
1756+
}
1757+
1758+
#[test]
1759+
fn test_parse_bool_flag_falsy_values() {
1760+
assert!(!parse_bool_flag("false"));
1761+
assert!(!parse_bool_flag("0"));
1762+
assert!(!parse_bool_flag("off"));
1763+
assert!(!parse_bool_flag(""));
1764+
}
1765+
1766+
#[test]
1767+
fn test_tls_env_defaults_uses_grpctestify_prefix() {
1768+
unsafe {
1769+
std::env::set_var(crate::config::ENV_GRPCTESTIFY_TLS_CA_FILE, "/tmp/ca.pem");
1770+
std::env::set_var(
1771+
crate::config::ENV_GRPCTESTIFY_TLS_CERT_FILE,
1772+
"/tmp/cert.pem",
1773+
);
1774+
std::env::set_var(crate::config::ENV_GRPCTESTIFY_TLS_KEY_FILE, "/tmp/key.pem");
1775+
std::env::set_var(crate::config::ENV_GRPCTESTIFY_TLS_SERVER_NAME, "localhost");
1776+
}
1777+
1778+
let defaults = tls_env_defaults();
1779+
assert_eq!(defaults.get("ca_cert"), Some(&"/tmp/ca.pem".to_string()));
1780+
assert_eq!(
1781+
defaults.get("client_cert"),
1782+
Some(&"/tmp/cert.pem".to_string())
1783+
);
1784+
assert_eq!(
1785+
defaults.get("client_key"),
1786+
Some(&"/tmp/key.pem".to_string())
1787+
);
1788+
assert_eq!(defaults.get("server_name"), Some(&"localhost".to_string()));
1789+
1790+
unsafe {
1791+
std::env::remove_var(crate::config::ENV_GRPCTESTIFY_TLS_CA_FILE);
1792+
std::env::remove_var(crate::config::ENV_GRPCTESTIFY_TLS_CERT_FILE);
1793+
std::env::remove_var(crate::config::ENV_GRPCTESTIFY_TLS_KEY_FILE);
1794+
std::env::remove_var(crate::config::ENV_GRPCTESTIFY_TLS_SERVER_NAME);
1795+
}
1796+
}
1797+
16941798
#[test]
16951799
fn test_test_runner_with_verbose() {
16961800
let runner = TestRunner::new(false, 30, false, false, true, None);

src/grpc/client.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -748,12 +748,25 @@ mod tests {
748748
fn test_compression_mode_from_env() {
749749
let _guard = ENV_MUTEX.lock().unwrap();
750750
unsafe {
751-
std::env::set_var("GRPCTESTIFY_COMPRESSION", "gzip");
751+
std::env::set_var(crate::config::ENV_GRPCTESTIFY_COMPRESSION, "gzip");
752752
}
753753
let mode = CompressionMode::from_env();
754754
assert_eq!(mode, CompressionMode::Gzip);
755755
unsafe {
756-
std::env::remove_var("GRPCTESTIFY_COMPRESSION");
756+
std::env::remove_var(crate::config::ENV_GRPCTESTIFY_COMPRESSION);
757+
}
758+
}
759+
760+
#[test]
761+
fn test_compression_mode_none_from_env() {
762+
let _guard = ENV_MUTEX.lock().unwrap();
763+
unsafe {
764+
std::env::set_var(crate::config::ENV_GRPCTESTIFY_COMPRESSION, "none");
765+
}
766+
let mode = CompressionMode::from_env();
767+
assert_eq!(mode, CompressionMode::None);
768+
unsafe {
769+
std::env::remove_var(crate::config::ENV_GRPCTESTIFY_COMPRESSION);
757770
}
758771
}
759772

@@ -762,7 +775,7 @@ mod tests {
762775
let _guard = ENV_MUTEX.lock().unwrap();
763776
// Ensure env var is not set
764777
unsafe {
765-
std::env::remove_var("GRPCTESTIFY_COMPRESSION");
778+
std::env::remove_var(crate::config::ENV_GRPCTESTIFY_COMPRESSION);
766779
}
767780
let mode = CompressionMode::from_env();
768781
assert_eq!(mode, CompressionMode::None);

src/grpc/tls.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,14 @@ pub enum CompressionMode {
195195
impl CompressionMode {
196196
/// Get compression mode from environment variable
197197
pub fn from_env() -> Self {
198-
match std::env::var("GRPCTESTIFY_COMPRESSION")
198+
match std::env::var(crate::config::ENV_GRPCTESTIFY_COMPRESSION)
199199
.unwrap_or_default()
200-
.to_lowercase()
200+
.trim()
201+
.to_ascii_lowercase()
201202
.as_str()
202203
{
203204
"gzip" => Self::Gzip,
205+
"" | "none" => Self::None,
204206
_ => Self::None,
205207
}
206208
}

src/lsp/handlers.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use serde_json::json;
77
use std::collections::HashMap;
88
use tower_lsp::lsp_types::*;
99

10+
use crate::config;
1011
use crate::parser::{self, ast::SectionType};
1112
use crate::plugins::{PluginManager, PluginPurity};
1213

@@ -243,7 +244,7 @@ pub fn get_address_from_document(content: &str) -> Option<String> {
243244
return Some(addr.trim().to_string());
244245
}
245246
}
246-
std::env::var("GRPCTESTIFY_ADDRESS").ok()
247+
std::env::var(config::ENV_GRPCTESTIFY_ADDRESS).ok()
247248
}
248249

249250
/// Convert validation error to LSP diagnostic

src/parser/ast.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,28 @@ impl GctfDocument {
316316
None
317317
}
318318

319+
/// Get TLS configuration merged with defaults (section values override defaults)
320+
pub fn get_tls_config_with_defaults(
321+
&self,
322+
defaults: &HashMap<String, String>,
323+
) -> Option<HashMap<String, String>> {
324+
let mut merged = defaults.clone();
325+
326+
if let Some(section) = self.first_section(SectionType::Tls)
327+
&& let SectionContent::KeyValues(config) = &section.content
328+
{
329+
for (key, value) in config {
330+
merged.insert(key.clone(), value.clone());
331+
}
332+
}
333+
334+
if merged.is_empty() {
335+
None
336+
} else {
337+
Some(merged)
338+
}
339+
}
340+
319341
/// Get PROTO configuration
320342
pub fn get_proto_config(&self) -> Option<HashMap<String, String>> {
321343
if let Some(section) = self.first_section(SectionType::Proto)
@@ -657,6 +679,39 @@ mod tests {
657679
assert_eq!(result.get("ca_cert"), Some(&"/path/to/ca.pem".to_string()));
658680
}
659681

682+
#[test]
683+
fn test_gctf_document_get_tls_config_with_defaults_env_only() {
684+
let doc = GctfDocument::new("test.gctf".to_string());
685+
let mut defaults = HashMap::new();
686+
defaults.insert("server_name".to_string(), "example.com".to_string());
687+
688+
let result = doc.get_tls_config_with_defaults(&defaults).unwrap();
689+
assert_eq!(result.get("server_name"), Some(&"example.com".to_string()));
690+
}
691+
692+
#[test]
693+
fn test_gctf_document_get_tls_config_with_defaults_section_overrides() {
694+
let mut doc = GctfDocument::new("test.gctf".to_string());
695+
let mut config = HashMap::new();
696+
config.insert("insecure".to_string(), "true".to_string());
697+
doc.sections.push(Section {
698+
section_type: SectionType::Tls,
699+
content: SectionContent::KeyValues(config),
700+
inline_options: InlineOptions::default(),
701+
raw_content: "".to_string(),
702+
start_line: 1,
703+
end_line: 2,
704+
});
705+
706+
let mut defaults = HashMap::new();
707+
defaults.insert("insecure".to_string(), "false".to_string());
708+
defaults.insert("server_name".to_string(), "example.com".to_string());
709+
710+
let result = doc.get_tls_config_with_defaults(&defaults).unwrap();
711+
assert_eq!(result.get("insecure"), Some(&"true".to_string()));
712+
assert_eq!(result.get("server_name"), Some(&"example.com".to_string()));
713+
}
714+
660715
#[test]
661716
fn test_gctf_document_get_proto_config() {
662717
let mut doc = GctfDocument::new("test.gctf".to_string());

0 commit comments

Comments
 (0)