feat: refactor import_certs command into separate module for improved structure

This commit is contained in:
GW_MC
2026-05-31 02:09:51 +00:00
parent cd28b5009a
commit 94d5e178d2
2 changed files with 123 additions and 127 deletions

View File

@@ -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<String>,
/// 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<String>,
/// 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<String>,
/// 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<String>,
// 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<String>,
/// Key file path
#[arg(
long,
group = "input_source",
requires = "cert",
conflicts_with = "zip",
value_name = "KEY_FILE"
)]
key: Option<String>,
/// 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<String>,
}
#[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());
}
}

View File

@@ -1,5 +1,7 @@
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
pub mod import_certs;
#[derive(Parser)] #[derive(Parser)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
pub struct Cli { pub struct Cli {
@@ -13,78 +15,14 @@ pub struct Cli {
#[derive(Subcommand)] #[derive(Subcommand)]
pub enum Commands { pub enum Commands {
#[command(about = "Import certificates for agent from zip file or separate cert and key files")] ImportCerts(import_certs::ImportCertsCommand),
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<String>,
/// 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<String>,
/// 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<String>,
/// 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<String>,
// 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<String>,
/// Key file path
#[arg(
long,
group = "input_source",
requires = "cert",
conflicts_with = "zip",
value_name = "KEY_FILE"
)]
key: Option<String>,
/// 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<String>,
},
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use clap::Parser; use clap::Parser;
use super::{Cli, Commands}; use super::*;
#[test] #[test]
fn parses_serve_flag_without_subcommand() { fn parses_serve_flag_without_subcommand() {
@@ -98,65 +36,4 @@ mod tests {
assert!(parsed.serve); assert!(parsed.serve);
assert!(parsed.command.is_none()); 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());
}
} }