mod common; use common::TestVault; use std::process::Command; use assert_cmd::cargo::CommandCargoExt; const BACKUP_PASS: &str = "strong-backup-pass-test-2026"; #[test] fn export_then_restore_round_trip() { let v = TestVault::init(); v.run(&["add", "login", "--title", "GitHub", "--username", "alice", "--password", "p"]); v.run(&["add", "login", "--title", "Email", "--username", "bob", "--password", "q"]); let backup_path = v.path().join("vault.relbak"); let out = v.run_with_backup_pass( &["backup", "export", backup_path.to_str().unwrap()], BACKUP_PASS, ); assert!(out.status.success(), "export failed: {:?}", String::from_utf8_lossy(&out.stderr)); assert!(backup_path.exists()); assert!(v.path().join(".relicario/last_backup").exists()); // Restore into a fresh dir. let restore_dir = tempfile::TempDir::new().unwrap(); let out = Command::cargo_bin("relicario") .unwrap() .current_dir(restore_dir.path()) .env("RELICARIO_TEST_BACKUP_PASSPHRASE", BACKUP_PASS) .args(["backup", "restore", backup_path.to_str().unwrap(), "."]) .output() .unwrap(); assert!(out.status.success(), "restore failed: {:?}", String::from_utf8_lossy(&out.stderr)); // Vault should be unlockable in the restore dir using the same passphrase + image. // Since the original vault didn't include the image, we copy it in manually // (the standard restore-without-image flow expects the user to keep their // reference image separately). std::fs::copy(&v.reference_image, restore_dir.path().join("reference.jpg")).unwrap(); let out = Command::cargo_bin("relicario") .unwrap() .current_dir(restore_dir.path()) .env("RELICARIO_TEST_PASSPHRASE", &v.passphrase) .env("RELICARIO_IMAGE", restore_dir.path().join("reference.jpg")) .args(["list"]) .output() .unwrap(); let stdout = String::from_utf8(out.stdout).unwrap(); assert!(stdout.contains("GitHub")); assert!(stdout.contains("Email")); } #[test] fn restore_refuses_non_empty_target() { let v = TestVault::init(); let backup_path = v.path().join("vault.relbak"); v.run_with_backup_pass(&["backup", "export", backup_path.to_str().unwrap()], BACKUP_PASS); let out = Command::cargo_bin("relicario") .unwrap() .current_dir(v.path()) // already has a .relicario/ .env("RELICARIO_TEST_BACKUP_PASSPHRASE", BACKUP_PASS) .args(["backup", "restore", backup_path.to_str().unwrap(), "."]) .output() .unwrap(); assert!(!out.status.success()); let err = String::from_utf8(out.stderr).unwrap(); assert!(err.contains("already contains a Relicario vault"), "stderr: {err}"); } #[test] fn export_with_include_image_round_trips_the_image() { let v = TestVault::init(); let backup_path = v.path().join("vault.relbak"); v.run_with_backup_pass( &["backup", "export", backup_path.to_str().unwrap(), "--include-image"], BACKUP_PASS, ); let restore_dir = tempfile::TempDir::new().unwrap(); let out = Command::cargo_bin("relicario") .unwrap() .current_dir(restore_dir.path()) .env("RELICARIO_TEST_BACKUP_PASSPHRASE", BACKUP_PASS) .args(["backup", "restore", backup_path.to_str().unwrap(), "."]) .output() .unwrap(); assert!(out.status.success(), "{:?}", String::from_utf8_lossy(&out.stderr)); assert!(restore_dir.path().join("reference.jpg").exists(), "image should be restored when --include-image was used"); } #[test] fn export_with_no_history_skips_git_dir() { let v = TestVault::init(); let backup_path = v.path().join("vault.relbak"); v.run_with_backup_pass( &["backup", "export", backup_path.to_str().unwrap(), "--no-history"], BACKUP_PASS, ); let restore_dir = tempfile::TempDir::new().unwrap(); let out = Command::cargo_bin("relicario") .unwrap() .current_dir(restore_dir.path()) .env("RELICARIO_TEST_BACKUP_PASSPHRASE", BACKUP_PASS) .args(["backup", "restore", backup_path.to_str().unwrap(), "."]) .output() .unwrap(); assert!(out.status.success(), "{:?}", String::from_utf8_lossy(&out.stderr)); // .git/ should exist but contain only the "restore from backup ..." commit. assert!(restore_dir.path().join(".git").is_dir()); let out = std::process::Command::new("git") .current_dir(restore_dir.path()) .args(["log", "--oneline"]) .output() .unwrap(); let log = String::from_utf8(out.stdout).unwrap(); assert_eq!(log.lines().count(), 1, "expected one commit, got: {log}"); assert!(log.contains("restore from backup")); } #[test] fn wrong_backup_passphrase_fails() { let v = TestVault::init(); let backup_path = v.path().join("vault.relbak"); v.run_with_backup_pass(&["backup", "export", backup_path.to_str().unwrap()], BACKUP_PASS); let restore_dir = tempfile::TempDir::new().unwrap(); let out = Command::cargo_bin("relicario") .unwrap() .current_dir(restore_dir.path()) .env("RELICARIO_TEST_BACKUP_PASSPHRASE", "definitely-wrong") .args(["backup", "restore", backup_path.to_str().unwrap(), "."]) .output() .unwrap(); assert!(!out.status.success()); let err = String::from_utf8(out.stderr).unwrap(); assert!(err.contains("wrong backup passphrase"), "stderr: {err}"); }