use rstest::rstest;
use super::{ExtensionFilter, ExtensionSpecError, InvalidCharacterKind, parse_extension};
#[test]
fn the_default_set_is_the_three_markdown_extensions() {
let filter = ExtensionFilter::default();
assert_eq!(filter.iter().collect::<Vec<_>>(), ["md", "mdc", "markdown"]);
assert_eq!(filter.to_string(), "md, mdc, markdown");
}
#[rstest]
#[case("md", "md")]
#[case(".md", "md")]
#[case(".MD", "md")]
#[case(" .Markdown ", "markdown")]
#[case("MDC", "mdc")]
fn one_extension_is_trimmed_unfolded_and_dedotted(#[case] value: &str, #[case] expected: &str) {
assert_eq!(
parse_extension(value).expect("a usable extension"),
expected
);
}
#[rstest]
#[case("", ExtensionSpecError::Empty)]
#[case(" ", ExtensionSpecError::Empty)]
#[case(".", ExtensionSpecError::DotOnly { value: ".".to_owned() })]
#[case(" . ", ExtensionSpecError::DotOnly { value: " . ".to_owned() })]
#[case(
"md/markdown",
ExtensionSpecError::InvalidCharacter {
value: "md/markdown".to_owned(),
kind: InvalidCharacterKind::PathSeparator,
}
)]
#[case(
"md\\markdown",
ExtensionSpecError::InvalidCharacter {
value: "md\\markdown".to_owned(),
kind: InvalidCharacterKind::PathSeparator,
}
)]
#[case(
"md\0",
ExtensionSpecError::InvalidCharacter {
value: "md\0".to_owned(),
kind: InvalidCharacterKind::Nul,
}
)]
#[case(
"mdc.",
ExtensionSpecError::InvalidCharacter {
value: "mdc.".to_owned(),
kind: InvalidCharacterKind::Dot,
}
)]
#[case(
"tar.gz",
ExtensionSpecError::InvalidCharacter {
value: "tar.gz".to_owned(),
kind: InvalidCharacterKind::Dot,
}
)]
#[case(
".md.",
ExtensionSpecError::InvalidCharacter {
value: ".md.".to_owned(),
kind: InvalidCharacterKind::Dot,
}
)]
fn an_unusable_extension_is_rejected_with_its_reason(
#[case] value: &str,
#[case] expected: ExtensionSpecError,
) {
assert_eq!(
parse_extension(value).expect_err("an unusable extension"),
expected
);
}
#[test]
fn the_empty_extension_message_is_the_one_the_feature_file_asserts() {
assert_eq!(ExtensionSpecError::Empty.to_string(), "extension is empty");
}
#[test]
fn an_invalid_character_names_the_reason_and_the_value() {
let error = parse_extension("md/markdown").expect_err("a path separator");
assert_eq!(
error.to_string(),
"extension \"md/markdown\" contains a path separator"
);
}
#[rstest]
#[case("docs/guide.md", true)]
#[case("docs/guide.MD", true)]
#[case("docs/guide.Markdown", true)]
#[case("notes.mdc", true)]
#[case("notes.mdx", false)]
#[case("docs/guide.md.bak", false)]
#[case("src/lib.rs", false)]
#[case("Makefile", false)]
#[case(".md", false)]
#[case("docs/.hidden", false)]
fn only_the_last_extension_counts_and_case_is_folded(#[case] path: &str, #[case] expected: bool) {
let filter = ExtensionFilter::default();
assert_eq!(filter.matches(path), expected, "{path}");
}
#[rstest]
#[case("a.mdc.", Some(""))]
#[case("a.b.c", Some("c"))]
#[case("archive.tar.gz", Some("gz"))]
#[case("docs/guide.md", Some("md"))]
#[case(".gitignore", None)]
fn a_paths_extension_is_the_segment_after_its_last_dot(
#[case] path: &str,
#[case] extension: Option<&str>,
) {
assert_eq!(super::extension(path), extension, "{path}");
}
#[test]
fn a_filter_is_built_from_parsed_extensions_and_unfolds_its_own_duplicates() {
let filter: ExtensionFilter = ["md", "MD", "mdc"].into_iter().map(str::to_owned).collect();
assert_eq!(filter.iter().collect::<Vec<_>>(), ["md", "mdc"]);
assert_eq!(filter.to_string(), "md, mdc");
}