mirror of
https://codeberg.org/JasterV/granc.git
synced 2026-04-26 18:40:05 +00:00
added CLI parsing tests
This commit is contained in:
parent
9002c2ef3f
commit
039ba1da20
1 changed files with 222 additions and 3 deletions
225
granc/src/cli.rs
225
granc/src/cli.rs
|
|
@ -6,7 +6,7 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "granc", version, about = "Dynamic gRPC CLI")]
|
#[command(name = "granc", version, about = "Dynamic gRPC CLI")]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
|
|
@ -24,14 +24,14 @@ pub enum Commands {
|
||||||
endpoint: (String, String),
|
endpoint: (String, String),
|
||||||
|
|
||||||
/// The server URL to connect to (e.g. http://localhost:50051)
|
/// The server URL to connect to (e.g. http://localhost:50051)
|
||||||
#[arg(long, short = 'u', value_parser = parse_body)]
|
#[arg(long, short = 'u')]
|
||||||
url: String,
|
url: String,
|
||||||
|
|
||||||
/// "JSON body (Object for Unary, Array for Streaming)"
|
/// "JSON body (Object for Unary, Array for Streaming)"
|
||||||
#[arg(long, short = 'b', value_parser = parse_body)]
|
#[arg(long, short = 'b', value_parser = parse_body)]
|
||||||
body: serde_json::Value,
|
body: serde_json::Value,
|
||||||
|
|
||||||
#[arg(short = 'h', long = "header", value_parser = parse_header)]
|
#[arg(short = 'H', long = "header", value_parser = parse_header)]
|
||||||
headers: Vec<(String, String)>,
|
headers: Vec<(String, String)>,
|
||||||
|
|
||||||
/// Optional path to a file descriptor set (.bin) to use instead of reflection
|
/// Optional path to a file descriptor set (.bin) to use instead of reflection
|
||||||
|
|
@ -116,3 +116,222 @@ fn parse_header(s: &str) -> Result<(String, String), String> {
|
||||||
fn parse_body(value: &str) -> Result<serde_json::Value, String> {
|
fn parse_body(value: &str) -> Result<serde_json::Value, String> {
|
||||||
serde_json::from_str(value).map_err(|e| format!("Invalid JSON: {e}"))
|
serde_json::from_str(value).map_err(|e| format!("Invalid JSON: {e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_call_command_reflection() {
|
||||||
|
let args = vec![
|
||||||
|
"granc",
|
||||||
|
"call",
|
||||||
|
"helloworld.Greeter/SayHello",
|
||||||
|
"--url",
|
||||||
|
"http://localhost:50051",
|
||||||
|
"--body",
|
||||||
|
r#"{"name": "Ferris"}"#,
|
||||||
|
];
|
||||||
|
|
||||||
|
let cli = Cli::try_parse_from(&args).expect("Parsing failed");
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
Commands::Call {
|
||||||
|
endpoint,
|
||||||
|
url,
|
||||||
|
body,
|
||||||
|
file_descriptor_set,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
assert_eq!(
|
||||||
|
endpoint,
|
||||||
|
("helloworld.Greeter".to_string(), "SayHello".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(url, "http://localhost:50051");
|
||||||
|
assert_eq!(body, serde_json::json!({"name": "Ferris"}));
|
||||||
|
assert!(file_descriptor_set.is_none());
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Call command"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_call_command_with_file_descriptor() {
|
||||||
|
let args = vec![
|
||||||
|
"granc",
|
||||||
|
"call",
|
||||||
|
"helloworld.Greeter/SayHello",
|
||||||
|
"--url",
|
||||||
|
"http://localhost:50051",
|
||||||
|
"--body",
|
||||||
|
r#"{"name": "Ferris"}"#,
|
||||||
|
"--file-descriptor-set",
|
||||||
|
"./descriptors.bin",
|
||||||
|
];
|
||||||
|
|
||||||
|
let cli = Cli::try_parse_from(&args).expect("Parsing failed");
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
Commands::Call {
|
||||||
|
file_descriptor_set,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
assert_eq!(
|
||||||
|
file_descriptor_set.unwrap().to_str().unwrap(),
|
||||||
|
"./descriptors.bin"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Call command"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_call_command_short_flags() {
|
||||||
|
let args = vec![
|
||||||
|
"granc",
|
||||||
|
"call",
|
||||||
|
"svc/mthd",
|
||||||
|
"-u",
|
||||||
|
"http://localhost:50051",
|
||||||
|
"-b",
|
||||||
|
"{}",
|
||||||
|
"-f",
|
||||||
|
"desc.bin",
|
||||||
|
"-H",
|
||||||
|
"auth:bearer",
|
||||||
|
];
|
||||||
|
|
||||||
|
let cli = Cli::try_parse_from(&args).expect("Parsing failed");
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
Commands::Call {
|
||||||
|
url,
|
||||||
|
file_descriptor_set,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
assert_eq!(url, "http://localhost:50051");
|
||||||
|
assert_eq!(file_descriptor_set.unwrap().to_str().unwrap(), "desc.bin");
|
||||||
|
assert_eq!(body, serde_json::json!({}));
|
||||||
|
assert_eq!(headers[0], ("auth".to_string(), "bearer".to_string()));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Call command"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_command_reflection() {
|
||||||
|
let args = vec!["granc", "list", "--url", "http://localhost:50051"];
|
||||||
|
let cli = Cli::try_parse_from(&args).expect("Parsing failed");
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
Commands::List { source } => {
|
||||||
|
assert_eq!(source.url.unwrap(), "http://localhost:50051");
|
||||||
|
assert!(source.file_descriptor_set.is_none());
|
||||||
|
}
|
||||||
|
_ => panic!("Expected List command"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_command_offline() {
|
||||||
|
let args = vec!["granc", "list", "--file-descriptor-set", "desc.bin"];
|
||||||
|
let cli = Cli::try_parse_from(&args).expect("Parsing failed");
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
Commands::List { source } => {
|
||||||
|
assert_eq!(
|
||||||
|
source.file_descriptor_set.unwrap().to_str().unwrap(),
|
||||||
|
"desc.bin"
|
||||||
|
);
|
||||||
|
assert!(source.url.is_none());
|
||||||
|
}
|
||||||
|
_ => panic!("Expected List command"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_describe_command() {
|
||||||
|
let args = vec![
|
||||||
|
"granc",
|
||||||
|
"describe",
|
||||||
|
"helloworld.Greeter",
|
||||||
|
"--url",
|
||||||
|
"http://localhost:50051",
|
||||||
|
];
|
||||||
|
let cli = Cli::try_parse_from(&args).expect("Parsing failed");
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
Commands::Describe { symbol, source } => {
|
||||||
|
assert_eq!(symbol, "helloworld.Greeter");
|
||||||
|
assert!(source.url.is_some());
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Describe command"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Failure Cases ---
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fail_invalid_json_body() {
|
||||||
|
let args = vec!["granc", "call", "s/m", "-u", "x", "--body", "{invalid_json"];
|
||||||
|
let err = Cli::try_parse_from(&args).unwrap_err();
|
||||||
|
// Should verify that the error comes from the body parser
|
||||||
|
assert!(err.to_string().contains("Invalid JSON"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fail_invalid_endpoint_format() {
|
||||||
|
let args = vec![
|
||||||
|
"granc",
|
||||||
|
"call",
|
||||||
|
"OnlyServiceNoMethod", // Missing '/'
|
||||||
|
"-u",
|
||||||
|
"x",
|
||||||
|
"-b",
|
||||||
|
"{}",
|
||||||
|
];
|
||||||
|
let err = Cli::try_parse_from(&args).unwrap_err();
|
||||||
|
assert!(err.to_string().contains("Invalid endpoint format"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fail_list_requires_source() {
|
||||||
|
let args = vec!["granc", "list"];
|
||||||
|
let err = Cli::try_parse_from(&args).unwrap_err();
|
||||||
|
// Clap error for missing required arguments in group
|
||||||
|
assert!(err.kind() == clap::error::ErrorKind::MissingRequiredArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fail_list_mutual_exclusion() {
|
||||||
|
let args = vec![
|
||||||
|
"granc",
|
||||||
|
"list",
|
||||||
|
"--url",
|
||||||
|
"http://host",
|
||||||
|
"--file-descriptor-set",
|
||||||
|
"file.bin",
|
||||||
|
];
|
||||||
|
let err = Cli::try_parse_from(&args).unwrap_err();
|
||||||
|
// Clap error for argument conflict
|
||||||
|
assert!(err.kind() == clap::error::ErrorKind::ArgumentConflict);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fail_describe_mutual_exclusion() {
|
||||||
|
let args = vec![
|
||||||
|
"granc",
|
||||||
|
"describe",
|
||||||
|
"Symbol",
|
||||||
|
"-u",
|
||||||
|
"http://host",
|
||||||
|
"-f",
|
||||||
|
"file.bin",
|
||||||
|
];
|
||||||
|
let err = Cli::try_parse_from(&args).unwrap_err();
|
||||||
|
assert!(err.kind() == clap::error::ErrorKind::ArgumentConflict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue