test(cli): export/restore round-trip + error paths
This commit is contained in:
142
crates/relicario-cli/tests/backup.rs
Normal file
142
crates/relicario-cli/tests/backup.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
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}");
|
||||
}
|
||||
@@ -78,6 +78,19 @@ impl TestVault {
|
||||
cmd.output().unwrap()
|
||||
}
|
||||
|
||||
pub fn run_with_backup_pass(&self, args: &[&str], backup_pass: &str) -> std::process::Output {
|
||||
let mut cmd = Command::cargo_bin("relicario").unwrap();
|
||||
cmd.current_dir(self.dir.path())
|
||||
.env("RELICARIO_IMAGE", &self.reference_image)
|
||||
.env("RELICARIO_TEST_PASSPHRASE", &self.passphrase)
|
||||
.env("RELICARIO_TEST_BACKUP_PASSPHRASE", backup_pass)
|
||||
.args(args)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
cmd.output().unwrap()
|
||||
}
|
||||
|
||||
pub fn run_with_input(&self, args: &[&str], extra: &[&str]) -> std::process::Output {
|
||||
let mut cmd = Command::cargo_bin("relicario").unwrap();
|
||||
cmd.current_dir(self.dir.path())
|
||||
|
||||
Reference in New Issue
Block a user