mod common; use common::TestVault; const FIXTURE: &str = "tests/fixtures/lastpass-sample.csv"; fn fixture_path() -> std::path::PathBuf { // Manifest dir = crates/relicario-cli; the fixture is relative to it. std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(FIXTURE) } #[test] fn imports_logins_secure_notes_and_warns_on_skipped() { let v = TestVault::init(); let out = v.run(&["import", "lastpass", fixture_path().to_str().unwrap()]); assert!( out.status.success(), "import failed:\nstdout: {}\nstderr: {}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr), ); let stderr = String::from_utf8(out.stderr).unwrap(); // 9 items expected (see fixture comment). assert!(stderr.contains("Imported 9"), "stderr: {stderr}"); assert!(stderr.contains("skipped 2"), "stderr: {stderr}"); // Each warning surfaces. assert!(stderr.contains("invalid base32 TOTP"), "TOTP warning missing"); assert!(stderr.contains("invalid URL"), "URL warning missing"); assert!(stderr.contains("missing `name`"), "name-missing warning missing"); assert!(stderr.contains("missing `password`"), "password-missing warning missing"); } #[test] fn list_after_import_shows_imported_titles() { let v = TestVault::init(); v.run(&["import", "lastpass", fixture_path().to_str().unwrap()]); let out = v.run(&["list"]); let stdout = String::from_utf8(out.stdout).unwrap(); assert!(stdout.contains("GitHub")); assert!(stdout.contains("Gmail")); assert!(stdout.contains("Home Wifi")); assert!(stdout.contains("Visa Card")); assert!(stdout.contains("日本語サイト")); // Skipped rows must NOT appear. assert!(!stdout.contains("No Password"), "row with no password should have been skipped"); } #[test] fn import_creates_a_single_git_commit() { let v = TestVault::init(); // Count commits before. let before = std::process::Command::new("git") .arg("-C").arg(v.path()) .args(["rev-list", "--count", "HEAD"]) .output().unwrap(); let before_n: u32 = String::from_utf8(before.stdout).unwrap().trim().parse().unwrap(); v.run(&["import", "lastpass", fixture_path().to_str().unwrap()]); let after = std::process::Command::new("git") .arg("-C").arg(v.path()) .args(["rev-list", "--count", "HEAD"]) .output().unwrap(); let after_n: u32 = String::from_utf8(after.stdout).unwrap().trim().parse().unwrap(); assert_eq!(after_n, before_n + 1, "expected exactly one new commit"); // Commit message includes the count + "LastPass". let log = std::process::Command::new("git") .arg("-C").arg(v.path()) .args(["log", "-1", "--pretty=%s"]) .output().unwrap(); let subject = String::from_utf8(log.stdout).unwrap(); assert!(subject.contains("9 items")); assert!(subject.contains("LastPass")); } #[test] fn import_with_zero_items_exits_nonzero() { let v = TestVault::init(); // Header-only CSV with one bad row → 0 items. let bad_csv = v.path().join("empty.csv"); std::fs::write( &bad_csv, "url,username,password,totp,extra,name,grouping,fav\n,,,,,,,\n", ).unwrap(); let out = v.run(&["import", "lastpass", bad_csv.to_str().unwrap()]); assert!(!out.status.success(), "expected non-zero exit on zero items"); let stderr = String::from_utf8(out.stderr).unwrap(); assert!(stderr.contains("imported 0 items"), "stderr: {stderr}"); } #[test] fn import_rejects_unrecognized_header() { let v = TestVault::init(); let bad_csv = v.path().join("wrong.csv"); std::fs::write(&bad_csv, "name,url,user,pass\nA,https://x,u,p\n").unwrap(); let out = v.run(&["import", "lastpass", bad_csv.to_str().unwrap()]); assert!(!out.status.success()); let stderr = String::from_utf8(out.stderr).unwrap(); assert!( stderr.contains("LastPass") || stderr.contains("expected"), "stderr: {stderr}", ); } #[test] fn imported_items_keep_unique_ids_across_runs() { // Decision D12: two imports of the same CSV must not collide. let v = TestVault::init(); v.run(&["import", "lastpass", fixture_path().to_str().unwrap()]); v.run(&["import", "lastpass", fixture_path().to_str().unwrap()]); let out = v.run(&["list"]); let stdout = String::from_utf8(out.stdout).unwrap(); // Each title imported twice — count occurrences of "GitHub" must be 2. let github_count = stdout.matches("GitHub").count(); assert_eq!(github_count, 2, "stdout: {stdout}"); }