From 94d5e178d225b0d7329835725e612658580e83a8 Mon Sep 17 00:00:00 2001 From: GW_MC <72297530+GWMCwing@users.noreply.github.com> Date: Sun, 31 May 2026 02:09:51 +0000 Subject: [PATCH] feat: refactor import_certs command into separate module for improved structure --- apps/nxmesh-agent/src/cli/import_certs.rs | 119 ++++++++++++++++++++ apps/nxmesh-agent/src/cli/mod.rs | 131 +--------------------- 2 files changed, 123 insertions(+), 127 deletions(-) create mode 100644 apps/nxmesh-agent/src/cli/import_certs.rs diff --git a/apps/nxmesh-agent/src/cli/import_certs.rs b/apps/nxmesh-agent/src/cli/import_certs.rs new file mode 100644 index 0000000..de92562 --- /dev/null +++ b/apps/nxmesh-agent/src/cli/import_certs.rs @@ -0,0 +1,119 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(about = "Import certificates for agent from zip file or separate cert and key files")] +pub struct ImportCertsCommand { + /// Zip file containing ca.pem cert.pem and key.pem + #[arg(value_name = "ZIP_FILE", group = "input_source")] + zip: Option, + /// Certificate name in zip file, required if using zip input + #[arg( + long, + group = "input_source", + requires = "zip", + default_value = "cert.pem", + value_name = "CERT_NAME" + )] + cert_name: Option, + /// Key name in zip file, required if using zip input + #[arg( + long, + group = "input_source", + requires = "zip", + default_value = "key.pem", + value_name = "KEY_NAME" + )] + key_name: Option, + /// CA certificate name in zip file, required if using zip input + #[arg( + long, + group = "input_source", + requires = "zip", + default_value = "ca.pem", + value_name = "CA_NAME" + )] + ca_name: Option, + + // Separate cert and key file inputs, required if not using zip input + /// Certificate file path + #[arg( + long, + group = "input_source", + requires = "key", + conflicts_with = "zip", + value_name = "CERT_FILE" + )] + cert: Option, + + /// Key file path + #[arg( + long, + group = "input_source", + requires = "cert", + conflicts_with = "zip", + value_name = "KEY_FILE" + )] + key: Option, + + /// Master CA certificate file path for verifying master identity, optional if the CA certificate is already trusted by the system + /// This is required if the master server uses a self-signed certificate that is not trusted by the system + #[arg( + long, + group = "input_source", + conflicts_with = "zip", + value_name = "CA_CERT_FILE" + )] + ca_cert: Option, +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn parses_import_certs_with_zip_defaults() { + let parsed = ImportCertsCommand::try_parse_from(["import-certs", "bundle.zip"]); + assert!(parsed.is_ok()); + + let parsed = parsed.ok(); + assert!(parsed.is_some()); + let parsed = parsed.unwrap_or_else(|| unreachable!()); + + assert_eq!(parsed.zip.as_deref(), Some("bundle.zip")); + assert_eq!(parsed.cert_name.as_deref(), Some("cert.pem")); + assert_eq!(parsed.key_name.as_deref(), Some("key.pem")); + assert_eq!(parsed.ca_name.as_deref(), Some("ca.pem")); + assert!(parsed.cert.is_none()); + assert!(parsed.key.is_none()); + assert!(parsed.ca_cert.is_none()); + } + + #[test] + fn rejects_import_certs_with_separate_files() { + let parsed = ImportCertsCommand::try_parse_from([ + "import-certs", + "--cert", + "agent.crt", + "--key", + "agent.key", + "--ca-cert", + "ca.crt", + ]); + assert!(parsed.is_err()); + } + + #[test] + fn rejects_conflicting_zip_and_separate_inputs() { + let parsed = ImportCertsCommand::try_parse_from([ + "import-certs", + "bundle.zip", + "--cert", + "agent.crt", + "--key", + "agent.key", + ]); + + assert!(parsed.is_err()); + } +} diff --git a/apps/nxmesh-agent/src/cli/mod.rs b/apps/nxmesh-agent/src/cli/mod.rs index ad41011..a21f44e 100644 --- a/apps/nxmesh-agent/src/cli/mod.rs +++ b/apps/nxmesh-agent/src/cli/mod.rs @@ -1,5 +1,7 @@ use clap::{Parser, Subcommand}; +pub mod import_certs; + #[derive(Parser)] #[command(version, about, long_about = None)] pub struct Cli { @@ -13,78 +15,14 @@ pub struct Cli { #[derive(Subcommand)] pub enum Commands { - #[command(about = "Import certificates for agent from zip file or separate cert and key files")] - ImportCerts { - // Zip file input, mutually exclusive with separate cert and key file inputs - /// Zip file containing ca.pem cert.pem and key.pem - #[arg(value_name = "ZIP_FILE", group = "input_source")] - zip: Option, - /// Certificate name in zip file, required if using zip input - #[arg( - long, - group = "input_source", - requires = "zip", - default_value = "cert.pem", - value_name = "CERT_NAME" - )] - cert_name: Option, - /// Key name in zip file, required if using zip input - #[arg( - long, - group = "input_source", - requires = "zip", - default_value = "key.pem", - value_name = "KEY_NAME" - )] - key_name: Option, - /// CA certificate name in zip file, required if using zip input - #[arg( - long, - group = "input_source", - requires = "zip", - default_value = "ca.pem", - value_name = "CA_NAME" - )] - ca_name: Option, - - // Separate cert and key file inputs, required if not using zip input - /// Certificate file path - #[arg( - long, - group = "input_source", - requires = "key", - conflicts_with = "zip", - value_name = "CERT_FILE" - )] - cert: Option, - - /// Key file path - #[arg( - long, - group = "input_source", - requires = "cert", - conflicts_with = "zip", - value_name = "KEY_FILE" - )] - key: Option, - - /// Master CA certificate file path for verifying master identity, optional if the CA certificate is already trusted by the system - /// This is required if the master server uses a self-signed certificate that is not trusted by the system - #[arg( - long, - group = "input_source", - conflicts_with = "zip", - value_name = "CA_CERT_FILE" - )] - ca_cert: Option, - }, + ImportCerts(import_certs::ImportCertsCommand), } #[cfg(test)] mod tests { use clap::Parser; - use super::{Cli, Commands}; + use super::*; #[test] fn parses_serve_flag_without_subcommand() { @@ -98,65 +36,4 @@ mod tests { assert!(parsed.serve); assert!(parsed.command.is_none()); } - - #[test] - fn parses_import_certs_with_zip_defaults() { - let parsed = Cli::try_parse_from(["nxmesh-agent", "import-certs", "bundle.zip"]); - assert!(parsed.is_ok()); - - let parsed = parsed.ok(); - assert!(parsed.is_some()); - let parsed = parsed.unwrap_or_else(|| unreachable!()); - - match parsed.command { - Some(Commands::ImportCerts { - zip, - cert_name, - key_name, - ca_name, - cert, - key, - ca_cert, - }) => { - assert_eq!(zip.as_deref(), Some("bundle.zip")); - assert_eq!(cert_name.as_deref(), Some("cert.pem")); - assert_eq!(key_name.as_deref(), Some("key.pem")); - assert_eq!(ca_name.as_deref(), Some("ca.pem")); - assert!(cert.is_none()); - assert!(key.is_none()); - assert!(ca_cert.is_none()); - } - _ => unreachable!(), - } - } - - #[test] - fn rejects_import_certs_with_separate_files() { - let parsed = Cli::try_parse_from([ - "nxmesh-agent", - "import-certs", - "--cert", - "agent.crt", - "--key", - "agent.key", - "--ca-cert", - "ca.crt", - ]); - assert!(parsed.is_err()); - } - - #[test] - fn rejects_conflicting_zip_and_separate_inputs() { - let parsed = Cli::try_parse_from([ - "nxmesh-agent", - "import-certs", - "bundle.zip", - "--cert", - "agent.crt", - "--key", - "agent.key", - ]); - - assert!(parsed.is_err()); - } }