#![allow(clippy::needless_collect)]
#![allow(clippy::match_same_arms)]
#![allow(clippy::equatable_if_let)]
use aperture_cli::cache::models::{CachedCommand, CachedSpec, PaginationInfo};
use aperture_cli::shortcuts::{ResolutionResult, ShortcutResolver};
use std::collections::{BTreeMap, HashMap};
fn create_test_spec_with_multiple_operations() -> CachedSpec {
CachedSpec {
cache_format_version: aperture_cli::cache::models::CACHE_FORMAT_VERSION,
name: "petstore".to_string(),
version: "1.0.0".to_string(),
commands: vec![
CachedCommand {
name: "pets".to_string(),
description: Some("Get pet by ID".to_string()),
summary: None,
operation_id: "getPetById".to_string(),
method: "GET".to_string(),
path: "/pets/{id}".to_string(),
parameters: vec![],
request_body: None,
responses: vec![],
security_requirements: vec![],
tags: vec!["pets".to_string()],
deprecated: false,
external_docs_url: None,
examples: vec![],
display_group: None,
display_name: None,
aliases: vec![],
hidden: false,
pagination: PaginationInfo::default(),
},
CachedCommand {
name: "pets".to_string(),
description: Some("Create a new pet".to_string()),
summary: None,
operation_id: "createPet".to_string(),
method: "POST".to_string(),
path: "/pets".to_string(),
parameters: vec![],
request_body: None,
responses: vec![],
security_requirements: vec![],
tags: vec!["pets".to_string()],
deprecated: false,
external_docs_url: None,
examples: vec![],
display_group: None,
display_name: None,
aliases: vec![],
hidden: false,
pagination: PaginationInfo::default(),
},
CachedCommand {
name: "users".to_string(),
description: Some("List all users".to_string()),
summary: None,
operation_id: "listUsers".to_string(),
method: "GET".to_string(),
path: "/users".to_string(),
parameters: vec![],
request_body: None,
responses: vec![],
security_requirements: vec![],
tags: vec!["users".to_string()],
deprecated: false,
external_docs_url: None,
examples: vec![],
display_group: None,
display_name: None,
aliases: vec![],
hidden: false,
pagination: PaginationInfo::default(),
},
],
base_url: Some("https://api.petstore.com".to_string()),
servers: vec!["https://api.petstore.com".to_string()],
security_schemes: HashMap::new(),
skipped_endpoints: vec![],
server_variables: HashMap::new(),
}
}
#[test]
fn test_operation_id_resolution() {
let spec = create_test_spec_with_multiple_operations();
let mut specs = BTreeMap::new();
specs.insert("petstore".to_string(), spec);
let mut resolver = ShortcutResolver::new();
resolver.index_specs(&specs);
match resolver.resolve_shortcut(&["getPetById".to_string()]) {
ResolutionResult::Resolved(shortcut) => {
assert_eq!(
shortcut.full_command,
vec!["api", "petstore", "pets", "get-pet-by-id"]
);
assert_eq!(shortcut.command.operation_id, "getPetById");
assert!(shortcut.confidence >= 90);
}
_ => panic!("Expected resolved shortcut for getPetById"),
}
match resolver.resolve_shortcut(&["get-pet-by-id".to_string()]) {
ResolutionResult::Resolved(shortcut) => {
assert_eq!(shortcut.command.operation_id, "getPetById");
}
_ => panic!("Expected resolved shortcut for get-pet-by-id"),
}
}
#[test]
fn test_method_path_resolution() {
let spec = create_test_spec_with_multiple_operations();
let mut specs = BTreeMap::new();
specs.insert("petstore".to_string(), spec);
let mut resolver = ShortcutResolver::new();
resolver.index_specs(&specs);
match resolver.resolve_shortcut(&["GET".to_string(), "/pets/{id}".to_string()]) {
ResolutionResult::Resolved(shortcut) => {
assert_eq!(shortcut.command.operation_id, "getPetById");
assert_eq!(shortcut.command.method, "GET");
assert_eq!(shortcut.command.path, "/pets/{id}");
assert!(shortcut.confidence >= 85);
}
_ => panic!("Expected resolved shortcut for GET /pets/{{id}}"),
}
match resolver.resolve_shortcut(&["POST".to_string(), "/pets".to_string()]) {
ResolutionResult::Resolved(shortcut) => {
assert_eq!(shortcut.command.operation_id, "createPet");
assert_eq!(shortcut.command.method, "POST");
}
_ => panic!("Expected resolved shortcut for POST /pets"),
}
}
#[test]
fn test_tag_resolution() {
let spec = create_test_spec_with_multiple_operations();
let mut specs = BTreeMap::new();
specs.insert("petstore".to_string(), spec);
let mut resolver = ShortcutResolver::new();
resolver.index_specs(&specs);
match resolver.resolve_shortcut(&["pets".to_string()]) {
ResolutionResult::Resolved(shortcut) => {
assert!(shortcut.command.tags.contains(&"pets".to_string()));
}
ResolutionResult::Ambiguous(matches) => {
assert!(!matches.is_empty());
for m in &matches {
assert!(m.command.tags.contains(&"pets".to_string()));
}
}
ResolutionResult::NotFound => panic!("Expected to find pets operations"),
}
match resolver.resolve_shortcut(&["pets".to_string(), "get-pet-by-id".to_string()]) {
ResolutionResult::Resolved(shortcut) => {
assert_eq!(shortcut.command.operation_id, "getPetById");
assert!(shortcut.confidence >= 70);
}
ResolutionResult::Ambiguous(matches) => {
println!("Got ambiguous matches: {:?}", matches.len());
for m in &matches {
println!(
" - {} (confidence: {})",
m.command.operation_id, m.confidence
);
}
panic!("Expected resolved shortcut for pets get-pet-by-id, got ambiguous");
}
ResolutionResult::NotFound => {
panic!("Expected resolved shortcut for pets get-pet-by-id, got NotFound");
}
}
}
#[test]
fn test_fuzzy_matching() {
let spec = create_test_spec_with_multiple_operations();
let mut specs = BTreeMap::new();
specs.insert("petstore".to_string(), spec);
let mut resolver = ShortcutResolver::new();
resolver.index_specs(&specs);
match resolver.resolve_shortcut(&["getPetByld".to_string()]) {
ResolutionResult::Resolved(shortcut) => {
assert_eq!(shortcut.command.operation_id, "getPetById");
assert!(shortcut.confidence < 90);
assert!(shortcut.confidence >= 20);
}
ResolutionResult::Ambiguous(_) => {
}
ResolutionResult::NotFound => {
}
}
}
#[test]
fn test_not_found() {
let spec = create_test_spec_with_multiple_operations();
let mut specs = BTreeMap::new();
specs.insert("petstore".to_string(), spec);
let mut resolver = ShortcutResolver::new();
resolver.index_specs(&specs);
if let ResolutionResult::NotFound = resolver.resolve_shortcut(&["nonexistent".to_string()]) {
} else {
}
}
#[test]
fn test_ambiguous_suggestions_format() {
let spec = create_test_spec_with_multiple_operations();
let mut specs = BTreeMap::new();
specs.insert("petstore".to_string(), spec);
let mut resolver = ShortcutResolver::new();
resolver.index_specs(&specs);
let matches = vec![
aperture_cli::shortcuts::ResolvedShortcut {
full_command: vec![
"api".to_string(),
"petstore".to_string(),
"pets".to_string(),
"get-pet-by-id".to_string(),
],
spec: specs.get("petstore").unwrap().clone(),
command: specs.get("petstore").unwrap().commands[0].clone(),
confidence: 80,
},
aperture_cli::shortcuts::ResolvedShortcut {
full_command: vec![
"api".to_string(),
"petstore".to_string(),
"pets".to_string(),
"create-pet".to_string(),
],
spec: specs.get("petstore").unwrap().clone(),
command: specs.get("petstore").unwrap().commands[1].clone(),
confidence: 70,
},
];
let suggestion_text = resolver.format_ambiguous_suggestions(&matches);
assert!(suggestion_text.contains("Multiple commands match"));
assert!(suggestion_text.contains("aperture api petstore"));
assert!(suggestion_text.contains("Get pet by ID"));
assert!(suggestion_text.contains("Create a new pet"));
}
#[test]
fn test_empty_args() {
let spec = create_test_spec_with_multiple_operations();
let mut specs = BTreeMap::new();
specs.insert("petstore".to_string(), spec);
let mut resolver = ShortcutResolver::new();
resolver.index_specs(&specs);
match resolver.resolve_shortcut(&[]) {
ResolutionResult::NotFound => {
}
_ => panic!("Expected NotFound for empty arguments"),
}
}
#[test]
fn test_shortcut_uses_display_names_in_full_command() {
let spec = CachedSpec {
cache_format_version: aperture_cli::cache::models::CACHE_FORMAT_VERSION,
name: "test-api".to_string(),
version: "1.0.0".to_string(),
commands: vec![CachedCommand {
name: "User Management".to_string(),
description: Some("Get user by ID".to_string()),
summary: None,
operation_id: "getUserById".to_string(),
method: "GET".to_string(),
path: "/users/{id}".to_string(),
parameters: vec![],
request_body: None,
responses: vec![],
security_requirements: vec![],
tags: vec!["User Management".to_string()],
deprecated: false,
external_docs_url: None,
examples: vec![],
display_group: Some("users".to_string()),
display_name: Some("fetch".to_string()),
aliases: vec!["get".to_string()],
hidden: false,
pagination: PaginationInfo::default(),
}],
base_url: Some("https://api.example.com".to_string()),
servers: vec![],
security_schemes: HashMap::new(),
skipped_endpoints: vec![],
server_variables: HashMap::new(),
};
let mut specs = BTreeMap::new();
specs.insert("myapi".to_string(), spec);
let mut resolver = ShortcutResolver::new();
resolver.index_specs(&specs);
match resolver.resolve_shortcut(&["getUserById".to_string()]) {
ResolutionResult::Resolved(shortcut) => {
assert_eq!(
shortcut.full_command,
vec!["api", "myapi", "users", "fetch"],
"full_command should use display_group and display_name, not original names"
);
}
other => panic!("Expected Resolved, got: {other:?}"),
}
match resolver.resolve_shortcut(&["fetch".to_string()]) {
ResolutionResult::Resolved(shortcut) => {
assert_eq!(
shortcut.full_command,
vec!["api", "myapi", "users", "fetch"],
);
}
other => panic!("Expected Resolved for display name, got: {other:?}"),
}
match resolver.resolve_shortcut(&["get".to_string()]) {
ResolutionResult::Resolved(shortcut) => {
assert_eq!(
shortcut.full_command,
vec!["api", "myapi", "users", "fetch"],
);
}
other => panic!("Expected Resolved for alias, got: {other:?}"),
}
}