Skip to main content

node_headless/
cli.rs

1//! Command-line interface for the headless node.
2
3use clap::{Parser, Subcommand};
4
5#[derive(Debug, Parser)]
6#[command(name = "node-headless")]
7#[command(author = "wowlab")]
8#[command(version)]
9#[command(about = "Headless WoW Lab simulation node for servers", long_about = None)]
10pub(crate) struct Cli {
11    #[command(subcommand)]
12    pub(crate) command: Option<Command>,
13
14    /// Skip automatic update check on startup.
15    #[arg(long, global = true)]
16    pub(crate) no_update: bool,
17}
18
19#[derive(Debug, Eq, PartialEq, Subcommand)]
20pub(crate) enum Command {
21    /// Update to the latest version.
22    Update {
23        /// Check for updates without installing.
24        #[arg(long)]
25        check: bool,
26    },
27    /// Start the node (default behavior)
28    Run,
29}
30
31#[cfg(test)]
32mod tests {
33    use clap::{CommandFactory, error::ErrorKind};
34    use googletest::prelude::*;
35
36    use super::*;
37
38    #[gtest]
39    fn no_subcommand_selects_implicit_run_with_updates_enabled() -> Result<()> {
40        let cli = Cli::try_parse_from(["node-headless"]).or_fail()?;
41
42        verify_that!(cli.command, none())?;
43
44        verify_false!(cli.no_update)
45    }
46
47    #[gtest]
48    fn update_check_is_distinct_from_update_install() -> Result<()> {
49        let check = Cli::try_parse_from(["node-headless", "update", "--check"]).or_fail()?;
50        let install = Cli::try_parse_from(["node-headless", "update"]).or_fail()?;
51
52        verify_that!(check.command, eq(&Some(Command::Update { check: true })))?;
53
54        verify_that!(install.command, eq(&Some(Command::Update { check: false })))
55    }
56
57    #[gtest]
58    fn no_update_is_global_before_or_after_run_subcommand() -> Result<()> {
59        let before = Cli::try_parse_from(["node-headless", "--no-update", "run"]).or_fail()?;
60        let after = Cli::try_parse_from(["node-headless", "run", "--no-update"]).or_fail()?;
61
62        verify_that!(before.command, eq(&Some(Command::Run)))?;
63        verify_true!(before.no_update)?;
64        verify_that!(after.command, eq(&Some(Command::Run)))?;
65
66        verify_true!(after.no_update)
67    }
68
69    #[gtest]
70    fn invalid_subcommand_remains_a_command_line_error() -> Result<()> {
71        let error = Cli::try_parse_from(["node-headless", "unknown"])
72            .err()
73            .or_fail()?;
74
75        verify_that!(error.kind(), eq(ErrorKind::InvalidSubcommand))
76    }
77
78    #[gtest]
79    fn top_level_help_lists_both_commands_and_global_flag() -> Result<()> {
80        let help = Cli::command().render_long_help().to_string();
81
82        verify_that!(
83            help,
84            all!(
85                contains_substring("update  Update to the latest version"),
86                contains_substring("run     Start the node (default behavior)"),
87                contains_substring("--no-update"),
88                contains_substring("Skip automatic update check on startup")
89            )
90        )
91    }
92}