mod common; use common::TestVault; #[test] fn settings_roundtrip_trash_retention() { let v = TestVault::init(); let out = v.run(&["settings", "show"]); assert!(String::from_utf8(out.stdout).unwrap().contains("trash_retention")); let out = v.run(&["settings", "trash-retention", "--days", "60"]); assert!(out.status.success(), "set failed: {:?}", out); let out = v.run(&["settings", "show"]); let stdout = String::from_utf8(out.stdout).unwrap(); assert!(stdout.contains("60"), "expected 60: {stdout}"); } #[test] fn settings_rejects_conflicting_retention_flags() { let v = TestVault::init(); let out = v.run(&["settings", "trash-retention", "--days", "30", "--forever"]); assert!(!out.status.success()); } #[test] fn generate_uses_vault_default_length() { let v = TestVault::init(); // Default vault settings: GeneratorRequest::Random { length: 20, ... }. let out = v.run(&["generate"]); assert!(out.status.success(), "{:?}", String::from_utf8_lossy(&out.stderr)); let pw = String::from_utf8(out.stdout).unwrap(); assert_eq!( pw.trim().chars().count(), 20, "expected 20 chars at default, got {pw:?}" ); // Update the vault default length to 32. let out = v.run(&["settings", "generator-defaults", "--length", "32"]); assert!( out.status.success(), "set generator-defaults failed: {}", String::from_utf8_lossy(&out.stderr) ); // `generate` (no flags) should now produce 32 chars. let out = v.run(&["generate"]); assert!(out.status.success(), "{:?}", String::from_utf8_lossy(&out.stderr)); let pw = String::from_utf8(out.stdout).unwrap(); assert_eq!( pw.trim().chars().count(), 32, "expected 32 chars after update, got {pw:?}" ); // Explicit flag overrides the vault default. let out = v.run(&["generate", "--length", "8"]); assert!(out.status.success()); let pw = String::from_utf8(out.stdout).unwrap(); assert_eq!( pw.trim().chars().count(), 8, "explicit flag should override vault default, got {pw:?}" ); } #[test] fn status_reports_item_attachment_and_device_counts() { let v = TestVault::init(); v.run(&["add", "login", "--title", "active", "--username", "u", "--password", "p"]); v.run(&["add", "login", "--title", "to-trash", "--username", "u", "--password", "p"]); v.run(&["rm", "to-trash"]); let payload = v.path().join("payload.txt"); std::fs::write(&payload, b"hello-world").unwrap(); v.run(&["attach", "active", payload.to_str().unwrap()]); let out = v.run(&["status"]); assert!( out.status.success(), "status failed:\nstdout: {}\nstderr: {}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr), ); let stdout = String::from_utf8(out.stdout).unwrap(); let lower = stdout.to_lowercase(); // 1 active + 1 trashed = 2 items total. assert!(lower.contains("items"), "missing items section: {stdout}"); assert!(stdout.contains('2') || stdout.contains("2 ") || lower.contains("active: 1") || lower.contains("1 active"), "expected item counts in output: {stdout}"); assert!(lower.contains("trash"), "missing trash count: {stdout}"); // 1 attachment, 11 bytes. assert!(lower.contains("attachment"), "missing attachment section: {stdout}"); assert!(stdout.contains("11"), "expected 11-byte size in output: {stdout}"); // 0 devices in default test vault (init does not register one). assert!(lower.contains("device"), "missing devices section: {stdout}"); // Last-commit line. assert!( lower.contains("last commit") || lower.contains("commit"), "missing last-commit info: {stdout}", ); } #[test] fn status_shows_last_backup_line() { let v = TestVault::init(); let out = v.run(&["status"]); assert!(out.status.success()); let stdout = String::from_utf8(out.stdout).unwrap(); assert!(stdout.contains("Last export:"), "missing last export line: {stdout}"); assert!(stdout.contains("never"), "fresh vault should report 'never': {stdout}"); } #[test] fn status_shows_recent_backup_after_export() { let v = TestVault::init(); let backup_path = v.path().join("v.relbak"); v.run_with_backup_pass( &["backup", "export", backup_path.to_str().unwrap()], "test-backup-pass-2026", ); let out = v.run(&["status"]); let stdout = String::from_utf8(out.stdout).unwrap(); assert!(stdout.contains("Last export:"), "{stdout}"); assert!(!stdout.contains("never"), "should NOT say 'never' after export: {stdout}"); } #[test] fn generate_works_outside_vault() { use assert_cmd::cargo::CommandCargoExt; use std::process::{Command, Stdio}; use tempfile::TempDir; let tmp = TempDir::new().unwrap(); let out = Command::cargo_bin("relicario") .unwrap() .current_dir(tmp.path()) .args(["generate", "--length", "12"]) .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() .unwrap(); assert!( out.status.success(), "no-vault generate failed: {}", String::from_utf8_lossy(&out.stderr) ); let pw = String::from_utf8(out.stdout).unwrap(); assert_eq!(pw.trim().chars().count(), 12); }