#![allow(missing_docs, reason = "test")]
#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
reason = "Panicking is acceptable and often desired in test code."
)]
mod common;
use common::announce_behavior;
use citum_engine::Processor;
use citum_io::load_bibliography;
use citum_schema::citation::{Citation, CitationItem};
use citum_schema::reference::{
Contributor, ContributorEntry, ContributorRole, DateValue, InputReference, Monograph,
MonographType, StructuredName, Title,
};
use indexmap::IndexMap;
use rstest::rstest;
use std::path::PathBuf;
fn project_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..")
}
fn render_pinned_gbt_entry(style_name: &str, ref_id: &str) -> String {
let corpus_path = project_root().join("tests/fixtures/test-items-library/gb-t-7714-2025.json");
let corpus = load_bibliography(&corpus_path).expect("pinned GB/T corpus should load");
let reference = corpus
.get(ref_id)
.unwrap_or_else(|| panic!("pinned GB/T corpus should contain {ref_id}"))
.clone();
let bibliography = IndexMap::from([(ref_id.to_string(), reference)]);
let style = citum_schema::embedded::get_embedded_style(style_name)
.unwrap_or_else(|| panic!("{style_name} should be embedded"))
.unwrap_or_else(|_| panic!("{style_name} should parse"))
.into_resolved();
Processor::new(style, bibliography).render_bibliography()
}
fn annotated_book(id: &str, title: &str, year: &str, note: &str) -> InputReference {
InputReference::Monograph(Box::new(Monograph {
id: Some(id.into()),
r#type: MonographType::Book,
title: Some(Title::Single(title.to_string())),
issued: DateValue {
value: year.to_string(),
note: Some(note.to_string()),
},
..Default::default()
}))
}
fn annotated_book_by(
id: &str,
title: &str,
author_family: &str,
year: &str,
note: &str,
) -> InputReference {
InputReference::Monograph(Box::new(Monograph {
id: Some(id.into()),
r#type: MonographType::Book,
title: Some(Title::Single(title.to_string())),
contributors: vec![ContributorEntry {
roles: ContributorRole::Author.into(),
contributor: Contributor::StructuredName(StructuredName {
family: author_family.into(),
..Default::default()
}),
gender: None,
}],
issued: DateValue {
value: year.to_string(),
note: Some(note.to_string()),
},
..Default::default()
}))
}
fn single_item_citation(id: &str) -> Citation {
Citation {
items: vec![CitationItem {
id: id.to_string(),
..Default::default()
}],
..Default::default()
}
}
#[test]
fn given_gb_t_author_date_style_when_rendering_bibliography_then_calendar_note_is_wrapped() {
announce_behavior(
"GB/T 7714 author-date wraps an issued date's opaque note (e.g. a Minguo \
calendar annotation) in the bibliography, per the style's `note-wrap: \
parentheses` bibliography-scoped `dates` option — csl26-epu6.",
);
let style = citum_schema::embedded::get_embedded_style("gb-t-7714-2025-author-date")
.expect("gb-t-7714-2025-author-date should be embedded")
.expect("gb-t-7714-2025-author-date should parse")
.into_resolved();
let bibliography = IndexMap::from([(
"minguo-1947".to_string(),
annotated_book("minguo-1947", "戰後臺灣史", "1947", "民国三十六年"),
)]);
let processor = Processor::new(style, bibliography);
let rendered = processor.render_bibliography();
assert_eq!(rendered, "[1]戰後臺灣史. [M]. 1947(民国三十六年)");
}
#[test]
fn given_gb_t_author_date_style_when_rendering_citation_then_calendar_note_is_absent() {
announce_behavior(
"GB/T 7714 author-date citations carry no `note-wrap` option (it is set only \
under bibliography.options.dates), so an annotated date's note never appears \
in citation output — bibliography-only scoping, csl26-epu6.",
);
let style = citum_schema::embedded::get_embedded_style("gb-t-7714-2025-author-date")
.expect("gb-t-7714-2025-author-date should be embedded")
.expect("gb-t-7714-2025-author-date should parse")
.into_resolved();
let bibliography = IndexMap::from([(
"minguo-1947".to_string(),
annotated_book("minguo-1947", "戰後臺灣史", "1947", "民国三十六年"),
)]);
let processor = Processor::new(style, bibliography);
let citation = processor
.process_citation(&single_item_citation("minguo-1947"))
.expect("annotated-date citation should render");
assert_eq!(citation, "(“戰後臺灣史”,1947)");
}
#[test]
fn given_two_refs_same_author_year_different_note_when_disambiguating_then_still_collide() {
announce_behavior(
"A date's opaque note never enters author-date collision grouping — two \
references by the same author with the same issued year but different \
calendar notes still collide and receive year-suffix disambiguators, exactly \
as if neither carried a note. `year()`/`effective_issued_date` read only \
`value`; `note` is invisible to `build_group_key` — csl26-epu6.",
);
let style = citum_schema::embedded::get_embedded_style("gb-t-7714-2025-author-date")
.expect("gb-t-7714-2025-author-date should be embedded")
.expect("gb-t-7714-2025-author-date should parse")
.into_resolved();
let bibliography = IndexMap::from([
(
"kang-a".to_string(),
annotated_book_by("kang-a", "First Work", "Kang", "1947", "民国三十六年"),
),
(
"kang-b".to_string(),
annotated_book_by("kang-b", "Second Work", "Kang", "1947", "不同的注释"),
),
]);
let processor = Processor::new(style, bibliography);
let rendered = processor.render_bibliography();
assert_eq!(
rendered,
"[1]Kang. First Work[M]. 1947a(民国三十六年)\n\n\
[2]Kang. Second Work[M]. 1947b(不同的注释)"
);
}
#[rstest]
#[case::gb_t_7714_2025_author_date_minguo(
"gb-t-7714-2025-author-date",
"gbt7714.7.5.4.1:1",
"[1][M]. 1947(民国三十六年)"
)]
#[case::gb_t_7714_2025_author_date_kangxi(
"gb-t-7714-2025-author-date",
"gbt7714.7.5.4.1:2",
"[1][M]. 1705(康熙四十四年)"
)]
#[case::gb_t_7714_2025_numeric_minguo(
"gb-t-7714-2025-numeric",
"gbt7714.7.5.4.1:1",
"[1][M]. 1947(民国三十六年)"
)]
#[case::gb_t_7714_2025_numeric_kangxi(
"gb-t-7714-2025-numeric",
"gbt7714.7.5.4.1:2",
"[1][M]. 1705(康熙四十四年)"
)]
#[case::gb_t_7714_2025_note_minguo(
"gb-t-7714-2025-note",
"gbt7714.7.5.4.1:1",
"[1][M]. 1947(民国三十六年)"
)]
#[case::gb_t_7714_2025_note_kangxi(
"gb-t-7714-2025-note",
"gbt7714.7.5.4.1:2",
"[1][M]. 1705(康熙四十四年)"
)]
#[case::gb_t_7714_2025_author_date_qing_dynasty(
"gb-t-7714-2025-author-date",
"gbt7714.8.2.2:2",
"[1]王夫之. 宋论[M]. 刻本. 金陵:湘乡曾国荃,1865(清同治四年)"
)]
#[case::gb_t_7714_2025_author_date_guangxu_era(
"gb-t-7714-2025-author-date",
"gbt7714.8.12.3:1",
"[1]李鸿章. 奏请上海道库洋务外销要款无款可筹仍拨药厘接济事:04-01-35-0399-039[A]. 北京:中国第一历史档案馆,1887(光绪十三年三月十三日)"
)]
#[case::gb_t_7714_2025_author_date_republic_era(
"gb-t-7714-2025-author-date",
"gbt7714.8.12.3:3",
"[1]中国人民解放军武汉市军事管制委员会接管国立武汉大学的文告. [A/OL]. 武汉:武汉大学档案馆,1949(中华民国三十八年八月). https://archive.whu.edu.cn/index/forwardView/20/51"
)]
fn given_pinned_gbt_record_when_rendering_bibliography_then_note_is_wrapped(
#[case] style_name: &str,
#[case] ref_id: &str,
#[case] expected: &str,
) {
let rendered = render_pinned_gbt_entry(style_name, ref_id);
assert_eq!(rendered, expected);
}