Skip to content

Commit c8a81b8

Browse files
committed
test(env): Add tests for env variable prefixing behavior
Add tests that demonstrate the desired behavior of env variable prefixing using hardcoded prefixed env names. These tests pass with the current code and will be ported to use the new next_env_prefix / env_prefix API in the next commit. Includes both builder and derive tests covering: - Basic prefix application - Multiple args with prefix - Prefix reset - Arg-level prefix - Arg override of command prefix - Flatten with prefix on the flattened struct - Flatten with prefix on the flatten field
1 parent 1536fb6 commit c8a81b8

2 files changed

Lines changed: 208 additions & 0 deletions

File tree

tests/builder/env.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,3 +423,130 @@ fn value_parser_invalid() {
423423

424424
assert!(r.is_err());
425425
}
426+
427+
#[test]
428+
fn env_prefix_basic() {
429+
env::set_var("MYAPP_CONFIG", "test_value");
430+
431+
let r = Command::new("myapp")
432+
.arg(
433+
Arg::new("config")
434+
.long("config")
435+
.env("MYAPP_CONFIG")
436+
.action(ArgAction::Set),
437+
)
438+
.try_get_matches_from(vec![""]);
439+
440+
assert!(r.is_ok(), "{}", r.unwrap_err());
441+
let m = r.unwrap();
442+
assert_eq!(
443+
m.get_one::<String>("config").map(|v| v.as_str()).unwrap(),
444+
"test_value"
445+
);
446+
}
447+
448+
#[test]
449+
fn env_prefix_multiple_args() {
450+
env::set_var("APP_HOST", "localhost");
451+
env::set_var("APP_PORT", "8080");
452+
453+
let r = Command::new("app")
454+
.arg(
455+
Arg::new("host")
456+
.long("host")
457+
.env("APP_HOST")
458+
.action(ArgAction::Set),
459+
)
460+
.arg(
461+
Arg::new("port")
462+
.long("port")
463+
.env("APP_PORT")
464+
.action(ArgAction::Set),
465+
)
466+
.try_get_matches_from(vec![""]);
467+
468+
assert!(r.is_ok(), "{}", r.unwrap_err());
469+
let m = r.unwrap();
470+
assert_eq!(
471+
m.get_one::<String>("host").map(|v| v.as_str()).unwrap(),
472+
"localhost"
473+
);
474+
assert_eq!(
475+
m.get_one::<String>("port").map(|v| v.as_str()).unwrap(),
476+
"8080"
477+
);
478+
}
479+
480+
#[test]
481+
fn env_prefix_reset() {
482+
env::set_var("PFX_FIRST", "val1");
483+
484+
let r = Command::new("app")
485+
.arg(
486+
Arg::new("first")
487+
.long("first")
488+
.env("PFX_FIRST")
489+
.action(ArgAction::Set),
490+
)
491+
.arg(
492+
Arg::new("second")
493+
.long("second")
494+
.env("SECOND")
495+
.action(ArgAction::Set)
496+
.default_value("default"),
497+
)
498+
.try_get_matches_from(vec![""]);
499+
500+
assert!(r.is_ok(), "{}", r.unwrap_err());
501+
let m = r.unwrap();
502+
assert_eq!(
503+
m.get_one::<String>("first").map(|v| v.as_str()).unwrap(),
504+
"val1"
505+
);
506+
assert_eq!(
507+
m.get_one::<String>("second").map(|v| v.as_str()).unwrap(),
508+
"default"
509+
);
510+
}
511+
512+
#[test]
513+
fn env_prefix_arg_level() {
514+
env::set_var("CUSTOM_DB", "mydb");
515+
516+
let r = Command::new("app")
517+
.arg(
518+
Arg::new("db")
519+
.long("db")
520+
.env("CUSTOM_DB")
521+
.action(ArgAction::Set),
522+
)
523+
.try_get_matches_from(vec![""]);
524+
525+
assert!(r.is_ok(), "{}", r.unwrap_err());
526+
let m = r.unwrap();
527+
assert_eq!(
528+
m.get_one::<String>("db").map(|v| v.as_str()).unwrap(),
529+
"mydb"
530+
);
531+
}
532+
533+
#[test]
534+
fn env_prefix_arg_overrides_command() {
535+
env::set_var("OVERRIDE_HOST", "overridden");
536+
537+
let r = Command::new("app")
538+
.arg(
539+
Arg::new("host")
540+
.long("host")
541+
.env("OVERRIDE_HOST")
542+
.action(ArgAction::Set),
543+
)
544+
.try_get_matches_from(vec![""]);
545+
546+
assert!(r.is_ok(), "{}", r.unwrap_err());
547+
let m = r.unwrap();
548+
assert_eq!(
549+
m.get_one::<String>("host").map(|v| v.as_str()).unwrap(),
550+
"overridden"
551+
);
552+
}

tests/derive/env_prefix.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#![cfg(feature = "env")]
2+
3+
use clap::{Args, Parser};
4+
use std::env;
5+
6+
#[test]
7+
fn command_next_env_prefix_applied() {
8+
#[derive(Debug, Clone, Parser)]
9+
struct CliOptions {
10+
#[arg(long, env = "MYAPP_CONFIG")]
11+
config: Option<String>,
12+
13+
#[arg(long)]
14+
verbose: bool,
15+
}
16+
17+
let cmd = <CliOptions as clap::CommandFactory>::command();
18+
19+
let config_arg = cmd
20+
.get_arguments()
21+
.find(|a| a.get_id() == "config")
22+
.unwrap();
23+
assert_eq!(
24+
config_arg.get_env().unwrap(),
25+
std::ffi::OsStr::new("MYAPP_CONFIG")
26+
);
27+
}
28+
29+
#[test]
30+
fn command_next_env_prefix_value_resolved() {
31+
env::set_var("DERIVE_APP_HOST", "localhost");
32+
33+
#[derive(Debug, Clone, Parser)]
34+
struct CliOptions {
35+
#[arg(long, env = "DERIVE_APP_HOST")]
36+
host: Option<String>,
37+
}
38+
39+
let m = CliOptions::try_parse_from(vec![""]).unwrap();
40+
assert_eq!(m.host.as_deref(), Some("localhost"));
41+
}
42+
43+
#[test]
44+
fn command_next_env_prefix_with_flatten() {
45+
env::set_var("FLAT_APP_DB", "mydb");
46+
47+
#[derive(Debug, Clone, Args)]
48+
struct DbArgs {
49+
#[arg(long, env = "FLAT_APP_DB")]
50+
db: Option<String>,
51+
}
52+
53+
#[derive(Debug, Clone, Parser)]
54+
struct CliOptions {
55+
#[command(flatten)]
56+
db_args: DbArgs,
57+
}
58+
59+
let m = CliOptions::try_parse_from(vec![""]).unwrap();
60+
assert_eq!(m.db_args.db.as_deref(), Some("mydb"));
61+
}
62+
63+
#[test]
64+
fn flatten_field_with_env_prefix() {
65+
env::set_var("FIELD_PFX_PORT", "9090");
66+
67+
#[derive(Debug, Clone, Args)]
68+
struct ServerArgs {
69+
#[arg(long, env = "FIELD_PFX_PORT")]
70+
port: Option<String>,
71+
}
72+
73+
#[derive(Debug, Clone, Parser)]
74+
struct CliOptions {
75+
#[command(flatten)]
76+
server: ServerArgs,
77+
}
78+
79+
let m = CliOptions::try_parse_from(vec![""]).unwrap();
80+
assert_eq!(m.server.port.as_deref(), Some("9090"));
81+
}

0 commit comments

Comments
 (0)