forked from EduCraft/curriculum-project-hub
ecc92c9a87
上游 0029/0030 与本地 filelib 侧同号 ADR 相撞,cph 两份改到本地空闲号段, 代码锚点引用一并跟随。
123 lines
4.7 KiB
Rust
123 lines
4.7 KiB
Rust
//! Integration tests for the bundle build path (ADR-0037): compiling a bundle
|
|
//! target's template (`exports/<target>.typ` under a `bundle.toml` root) as
|
|
//! main, injecting the augmented **bundle** manifest, against the real
|
|
//! `render/` package.
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use cph_diag::Severity;
|
|
use cph_typst::{build_augmented_bundle_manifest, Engine};
|
|
|
|
fn fixture_root() -> PathBuf {
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/bundle")
|
|
}
|
|
|
|
fn real_render_dir() -> PathBuf {
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
.join("..")
|
|
.join("..")
|
|
.join("render")
|
|
}
|
|
|
|
fn load_bundle() -> cph_model::Bundle {
|
|
let (bundle, diags) = cph_model::load_bundle(&fixture_root());
|
|
let bundle = bundle.expect("bundle fixture loads into a Bundle");
|
|
let errors: Vec<_> = diags
|
|
.iter()
|
|
.filter(|d| d.severity == Severity::Error)
|
|
.collect();
|
|
assert!(errors.is_empty(), "fixture has loader errors: {errors:?}");
|
|
bundle
|
|
}
|
|
|
|
/// PURE UNIT TEST (no fonts, no render package): the augmented bundle manifest
|
|
/// carries the bundle's own `[info]` and an ordered `[[lessons]]`, each with
|
|
/// that member's own `info`/`target` and a `path`-prefixed outline (ADR-0037).
|
|
#[test]
|
|
fn augmented_bundle_manifest_prefixes_member_paths() {
|
|
let bundle = load_bundle();
|
|
let src = build_augmented_bundle_manifest(&bundle);
|
|
|
|
assert!(
|
|
src.contains("测试合集"),
|
|
"bundle info.title present:\n{src}"
|
|
);
|
|
|
|
let doc: toml::Value = toml::from_str(&src).expect("augmented bundle manifest is valid TOML");
|
|
let lessons = doc
|
|
.get("lessons")
|
|
.and_then(|l| l.as_array())
|
|
.expect("lessons array present");
|
|
assert_eq!(lessons.len(), 2, "two member lessons:\n{src}");
|
|
|
|
// lesson-a: target defaults to its own first declared target ("student"),
|
|
// outline paths are prefixed with "lesson-a/".
|
|
let a_target = lessons[0].get("target").unwrap().as_str().unwrap();
|
|
assert_eq!(a_target, "student");
|
|
let a_outline = lessons[0].get("outline").unwrap().as_array().unwrap();
|
|
// segment + section heading + lemma = 3 outline entries.
|
|
assert_eq!(a_outline.len(), 3);
|
|
let a_seg_path = a_outline[0].get("path").unwrap().as_str().unwrap();
|
|
assert_eq!(a_seg_path, "lesson-a/segments/a");
|
|
let a_section_path = a_outline[1].get("path").unwrap().as_str().unwrap();
|
|
assert_eq!(a_section_path, "lesson-a/小节");
|
|
let a_lemma_path = a_outline[2].get("path").unwrap().as_str().unwrap();
|
|
assert_eq!(a_lemma_path, "lesson-a/小节/lemmas/引理甲");
|
|
|
|
// lesson-b: bundle.toml overrides `target = "teacher"`.
|
|
let b_target = lessons[1].get("target").unwrap().as_str().unwrap();
|
|
assert_eq!(b_target, "teacher");
|
|
let b_outline = lessons[1].get("outline").unwrap().as_array().unwrap();
|
|
assert_eq!(b_outline.len(), 1);
|
|
let b_seg_path = b_outline[0].get("path").unwrap().as_str().unwrap();
|
|
assert_eq!(b_seg_path, "lesson-b/segments/b");
|
|
}
|
|
|
|
/// THROUGH-TEMPLATE compile-check against the REAL render package: compiling
|
|
/// the bundle's `merged` target as main with the injected augmented bundle
|
|
/// manifest is clean.
|
|
#[test]
|
|
fn compile_check_clean_through_bundle_template() {
|
|
let bundle = load_bundle();
|
|
let engine = Engine::with_render_dir(real_render_dir());
|
|
let diags = engine.compile_check_bundle(&bundle, "merged");
|
|
let errors: Vec<_> = diags
|
|
.iter()
|
|
.filter(|d| d.severity == Severity::Error)
|
|
.collect();
|
|
assert!(errors.is_empty(), "unexpected compile errors: {errors:#?}");
|
|
}
|
|
|
|
/// THROUGH-TEMPLATE PDF export, fully offline: a non-trivial combined PDF is
|
|
/// produced from the two member lessons through the real bundle template.
|
|
#[test]
|
|
fn build_bundle_pdf_through_template_offline() {
|
|
let bundle = load_bundle();
|
|
let engine = Engine::with_render_dir(real_render_dir());
|
|
|
|
let pdf = engine
|
|
.build_bundle_pdf(&bundle, "merged")
|
|
.unwrap_or_else(|d| panic!("bundle PDF build failed: {d:#?}"));
|
|
assert!(pdf.starts_with(b"%PDF"), "output is a PDF");
|
|
assert!(
|
|
pdf.len() > 1024,
|
|
"bundle PDF is non-trivial (got {} bytes)",
|
|
pdf.len()
|
|
);
|
|
}
|
|
|
|
/// An undeclared bundle target name is a blocking `SchemaViolation`, exactly
|
|
/// like a lesson's own unknown-target path.
|
|
#[test]
|
|
fn unknown_bundle_target_is_blocking() {
|
|
let bundle = load_bundle();
|
|
let engine = Engine::with_render_dir(real_render_dir());
|
|
let diags = engine.compile_check_bundle(&bundle, "nonexistent");
|
|
assert_eq!(diags.len(), 1, "one blocking diagnostic: {diags:#?}");
|
|
assert_eq!(diags[0].severity, Severity::Error);
|
|
assert!(
|
|
diags[0].message.contains("not declared"),
|
|
"expected an undeclared-target error: {diags:#?}"
|
|
);
|
|
}
|