use std::{fs::File, path::Path, process::Command}; use assert_cmd::prelude::*; use assert_fs::fixture::{FileWriteStr, PathChild}; use filetime::FileTime; use predicates::prelude::*; use serde_json::Value; use sha2::{Digest, Sha256}; use testresult::TestResult; #[test] fn corrupted() -> TestResult { let dir = assert_fs::TempDir::new()?; dir.child("a").write_str("apple")?; dir.child("b").write_str("banana")?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .assert() .success() .stdout(predicate::str::is_empty()) .stderr(predicate::str::is_empty()); let db = dir.child(".brck"); assert!(db.exists()); let db_time = db.metadata()?.modified()?; let db_hash = sha256(db.path())?; let b = dir.child("b"); let b_time = FileTime::from_last_modification_time(&b.metadata()?); b.write_str("cheese")?; filetime::set_file_mtime(b.path(), b_time)?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .assert() .failure() .stdout(predicate::str::contains("corrupted")) .stderr(predicate::str::contains("Error")); assert_eq!(db_time, db.metadata()?.modified()?); assert_eq!(db_hash, sha256(db.path())?); Ok(()) } #[test] fn quiet_by_default() -> TestResult { let dir = assert_fs::TempDir::new()?; dir.child("a").write_str("apple")?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .assert() .success() .stdout(predicate::str::is_empty()) .stderr(predicate::str::is_empty()); Ok(()) } #[test] fn verbose() -> TestResult { let dir = assert_fs::TempDir::new()?; dir.child("a").write_str("apple")?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .arg("--verbose") .assert() .success() .stdout(predicate::str::contains("added")) .stderr(predicate::str::is_empty()); let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .arg("--verbose") .assert() .success() .stdout(predicate::str::is_empty()) .stderr(predicate::str::is_empty()); let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .arg("--verbose") .arg("--verbose") .assert() .success() .stdout(predicate::str::contains("unchanged")) .stderr(predicate::str::is_empty()); Ok(()) } #[test] fn json() -> TestResult { let dir = assert_fs::TempDir::new()?; dir.child("a").write_str("apple")?; let mut cmd = Command::cargo_bin("brck")?; let assert = cmd .current_dir(&dir) .arg("--verbose") .arg("--verbose") .arg("--json") .assert() .success() .stderr(predicate::str::is_empty()); let out = &assert.get_output().stdout; let mut iter = serde_json::Deserializer::from_slice(out).into_iter::(); let added = iter.next().unwrap()?; assert_eq!( added.get("type"), Some(Value::String("added".into())).as_ref() ); assert_eq!( added.get("new").unwrap().get("sha256"), Some(Value::String( "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b".into() )) .as_ref() ); assert!(iter.next().is_none()); Ok(()) } #[test] fn summary() -> TestResult { let dir = assert_fs::TempDir::new()?; dir.child("a").write_str("apple")?; dir.child("b").write_str("banana")?; dir.child("c").write_str("cherry")?; dir.child("d").write_str("date")?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .assert() .success() .stdout(predicate::str::is_empty()) .stderr(predicate::str::is_empty()); std::fs::remove_file(dir.child("b"))?; dir.child("c").write_str("cashew")?; dir.child("d").write_str("date")?; dir.child("e").write_str("elderberry")?; dir.child("f").write_str("fig")?; let mut cmd = Command::cargo_bin("brck")?; // Matching ^ and $ doesn't really work. let pred = predicate::str::is_match("Removed: *1")? .and(predicate::str::is_match("Added: *2")?) // e, f .and(predicate::str::is_match("Unchanged: *1")?) // a .and(predicate::str::is_match("Corrupted: *0")?) // - .and(predicate::str::is_match("Touched: *1")?) // d .and(predicate::str::is_match("Changed: *1")?) // c .and(predicate::str::is_match("Total: *6")?); // a-f cmd.current_dir(&dir) .arg("--summary") .assert() .success() .stdout(predicate::str::is_empty()) .stderr(pred); Ok(()) } #[test] fn dry_run_init() -> TestResult { let dir = assert_fs::TempDir::new()?; dir.child("a").write_str("apple")?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .arg("--dry-run") .assert() .success() .stdout(predicate::str::is_empty()) .stderr(predicate::str::contains("dry run")); assert!(!dir.child(".brck").exists()); Ok(()) } #[test] fn dry_run_update() -> TestResult { let dir = assert_fs::TempDir::new()?; dir.child("a").write_str("apple")?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .assert() .success() .stdout(predicate::str::is_empty()) .stderr(predicate::str::is_empty()); let db = dir.child(".brck"); assert!(db.exists()); let db_time = db.metadata()?.modified()?; let db_hash = sha256(db.path())?; dir.child("b").write_str("banana")?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .arg("--dry-run") .assert() .success() .stdout(predicate::str::is_empty()) .stderr(predicate::str::contains("dry run")); assert_eq!(db_time, db.metadata()?.modified()?); assert_eq!(db_hash, sha256(db.path())?); Ok(()) } #[test] fn deny() -> TestResult { let dir = assert_fs::TempDir::new()?; dir.child("a").write_str("apple")?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .assert() .success() .stdout(predicate::str::is_empty()) .stderr(predicate::str::is_empty()); let db = dir.child(".brck"); assert!(db.exists()); let db_time = db.metadata()?.modified()?; let db_hash = sha256(db.path())?; dir.child("b").write_str("banana")?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .arg("--deny") .arg("added") .assert() .failure() .stdout(predicate::str::contains("added")) .stderr(predicate::str::contains("Error")); assert_eq!(db_time, db.metadata()?.modified()?); assert_eq!(db_hash, sha256(db.path())?); Ok(()) } #[test] fn empty_dir() -> TestResult { let dir = assert_fs::TempDir::new()?; let mut cmd = Command::cargo_bin("brck")?; cmd.current_dir(&dir) .assert() .success() .stdout(predicate::str::is_empty()) .stderr(predicate::str::is_empty()); assert!(dir.child(".brck").exists()); Ok(()) } #[test] fn short_help() -> TestResult { let mut cmd = Command::cargo_bin("brck")?; cmd.arg("-h") .assert() .failure() .stdout(predicate::str::is_empty()) .stderr(predicate::str::contains("--help")); Ok(()) } #[test] fn long_help() -> TestResult { let mut cmd = Command::cargo_bin("brck")?; cmd.arg("--help") .assert() .failure() .stdout(predicate::str::is_empty()) .stderr(predicate::str::contains("Usage")); Ok(()) } fn sha256>(path: P) -> Result, std::io::Error> { let mut file = File::open(path)?; let mut hasher = Sha256::new(); std::io::copy(&mut file, &mut hasher)?; let hash = hasher.finalize(); Ok(hash.as_slice().into()) }