#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing,
reason = "Panicking is acceptable and often desired in tests."
)]
use super::*;
use csl_legacy::csl_json::CSL_TYPES;
use serde_json::json;
#[test]
fn structured_circa_csl_dates_convert_to_approximate_edtf() {
let structured: DateValue = csl_legacy::csl_json::DateVariable {
date_parts: Some(vec![vec![1988]]),
circa: Some(true),
..Default::default()
}
.into();
assert_eq!(structured.value, "1988~");
}
fn minimal_reference(ref_type: &str) -> csl_legacy::csl_json::Reference {
csl_legacy::csl_json::Reference {
id: format!("contract-{ref_type}"),
ref_type: ref_type.to_string(),
title: Some("Contract Test Title".to_string()),
issued: Some(csl_legacy::csl_json::DateVariable {
date_parts: Some(vec![vec![2024]]),
..Default::default()
}),
..Default::default()
}
}
const EXPECTATIONS: &[(&str, &str)] = &[
("article", "preprint"),
("article-journal", "article-journal"),
("article-magazine", "article-magazine"),
("article-newspaper", "article-newspaper"),
("bill", "document"),
("book", "book"),
("broadcast", "broadcast"),
("chapter", "chapter"),
("classic", "classic"),
("collection", "collection"),
("dataset", "dataset"),
("document", "document"),
("entry", "entry"),
("entry-dictionary", "entry-dictionary"),
("entry-encyclopedia", "entry-encyclopedia"),
("event", "event"),
("figure", "figure"),
("graphic", "graphic"),
("hearing", "hearing"),
("interview", "interview"),
("legal_case", "legal-case"),
("legislation", "statute"),
("manuscript", "manuscript"),
("map", "map"),
("motion_picture", "motion-picture"),
("musical_score", "musical-score"),
("pamphlet", "pamphlet"),
("paper-conference", "paper-conference"),
("patent", "patent"),
("performance", "performance"),
("periodical", "periodical"),
("personal_communication", "personal-communication"),
("post", "post"),
("post-weblog", "post-weblog"),
("regulation", "regulation"),
("report", "report"),
("review", "review"),
("review-book", "review-book"),
("software", "software"),
("song", "song"),
("speech", "speech"),
("standard", "standard"),
("thesis", "thesis"),
("treaty", "treaty"),
("webpage", "webpage"),
];
#[test]
fn every_csl_1_0_2_type_has_an_expectation_table_entry() {
for csl_type in CSL_TYPES {
assert!(
EXPECTATIONS.iter().any(|(input, _)| input == csl_type),
"CSL_TYPES entry `{csl_type}` has no entry in the contract test's \
EXPECTATIONS table; every CSL 1.0.2 type must be covered"
);
}
assert_eq!(
EXPECTATIONS.len(),
CSL_TYPES.len(),
"EXPECTATIONS table size has drifted from CSL_TYPES; add or remove \
an entry so the two stay in lockstep"
);
}
#[test]
fn every_csl_1_0_2_type_round_trips_through_ref_type() {
let failures: Vec<String> = EXPECTATIONS
.iter()
.filter_map(|(csl_type, expected)| {
let legacy = minimal_reference(csl_type);
let actual = InputReference::from(legacy).ref_type();
(&actual != expected)
.then(|| format!("{csl_type}: expected `{expected}`, got `{actual}`"))
})
.collect();
assert!(
failures.is_empty(),
"CSL type round-trip mismatches (see docs/specs/CSL_TYPE_CONVERSION_CONTRACT.md):\n{}",
failures.join("\n")
);
}
#[test]
fn map_with_capitalized_export_genre_still_round_trips_as_map() {
let mut legacy = minimal_reference("map");
legacy.genre = Some("Map".to_string());
let converted = InputReference::from(legacy);
assert_eq!(converted.ref_type(), "map");
}
#[test]
fn manuscript_with_recognized_collection_note_override_converts_to_collection() {
let mut legacy = minimal_reference("manuscript");
legacy.note = Some("type: collection".to_string());
legacy.archive = Some("University of Georgia Library".to_string());
legacy.parse_note_field_hacks();
assert_eq!(legacy.ref_type, "collection");
let converted = InputReference::from(legacy);
assert_eq!(converted.ref_type(), "collection");
let crate::reference::ClassExtension::Monograph(monograph) = converted.extension() else {
panic!(
"expected the archival Monograph shape for a `collection` conversion, got `{}`",
converted.ref_type()
);
};
assert_eq!(
monograph.archive.as_deref(),
Some("University of Georgia Library"),
"archival fields must survive the collection conversion"
);
}
#[test]
fn book_volume_title_note_field_survives_as_monograph_metadata() {
let mut legacy = minimal_reference("book");
legacy.note = Some("volume-title: History of Science".to_string());
legacy.parse_note_field_hacks();
let converted = InputReference::from(legacy);
let crate::reference::ClassExtension::Monograph(monograph) = converted.extension() else {
panic!("book must convert to a Monograph");
};
assert_eq!(
monograph.volume_title.as_deref(),
Some("History of Science")
);
assert!(monograph.container.is_none());
}
#[test]
fn map_scale_and_dimensions_survive_as_monograph_metadata() {
let mut legacy = minimal_reference("map");
legacy.note = Some("dimensions: 128cm×84cm".to_string());
legacy.extra.insert("scale".to_string(), json!("1:25000"));
legacy.parse_note_field_hacks();
let converted = InputReference::from(legacy);
let crate::reference::ClassExtension::Monograph(monograph) = converted.extension() else {
panic!("map must convert to a Monograph");
};
assert_eq!(monograph.scale.as_deref(), Some("1:25000"));
assert_eq!(monograph.size.as_deref(), Some("128cm×84cm"));
}
#[test]
fn standalone_article_version_note_field_survives_on_preprint() {
let mut legacy = minimal_reference("article");
legacy.note = Some("version: 2".to_string());
legacy.parse_note_field_hacks();
let converted = InputReference::from(legacy);
assert_eq!(converted.ref_type(), "preprint");
let crate::reference::ClassExtension::Monograph(monograph) = converted.extension() else {
panic!("standalone article must convert to a preprint Monograph");
};
assert_eq!(monograph.version.as_deref(), Some("2"));
}