1use super::utils;
2
3use clap::{arg, error::Error, error::ErrorKind, value_parser, Arg, Command};
4
5#[track_caller]
6fn assert_error<F: clap::error::ErrorFormatter>(
7    err: Error<F>,
8    expected_kind: ErrorKind,
9    expected_output: &str,
10    stderr: bool,
11) {
12    let actual_output = err.to_string();
13    assert_eq!(
14        stderr,
15        err.use_stderr(),
16        "Should Use STDERR failed. Should be {} but is {}",
17        stderr,
18        err.use_stderr()
19    );
20    assert_eq!(expected_kind, err.kind());
21    utils::assert_eq(expected_output, actual_output)
22}
23
24#[test]
25fn app_error() {
26    static MESSAGE: &str = "error: failed for mysterious reasons
27
28Usage: test [OPTIONS] --all
29
30For more information, try '--help'.
31";
32    let cmd = Command::new("test")
33        .arg(
34            Arg::new("all")
35                .short('a')
36                .long("all")
37                .required(true)
38                .action(clap::ArgAction::SetTrue)
39                .help("Also do versioning for private crates (will not be published)"),
40        )
41        .arg(
42            Arg::new("exact")
43                .long("exact")
44                .help("Specify inter dependency version numbers exactly with `=`"),
45        )
46        .arg(
47            Arg::new("no_git_commit")
48                .long("no-git-commit")
49                .help("Do not commit version changes"),
50        )
51        .arg(
52            Arg::new("no_git_push")
53                .long("no-git-push")
54                .help("Do not push generated commit and tags to git remote"),
55        );
56    let mut cmd = cmd;
57    let expected_kind = ErrorKind::InvalidValue;
58    let err = cmd.error(expected_kind, "failed for mysterious reasons");
59    assert_error(err, expected_kind, MESSAGE, true);
60}
61
62#[test]
63fn value_validation_has_newline() {
64    let res = Command::new("test")
65        .arg(
66            arg!(<PORT>)
67                .value_parser(value_parser!(usize))
68                .help("Network port to use"),
69        )
70        .try_get_matches_from(["test", "foo"]);
71
72    assert!(res.is_err());
73    let err = res.unwrap_err();
74    assert!(
75        err.to_string().ends_with('\n'),
76        "Errors should have a trailing newline, got {:?}",
77        err.to_string()
78    );
79}
80
81#[test]
82fn kind_prints_help() {
83    let cmd = Command::new("test");
84    let res = cmd
85        .try_get_matches_from(["test", "--help"])
86        .map_err(|e| e.apply::<clap::error::KindFormatter>());
87    assert!(res.is_err());
88    let err = res.unwrap_err();
89    let expected_kind = ErrorKind::DisplayHelp;
90    static MESSAGE: &str = "\
91Usage: test
92
93Options:
94  -h, --help  Print help
95";
96    assert_error(err, expected_kind, MESSAGE, false);
97}
98
99#[test]
100fn kind_formats_validation_error() {
101    let cmd = Command::new("test");
102    let res = cmd
103        .try_get_matches_from(["test", "unused"])
104        .map_err(|e| e.apply::<clap::error::KindFormatter>());
105    assert!(res.is_err());
106    let err = res.unwrap_err();
107    let expected_kind = ErrorKind::UnknownArgument;
108    static MESSAGE: &str = "\
109error: unexpected argument found
110";
111    assert_error(err, expected_kind, MESSAGE, true);
112}
113
114#[test]
115#[cfg(feature = "error-context")]
116fn rich_formats_validation_error() {
117    let cmd = Command::new("test");
118    let res = cmd.try_get_matches_from(["test", "unused"]);
119    assert!(res.is_err());
120    let err = res.unwrap_err();
121    let expected_kind = ErrorKind::UnknownArgument;
122    static MESSAGE: &str = "\
123error: unexpected argument 'unused' found
124
125Usage: test
126
127For more information, try '--help'.
128";
129    assert_error(err, expected_kind, MESSAGE, true);
130}
131
132#[test]
133#[cfg(feature = "error-context")]
134fn suggest_trailing() {
135    let cmd = Command::new("rg").arg(arg!([PATTERN]));
136
137    let res = cmd.try_get_matches_from(["rg", "--foo"]);
138    assert!(res.is_err());
139    let err = res.unwrap_err();
140    let expected_kind = ErrorKind::UnknownArgument;
141    static MESSAGE: &str = "\
142error: unexpected argument '--foo' found
143
144  note: to pass '--foo' as a value, use '-- --foo'
145
146Usage: rg [PATTERN]
147
148For more information, try '--help'.
149";
150    assert_error(err, expected_kind, MESSAGE, true);
151}
152
153#[test]
154#[cfg(feature = "error-context")]
155fn trailing_already_in_use() {
156    let cmd = Command::new("rg").arg(arg!([PATTERN]));
157
158    let res = cmd.try_get_matches_from(["rg", "--", "--foo", "--foo"]);
159    assert!(res.is_err());
160    let err = res.unwrap_err();
161    let expected_kind = ErrorKind::UnknownArgument;
162    static MESSAGE: &str = "\
163error: unexpected argument '--foo' found
164
165Usage: rg [PATTERN]
166
167For more information, try '--help'.
168";
169    assert_error(err, expected_kind, MESSAGE, true);
170}
171
172#[test]
173#[cfg(feature = "error-context")]
174fn cant_use_trailing() {
175    let cmd = Command::new("test");
176
177    let res = cmd.try_get_matches_from(["test", "--foo"]);
178    assert!(res.is_err());
179    let err = res.unwrap_err();
180    let expected_kind = ErrorKind::UnknownArgument;
181    static MESSAGE: &str = "\
182error: unexpected argument '--foo' found
183
184Usage: test
185
186For more information, try '--help'.
187";
188    assert_error(err, expected_kind, MESSAGE, true);
189}
190