use super::*; use vamp::{ plugin::TextPosition, review::{Anchor, Comment, Side}, }; #[test] fn output_filters_resolution_and_extracts_exact_graphemes() { let mut review = Review::new(PathBuf::new()); review.storage_id = Some(7); review.storage_revision = 2; review.comments = [false, true] .into_iter() .enumerate() .map(|(id, resolved)| Comment { id: id as u64 + 1, body: "fix".into(), resolved, anchor: Anchor { revision: "commit".into(), path: "a.rs".into(), side: Side::After, start: TextPosition { row: 0, column: 1 }, end: TextPosition { row: 1, column: 1 }, content: "xe\u{301}👩‍💻\n界z\n".into(), }, }) .collect(); let output = envelope(&review, false, true); assert_eq!(output["comments"].as_array().unwrap().len(), 1); assert_eq!(output["comments"][0]["excerpt"], "e\u{301}👩‍💻\n界"); assert!(output["comments"][0]["anchor"].get("content").is_none()); assert_eq!(output["counts"]["resolved"], 1); assert_eq!( envelope(&review, true, true)["comments"] .as_array() .unwrap() .len(), 2 ); } #[test] fn absent_storage_is_not_created_and_incompatible_storage_is_preserved() { let directory = std::env::temp_dir().join(format!("vamp-review-cli-{}", std::process::id())); std::fs::create_dir(&directory).unwrap(); let absent = directory.join("absent"); assert!(Store::open_read_only(&absent).unwrap().is_none()); assert!(!absent.exists()); let path = directory.join("reviews.sqlite3"); std::fs::write(&path, b"incompatible").unwrap(); assert!(Store::open_read_only(&directory).is_err()); assert_eq!(std::fs::read(&path).unwrap(), b"incompatible"); std::fs::remove_file(&path).unwrap(); let connection = rusqlite::Connection::open(&path).unwrap(); connection .execute_batch("CREATE TABLE incompatible (value TEXT); PRAGMA user_version = 42") .unwrap(); drop(connection); let before = std::fs::read(&path).unwrap(); assert!( Store::open_read_only(&directory) .err() .unwrap() .contains("schema") ); assert_eq!(std::fs::read(&path).unwrap(), before); std::fs::remove_dir_all(directory).unwrap(); } #[test] fn existing_storage_is_read_only_and_unchanged() { let directory = std::env::temp_dir().join(format!("vamp-review-cli-existing-{}", std::process::id())); let mut store = Store::open(&directory).unwrap(); let mut review = Review::new(directory.clone()); review.summary = "stored".into(); store.save(&mut review).unwrap(); drop(store); let path = directory.join("reviews.sqlite3"); #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o640)).unwrap(); } let before = std::fs::read(&path).unwrap(); let mut store = Store::open_read_only(&directory).unwrap().unwrap(); let mut loaded = store.load(&directory, review.storage_id.unwrap()).unwrap(); assert_eq!(loaded.summary, "stored"); loaded.summary = "changed".into(); assert!(store.save(&mut loaded).is_err()); drop(store); assert_eq!(std::fs::read(&path).unwrap(), before); #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; assert_eq!( std::fs::metadata(&path).unwrap().permissions().mode() & 0o777, 0o640 ); let links = directory.join("links"); std::fs::create_dir(&links).unwrap(); std::os::unix::fs::symlink(&path, links.join("reviews.sqlite3")).unwrap(); assert!(Store::open_read_only(&links).is_err()); } std::fs::remove_dir_all(directory).unwrap(); } #[test] fn comment_mutations_require_positive_explicit_ids_and_reject_all() { for command in ["delete", "resolve"] { for ids in [ vec![], vec!["1"], vec!["0", "1"], vec!["-1", "1"], vec!["9223372036854775808", "1"], vec!["all", "1"], vec!["1", "0"], vec!["1", "-1"], vec!["1", "18446744073709551616"], vec!["1", "--json"], ] { let mut args = std::iter::once(command).chain(ids).map(OsString::from); assert!(Request::parse(&mut args).is_err()); } let mut args = [command, "9223372036854775807", "18446744073709551615"] .into_iter() .map(OsString::from); let mut request = Request::parse(&mut args).unwrap(); assert_eq!(request.id, Some(i64::MAX)); let (action, id) = request.comment.as_ref().unwrap(); assert_eq!(*id, u64::MAX); assert!(matches!( (command, action), ("delete", CommentAction::Delete) | ("resolve", CommentAction::Resolve) )); assert!(request.option(OsStr::new("--all"), &mut args).is_err()); request.option(OsStr::new("--json"), &mut args).unwrap(); let mut repo = [OsString::from("checkout")].into_iter(); request.option(OsStr::new("--repo"), &mut repo).unwrap(); assert!(request.json); assert_eq!(request.repo, Some(PathBuf::from("checkout"))); } }