use camino::Utf8Path;
use mdtablefix::report::LineDelta;
use rstest::rstest;
use test_macros::traced_test;
use super::{
ConflictGuard,
Mode,
analyse,
assess,
test_support::{ALIGNED, RAGGED, align, fixture, identity, read, readable},
};
#[test]
fn assess_reports_no_change_for_its_own_output() {
let (_dir, directory) = fixture("clean.md", ALIGNED);
let assessment = assess(&readable(&directory), Utf8Path::new("clean.md"), &identity)
.expect("assess fixture");
assert!(
!assessment.is_changed(),
"a fixed point must report no change"
);
}
#[test]
fn assess_reports_a_change_when_the_formatter_rewrites() {
let (_dir, directory) = fixture("ragged.md", RAGGED);
let assessment =
assess(&readable(&directory), Utf8Path::new("ragged.md"), &align).expect("assess fixture");
assert!(assessment.is_changed());
}
#[test]
fn assess_keeps_a_byte_order_mark() {
let (_dir, directory) = fixture("bom.md", &format!("\u{FEFF}{ALIGNED}"));
let assessment =
assess(&readable(&directory), Utf8Path::new("bom.md"), &identity).expect("assess fixture");
assert!(
!assessment.is_changed(),
"the byte-order mark must survive the round trip"
);
}
#[test]
fn assess_fails_for_a_missing_file() {
let (_dir, directory) = fixture("clean.md", ALIGNED);
let result = assess(
&readable(&directory),
Utf8Path::new("missing.md"),
&identity,
);
assert!(result.is_err(), "reading a missing file must fail");
}
#[test]
fn check_reports_drift_without_writing() {
let (_dir, directory) = fixture("ragged.md", RAGGED);
let (report, payload) = analyse(
Mode::Check,
&ConflictGuard::unguarded(),
&directory,
Utf8Path::new("ragged.md"),
Utf8Path::new("ragged.md"),
&align,
)
.expect("analyse fixture");
assert!(report.is_changed);
assert_eq!(report.display_path, Utf8Path::new("ragged.md"));
assert_eq!(report.delta, LineDelta::between(RAGGED, ALIGNED));
assert_eq!(payload, "ragged.md +3 -3\n");
assert_eq!(
read(&directory, "ragged.md"),
RAGGED,
"a reporting mode must not write"
);
}
#[test]
fn check_reports_a_clean_file_with_no_payload() {
let (_dir, directory) = fixture("clean.md", ALIGNED);
let (report, payload) = analyse(
Mode::Check,
&ConflictGuard::unguarded(),
&directory,
Utf8Path::new("clean.md"),
Utf8Path::new("clean.md"),
&identity,
)
.expect("analyse fixture");
assert!(!report.is_changed);
assert_eq!(report.delta, LineDelta::default());
assert_eq!(payload, "");
}
#[test]
#[traced_test]
fn check_reports_the_path_the_user_wrote() {
let (_dir, directory) = fixture("nested/inner.md", RAGGED);
let nested = directory
.open_dir(Utf8Path::new("nested"))
.expect("open the nested directory");
let (_report, _payload) = analyse(
Mode::Check,
&ConflictGuard::unguarded(),
&nested,
Utf8Path::new("nested/inner.md"),
Utf8Path::new("inner.md"),
&align,
)
.expect("analyse fixture");
assert!(
logs_contain("nested/inner.md"),
"the line-ending report must name the path the user wrote"
);
}
fn marked(marker: char, text: &str) -> String {
let mut out = String::new();
for line in text.lines() {
out.push(marker);
out.push_str(line);
out.push('\n');
}
out
}
#[test]
fn diff_reports_a_unified_diff_without_writing() {
let (_dir, directory) = fixture("ragged.md", RAGGED);
let (report, payload) = analyse(
Mode::Diff,
&ConflictGuard::unguarded(),
&directory,
Utf8Path::new("ragged.md"),
Utf8Path::new("ragged.md"),
&align,
)
.expect("analyse fixture");
assert!(report.is_changed);
assert_eq!(report.delta, LineDelta::between(RAGGED, ALIGNED));
assert_eq!(
payload,
format!(
"--- ragged.md\n+++ ragged.md\n@@ -1,3 +1,3 @@\n{}{}",
marked('-', RAGGED),
marked('+', ALIGNED),
)
);
assert_eq!(
read(&directory, "ragged.md"),
RAGGED,
"a reporting mode must not write"
);
}
#[test]
fn diff_reports_a_clean_file_with_no_payload() {
let (_dir, directory) = fixture("clean.md", ALIGNED);
let (report, payload) = analyse(
Mode::Diff,
&ConflictGuard::unguarded(),
&directory,
Utf8Path::new("clean.md"),
Utf8Path::new("clean.md"),
&identity,
)
.expect("analyse fixture");
assert!(!report.is_changed);
assert_eq!(payload, "");
}
#[test]
fn print_reports_the_formatted_text_without_writing() {
let (_dir, directory) = fixture("ragged.md", RAGGED);
let (_report, payload) = analyse(
Mode::Print,
&ConflictGuard::unguarded(),
&directory,
Utf8Path::new("ragged.md"),
Utf8Path::new("ragged.md"),
&align,
)
.expect("analyse fixture");
assert_eq!(payload, ALIGNED);
assert_eq!(read(&directory, "ragged.md"), RAGGED);
}
#[rstest]
#[case("docs/guide.md", "docs/guide.md\n")]
#[case("docs/a b.md", "docs/a b.md\n")]
#[case("odd\\name.md", "odd\\\\name.md\n")]
#[case("two\nlines.md", "two\\nlines.md\n")]
#[case("carriage\rreturn.md", "carriage\\rreturn.md\n")]
fn list_files_prints_one_line_per_selected_path(#[case] name: &str, #[case] expected: &str) {
let (_dir, directory) = fixture("clean.md", ALIGNED);
let display_path = Utf8Path::new(name);
let (report, payload) = analyse(
Mode::ListFiles,
&ConflictGuard::unguarded(),
&directory,
display_path,
display_path,
&identity,
)
.expect("list a path");
assert_eq!(payload, expected);
assert_eq!(report.display_path, display_path);
assert!(!report.is_changed, "a listing assesses no content");
}
#[test]
fn assess_declines_a_path_outside_the_capability() {
let (_dir, directory) = fixture("clean.md", ALIGNED);
let result = assess(
&readable(&directory),
Utf8Path::new("../escape.md"),
&identity,
);
assert!(result.is_err(), "a path outside the capability must fail");
}