test: Added tests for the new gopass provider
This commit is contained in:
+46
-46
@@ -1,7 +1,8 @@
|
||||
use crate::command::preview_command;
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use clap_complete::CompletionCandidate;
|
||||
use futures::future::join_all;
|
||||
use gman::config::{load_config, Config, RunConfig};
|
||||
use gman::config::{Config, RunConfig, load_config};
|
||||
use log::{debug, error};
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
@@ -9,7 +10,6 @@ use std::ffi::{OsStr, OsString};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use clap_complete::CompletionCandidate;
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
const ARG_FORMAT_PLACEHOLDER_KEY: &str = "{{key}}";
|
||||
@@ -256,48 +256,49 @@ pub fn parse_args(
|
||||
}
|
||||
|
||||
pub fn run_config_completer(current: &OsStr) -> Vec<CompletionCandidate> {
|
||||
let cur = current.to_string_lossy();
|
||||
match load_config() {
|
||||
Ok(config) => {
|
||||
if let Some(run_configs) = config.run_configs {
|
||||
run_configs
|
||||
.iter()
|
||||
.filter(|rc| {
|
||||
rc.name
|
||||
.as_ref()
|
||||
.expect("run config has no name")
|
||||
.starts_with(&*cur)
|
||||
})
|
||||
.map(|rc| {
|
||||
CompletionCandidate::new(rc.name.as_ref().expect("run config has no name"))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
Err(_) => vec![],
|
||||
}
|
||||
let cur = current.to_string_lossy();
|
||||
match load_config() {
|
||||
Ok(config) => {
|
||||
if let Some(run_configs) = config.run_configs {
|
||||
run_configs
|
||||
.iter()
|
||||
.filter(|rc| {
|
||||
rc.name
|
||||
.as_ref()
|
||||
.expect("run config has no name")
|
||||
.starts_with(&*cur)
|
||||
})
|
||||
.map(|rc| {
|
||||
CompletionCandidate::new(rc.name.as_ref().expect("run config has no name"))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
Err(_) => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn secrets_completer(current: &OsStr) -> Vec<CompletionCandidate> {
|
||||
let cur = current.to_string_lossy();
|
||||
match load_config() {
|
||||
Ok(config) => {
|
||||
let mut provider_config = match config.extract_provider_config(None) {
|
||||
Ok(pc) => pc,
|
||||
Err(_) => return vec![],
|
||||
};
|
||||
let secrets_provider = provider_config.extract_provider();
|
||||
let h = Handle::current();
|
||||
tokio::task::block_in_place(|| h.block_on(secrets_provider.list_secrets())).unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|s| s.starts_with(&*cur))
|
||||
.map(CompletionCandidate::new)
|
||||
.collect()
|
||||
}
|
||||
Err(_) => vec![],
|
||||
}
|
||||
let cur = current.to_string_lossy();
|
||||
match load_config() {
|
||||
Ok(config) => {
|
||||
let mut provider_config = match config.extract_provider_config(None) {
|
||||
Ok(pc) => pc,
|
||||
Err(_) => return vec![],
|
||||
};
|
||||
let secrets_provider = provider_config.extract_provider();
|
||||
let h = Handle::current();
|
||||
tokio::task::block_in_place(|| h.block_on(secrets_provider.list_secrets()))
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|s| s.starts_with(&*cur))
|
||||
.map(CompletionCandidate::new)
|
||||
.collect()
|
||||
}
|
||||
Err(_) => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -402,11 +403,10 @@ mod tests {
|
||||
..Config::default()
|
||||
};
|
||||
|
||||
// Capture stderr for dry_run preview
|
||||
let tokens = vec![OsString::from("echo"), OsString::from("hello")];
|
||||
// Best-effort: ensure function does not error under dry_run
|
||||
let res = wrap_and_run_command(None, &cfg, tokens, None, true).await;
|
||||
assert!(res.is_ok());
|
||||
// Not asserting output text to keep test platform-agnostic
|
||||
let err = wrap_and_run_command(None, &cfg, tokens, None, true)
|
||||
.await
|
||||
.expect_err("expected failed secret resolution in dry_run");
|
||||
assert!(err.to_string().contains("Failed to fetch"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ use crate::cli::secrets_completer;
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Subcommand;
|
||||
use clap::{
|
||||
crate_authors, crate_description, crate_name, crate_version, CommandFactory, Parser, ValueEnum,
|
||||
CommandFactory, Parser, ValueEnum, crate_authors, crate_description, crate_name, crate_version,
|
||||
};
|
||||
use clap_complete::{ArgValueCompleter, CompleteEnv};
|
||||
use crossterm::execute;
|
||||
use crossterm::terminal::{disable_raw_mode, LeaveAlternateScreen};
|
||||
use gman::config::{get_config_file_path, load_config, Config};
|
||||
use crossterm::terminal::{LeaveAlternateScreen, disable_raw_mode};
|
||||
use gman::config::{Config, get_config_file_path, load_config};
|
||||
use std::ffi::OsString;
|
||||
use std::io::{self, IsTerminal, Read, Write};
|
||||
use std::panic::PanicHookInfo;
|
||||
@@ -87,7 +87,7 @@ enum Commands {
|
||||
#[clap(alias = "show")]
|
||||
Get {
|
||||
/// Name of the secret to retrieve
|
||||
#[arg(add = ArgValueCompleter::new(secrets_completer))]
|
||||
#[arg(add = ArgValueCompleter::new(secrets_completer))]
|
||||
name: String,
|
||||
},
|
||||
|
||||
@@ -95,7 +95,7 @@ enum Commands {
|
||||
/// If a provider does not support updating secrets, this command will return an error.
|
||||
Update {
|
||||
/// Name of the secret to update
|
||||
#[arg(add = ArgValueCompleter::new(secrets_completer))]
|
||||
#[arg(add = ArgValueCompleter::new(secrets_completer))]
|
||||
name: String,
|
||||
},
|
||||
|
||||
@@ -103,7 +103,7 @@ enum Commands {
|
||||
#[clap(aliases = &["remove", "rm"])]
|
||||
Delete {
|
||||
/// Name of the secret to delete
|
||||
#[arg(add = ArgValueCompleter::new(secrets_completer))]
|
||||
#[arg(add = ArgValueCompleter::new(secrets_completer))]
|
||||
name: String,
|
||||
},
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
//!
|
||||
//! let rc = RunConfig{
|
||||
//! name: Some("echo".into()),
|
||||
//! provider: None,
|
||||
//! secrets: Some(vec!["api_key".into()]),
|
||||
//! files: None,
|
||||
//! flag: None,
|
||||
|
||||
@@ -18,14 +18,14 @@ use validator::Validate;
|
||||
///
|
||||
/// Example
|
||||
/// ```no_run
|
||||
/// use gman::providers::local::GopassProvider;
|
||||
/// use gman::providers::gopass::GopassProvider;
|
||||
/// use gman::providers::{SecretProvider, SupportedProvider};
|
||||
/// use gman::config::Config;
|
||||
///
|
||||
/// let provider = GopassProvider::default();
|
||||
/// let _ = provider.set_secret("MY_SECRET", "value");
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Validate, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, Clone, Validate, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct GopassProvider {
|
||||
pub store: Option<String>,
|
||||
|
||||
@@ -6,7 +6,7 @@ pub mod aws_secrets_manager;
|
||||
pub mod azure_key_vault;
|
||||
pub mod gcp_secret_manager;
|
||||
mod git_sync;
|
||||
mod gopass;
|
||||
pub mod gopass;
|
||||
pub mod local;
|
||||
|
||||
use crate::providers::gopass::GopassProvider;
|
||||
|
||||
+11
-11
@@ -9,7 +9,7 @@ mod tests {
|
||||
fn test_run_config_valid() {
|
||||
let run_config = RunConfig {
|
||||
name: Some("test".to_string()),
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: Some(vec!["secret1".to_string()]),
|
||||
flag: None,
|
||||
flag_position: None,
|
||||
@@ -24,7 +24,7 @@ mod tests {
|
||||
fn test_run_config_missing_name() {
|
||||
let run_config = RunConfig {
|
||||
name: None,
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: Some(vec!["secret1".to_string()]),
|
||||
flag: None,
|
||||
flag_position: None,
|
||||
@@ -39,7 +39,7 @@ mod tests {
|
||||
fn test_run_config_missing_secrets() {
|
||||
let run_config = RunConfig {
|
||||
name: Some("test".to_string()),
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: None,
|
||||
flag: None,
|
||||
flag_position: None,
|
||||
@@ -54,7 +54,7 @@ mod tests {
|
||||
fn test_run_config_invalid_flag_position() {
|
||||
let run_config = RunConfig {
|
||||
name: Some("test".to_string()),
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: Some(vec!["secret1".to_string()]),
|
||||
flag: Some("--test-flag".to_string()),
|
||||
flag_position: Some(0),
|
||||
@@ -69,7 +69,7 @@ mod tests {
|
||||
fn test_run_config_flags_or_none_all_some() {
|
||||
let run_config = RunConfig {
|
||||
name: Some("test".to_string()),
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: Some(vec!["secret1".to_string()]),
|
||||
flag: Some("--test-flag".to_string()),
|
||||
flag_position: Some(1),
|
||||
@@ -84,7 +84,7 @@ mod tests {
|
||||
fn test_run_config_flags_or_none_all_none() {
|
||||
let run_config = RunConfig {
|
||||
name: Some("test".to_string()),
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: Some(vec!["secret1".to_string()]),
|
||||
flag: None,
|
||||
flag_position: None,
|
||||
@@ -99,7 +99,7 @@ mod tests {
|
||||
fn test_run_config_flags_or_none_partial_some() {
|
||||
let run_config = RunConfig {
|
||||
name: Some("test".to_string()),
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: Some(vec!["secret1".to_string()]),
|
||||
flag: Some("--test-flag".to_string()),
|
||||
flag_position: None,
|
||||
@@ -114,7 +114,7 @@ mod tests {
|
||||
fn test_run_config_flags_or_none_missing_placeholder() {
|
||||
let run_config = RunConfig {
|
||||
name: Some("test".to_string()),
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: Some(vec!["secret1".to_string()]),
|
||||
flag: Some("--test-flag".to_string()),
|
||||
flag_position: Some(1),
|
||||
@@ -129,7 +129,7 @@ mod tests {
|
||||
fn test_run_config_flags_or_files_all_none() {
|
||||
let run_config = RunConfig {
|
||||
name: Some("test".to_string()),
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: Some(vec!["secret1".to_string()]),
|
||||
flag: None,
|
||||
flag_position: None,
|
||||
@@ -144,7 +144,7 @@ mod tests {
|
||||
fn test_run_config_flags_or_files_files_is_some() {
|
||||
let run_config = RunConfig {
|
||||
name: Some("test".to_string()),
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: Some(vec!["secret1".to_string()]),
|
||||
flag: None,
|
||||
flag_position: None,
|
||||
@@ -159,7 +159,7 @@ mod tests {
|
||||
fn test_run_config_flags_or_files_all_some() {
|
||||
let run_config = RunConfig {
|
||||
name: Some("test".to_string()),
|
||||
provider: None,
|
||||
provider: None,
|
||||
secrets: Some(vec!["secret1".to_string()]),
|
||||
flag: Some("--test-flag".to_string()),
|
||||
flag_position: Some(1),
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use gman::config::{Config, ProviderConfig};
|
||||
use gman::providers::{SecretProvider, SupportedProvider};
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
use validator::Validate;
|
||||
|
||||
#[test]
|
||||
fn test_gopass_supported_provider_display_and_validate_from_yaml() {
|
||||
// Build a SupportedProvider via YAML to avoid direct type import
|
||||
let yaml = r#"---
|
||||
type: gopass
|
||||
store: personal
|
||||
"#;
|
||||
|
||||
let sp: SupportedProvider = serde_yaml::from_str(yaml).expect("valid supported provider yaml");
|
||||
// Validate delegates to inner provider (no required fields)
|
||||
assert!(sp.validate().is_ok());
|
||||
// Display formatting for the enum variant
|
||||
assert_eq!(sp.to_string(), "gopass");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_config_with_gopass_deserialize_and_extract() {
|
||||
// Minimal ProviderConfig YAML using the gopass variant
|
||||
let yaml = r#"---
|
||||
name: gopass
|
||||
type: gopass
|
||||
"#;
|
||||
|
||||
let pc: ProviderConfig = serde_yaml::from_str(yaml).expect("valid provider config yaml");
|
||||
// Gopass has no required fields, so validation should pass
|
||||
assert!(pc.validate().is_ok());
|
||||
|
||||
// Extract the provider and inspect its name via the trait
|
||||
let mut pc_owned = pc.clone();
|
||||
let provider: &mut dyn SecretProvider = pc_owned.extract_provider();
|
||||
assert_str_eq!(provider.name(), "GopassProvider");
|
||||
|
||||
// Round-trip through Config with default_provider
|
||||
let cfg_yaml = r#"---
|
||||
default_provider: gopass
|
||||
providers:
|
||||
- name: gopass
|
||||
type: gopass
|
||||
store: personal
|
||||
"#;
|
||||
let cfg: Config = serde_yaml::from_str(cfg_yaml).expect("valid config yaml");
|
||||
assert!(cfg.validate().is_ok());
|
||||
|
||||
let extracted = cfg
|
||||
.extract_provider_config(None)
|
||||
.expect("should find default provider");
|
||||
assert_eq!(extracted.name.as_deref(), Some("gopass"));
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
mod aws_secrets_manager_tests;
|
||||
mod azure_key_vault_tests;
|
||||
mod gcp_secret_manager_tests;
|
||||
mod gopass_tests;
|
||||
mod local_tests;
|
||||
mod provider_tests;
|
||||
|
||||
Reference in New Issue
Block a user