Skip to content

Commit 7d223cc

Browse files
committed
test: Add regression tests for issue #4707
1 parent de57287 commit 7d223cc

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

tests/builder/issue_4707.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/// Tests for GitHub issue #4707: `requires` validation should not be bypassed when
2+
/// arguments are in a mutually exclusive group.
3+
///
4+
/// This issue appears to have been resolved in the current version of clap.
5+
/// These tests verify that the requires validation works correctly.
6+
use clap::{Arg, ArgAction, ArgGroup, Command, error::ErrorKind};
7+
8+
#[test]
9+
fn issue_4707_requires_should_be_validated_when_args_are_in_group() {
10+
// This test ensures that `requires` validation is NOT bypassed
11+
// when arguments are in a mutually exclusive group
12+
let cmd = Command::new("test")
13+
.arg(Arg::new("one").short('1').action(ArgAction::SetTrue).requires("foo"))
14+
.arg(Arg::new("two").short('2').action(ArgAction::SetTrue).requires("foo"))
15+
.arg(Arg::new("foo").short('f').action(ArgAction::SetTrue))
16+
.group(ArgGroup::new("group").args(["one", "two"]));
17+
18+
// This should fail because --foo is required when either -1 or -2 is present
19+
let result = cmd.try_get_matches_from(vec!["test", "-1"]);
20+
21+
// Verify the validation works correctly (issue is fixed)
22+
assert!(result.is_err(), "Should fail because -1 requires foo but foo is missing");
23+
assert_eq!(result.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
24+
}
25+
26+
#[test]
27+
fn issue_4707_mutually_exclusive_group_bypasses_requires() {
28+
// Test with explicitly mutually exclusive group
29+
let cmd = Command::new("test")
30+
.arg(Arg::new("one").short('1').action(ArgAction::SetTrue).requires("foo"))
31+
.arg(Arg::new("two").short('2').action(ArgAction::SetTrue).requires("foo"))
32+
.arg(Arg::new("foo").short('f').action(ArgAction::SetTrue))
33+
.group(ArgGroup::new("group").args(["one", "two"]).multiple(false)); // explicit mutually exclusive
34+
35+
// This should fail because --foo is required when either -1 or -2 is present
36+
let result = cmd.try_get_matches_from(vec!["test", "-1"]);
37+
38+
assert!(result.is_err(), "Should fail because -1 requires foo but foo is missing");
39+
assert_eq!(result.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
40+
}
41+
42+
#[test]
43+
fn issue_4707_requires_should_work_when_required_arg_provided() {
44+
let cmd = Command::new("test")
45+
.arg(Arg::new("one").short('1').action(ArgAction::SetTrue).requires("foo"))
46+
.arg(Arg::new("two").short('2').action(ArgAction::SetTrue).requires("foo"))
47+
.arg(Arg::new("foo").short('f').action(ArgAction::SetTrue))
48+
.group(ArgGroup::new("group").args(["one", "two"]));
49+
50+
// This should succeed because --foo is provided
51+
let result = cmd.try_get_matches_from(vec!["test", "-1", "-f"]);
52+
53+
assert!(result.is_ok(), "Should have succeeded when required argument is provided");
54+
}
55+
56+
#[test]
57+
fn issue_4707_group_requires_validation() {
58+
// Test with group that has 'requires' on the group itself
59+
let cmd = Command::new("test")
60+
.arg(Arg::new("one").short('1').action(ArgAction::SetTrue))
61+
.arg(Arg::new("two").short('2').action(ArgAction::SetTrue))
62+
.arg(Arg::new("foo").short('f').action(ArgAction::SetTrue))
63+
.group(ArgGroup::new("group").args(["one", "two"]).requires("foo"));
64+
65+
// This should fail because group requires 'foo'
66+
let result = cmd.try_get_matches_from(vec!["test", "-1"]);
67+
68+
assert!(result.is_err(), "Should fail because group requires foo");
69+
assert_eq!(result.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
70+
}
71+
72+
#[test]
73+
fn issue_4707_complex_interaction_test() {
74+
// Test complex interactions between mutually exclusive groups and requires
75+
let cmd = Command::new("test")
76+
.arg(Arg::new("verbose").short('v').action(ArgAction::SetTrue).requires("output"))
77+
.arg(Arg::new("quiet").short('q').action(ArgAction::SetTrue).requires("output"))
78+
.arg(Arg::new("output").short('o').action(ArgAction::SetTrue))
79+
.group(ArgGroup::new("verbosity").args(["verbose", "quiet"]).multiple(false));
80+
81+
// Test case 1: One argument from group without its required dependency
82+
let result1 = cmd.clone().try_get_matches_from(vec!["test", "-v"]);
83+
assert!(result1.is_err(), "Should fail because -v requires output");
84+
assert_eq!(result1.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
85+
86+
// Test case 2: Other argument from group without its required dependency
87+
let result2 = cmd.clone().try_get_matches_from(vec!["test", "-q"]);
88+
assert!(result2.is_err(), "Should fail because -q requires output");
89+
assert_eq!(result2.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
90+
91+
// Test case 3: Valid usage with dependency
92+
let result3 = cmd.clone().try_get_matches_from(vec!["test", "-v", "-o"]);
93+
assert!(result3.is_ok(), "Should succeed when dependency is provided");
94+
}

0 commit comments

Comments
 (0)