Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c8bee36e2 |
2 changed files with 106 additions and 3 deletions
38
src/args.rs
38
src/args.rs
|
|
@ -41,9 +41,6 @@ pub struct AddPass {
|
|||
pub struct GetPass {
|
||||
/// The name of the password to get
|
||||
pub name: String,
|
||||
/// Copy the password to the clipboard instead of printing it
|
||||
#[arg(short, long)]
|
||||
pub copy: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
|
@ -51,3 +48,38 @@ pub struct DeletePass {
|
|||
/// The name of the password to delete
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parses_add_command() {
|
||||
let cli = Cli::try_parse_from(["pm", "add", "github"]).expect("parse add");
|
||||
match cli.command {
|
||||
Some(Commands::Add(add)) => assert_eq!(add.name, "github"),
|
||||
_ => panic!("expected add command"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_get_command() {
|
||||
let cli = Cli::try_parse_from(["pm", "get", "email"]).expect("parse get");
|
||||
match cli.command {
|
||||
Some(Commands::Get(get)) => assert_eq!(get.name, "email"),
|
||||
_ => panic!("expected get command"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_removed_copy_flag() {
|
||||
let cli = Cli::try_parse_from(["pm", "get", "email", "--copy"]);
|
||||
assert!(cli.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_list_command() {
|
||||
let cli = Cli::try_parse_from(["pm", "list"]).expect("parse list");
|
||||
assert!(matches!(cli.command, Some(Commands::List)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,3 +118,74 @@ pub fn verify_master_password(password: &str, recorded_hash: &str) -> bool {
|
|||
.verify_password(password.as_bytes(), &parsed_hash)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
fn temp_file_path(prefix: &str) -> String {
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("time went backwards")
|
||||
.as_nanos();
|
||||
std::env::temp_dir()
|
||||
.join(format!("pm_test_{}_{}_{}", prefix, std::process::id(), nanos))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_derivation_is_deterministic_for_same_input() {
|
||||
let mut p1 = String::from("master-password");
|
||||
let mut p2 = String::from("master-password");
|
||||
let salt = "abc123fixedsalt";
|
||||
|
||||
let k1 = derive_key(&mut p1, salt);
|
||||
let k2 = derive_key(&mut p2, salt);
|
||||
|
||||
assert_eq!(k1, k2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_and_verify_master_password_work() {
|
||||
let password = "correct-horse-battery-staple";
|
||||
let hash = hash_master_password(password);
|
||||
|
||||
assert!(verify_master_password(password, &hash));
|
||||
assert!(!verify_master_password("wrong-password", &hash));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_load_and_delete_roundtrip() {
|
||||
let path = temp_file_path("vault");
|
||||
let key = [7u8; 32];
|
||||
let mut data = HashMap::new();
|
||||
data.insert("email".to_string(), "pw1".to_string());
|
||||
data.insert("bank".to_string(), "pw2".to_string());
|
||||
|
||||
save_passwords(&path, &data, &key);
|
||||
let loaded = load_passwords(&path, &key);
|
||||
assert_eq!(loaded, data);
|
||||
|
||||
delete_password(&path, "email", &key);
|
||||
let after_delete = load_passwords(&path, &key);
|
||||
assert!(!after_delete.contains_key("email"));
|
||||
assert_eq!(after_delete.get("bank"), Some(&"pw2".to_string()));
|
||||
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_or_create_salt_reuses_existing_value() {
|
||||
let path = temp_file_path("salt");
|
||||
|
||||
let first = get_or_create_salt(&path);
|
||||
let second = get_or_create_salt(&path);
|
||||
|
||||
assert_eq!(first, second);
|
||||
assert_eq!(first.len(), 32);
|
||||
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue