forked from bai/curriculum-project-hub
feat(checker): align cph-typst + cph-check to export-as-build model (WU-D)
Integrate the WU-B (structured targets) and WU-C (numbly, config) changes. cph-typst: - World resolves any @preview/* package from the in-repo vendored dir (<render_dir>/vendor/typst-packages/preview/<name>/<version>/), fully offline — no typst-kit download. Proven: build_pdf_with_real_render compiles the real render package (which imports @preview/numbly:0.1.0) through the embedded engine, PDF out. - Driver threads each target's presentation config: looks up the TargetConfig by name, emits `config: (numbering: (heading: (...)))` (escaped) when an override is present, else `config: (:)` (render default). - target_precheck guards artifact/target: FileTree → blocking diagnostic (MVP is single-file, deferred per ADR-0009); --target not declared → blocking diagnostic; no-targets lesson still defaults through. cph-check: - Migrated to Lesson.targets: Vec<TargetConfig> (target_names() / target.name at the 6 sites). Pipeline unchanged (already matches ADR-0010 phases). Documented that !has_errors() implements Spec.Courseware.Legal (no error-level diagnostic). Workspace: fmt + clippy -D warnings + all tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -78,6 +78,53 @@ fn optional_proof_included_when_present() {
|
||||
assert!(src.contains("_p1_proof = include"));
|
||||
}
|
||||
|
||||
/// The mini fixture's targets carry no `numbering.heading` override, so the
|
||||
/// driver emits an empty `config: (:)` (render package applies its default).
|
||||
#[test]
|
||||
fn driver_emits_empty_config_when_no_override() {
|
||||
let lesson = load_mini();
|
||||
let src = generate_driver(&lesson, "student");
|
||||
assert!(
|
||||
src.contains("config: (:)"),
|
||||
"expected empty config for a target with no numbering override:\n{src}"
|
||||
);
|
||||
}
|
||||
|
||||
/// CONFIG THREADING: a target declaring a `numbering.heading` override produces
|
||||
/// a driver that passes `config: (numbering: (heading: (...)))` with the
|
||||
/// patterns as escaped typst string literals, in order.
|
||||
#[test]
|
||||
fn driver_threads_numbering_heading_config() {
|
||||
use cph_model::{ArtifactKind, Info, Lesson, NumberingConfig, Project, TargetConfig};
|
||||
use std::path::PathBuf;
|
||||
|
||||
let lesson = Lesson {
|
||||
project: Project {
|
||||
id: "x".into(),
|
||||
name: "x".into(),
|
||||
},
|
||||
info: Info {
|
||||
title: "t".into(),
|
||||
author: None,
|
||||
},
|
||||
parts: vec![],
|
||||
targets: vec![TargetConfig {
|
||||
name: "student".into(),
|
||||
artifact: ArtifactKind::SingleFile,
|
||||
numbering: Some(NumberingConfig {
|
||||
heading: Some(vec!["{1:一}、".into(), "{1:1}.{2:1}".into()]),
|
||||
}),
|
||||
}],
|
||||
root: PathBuf::from("/tmp/does-not-matter"),
|
||||
};
|
||||
|
||||
let src = generate_driver(&lesson, "student");
|
||||
assert!(
|
||||
src.contains(r#"config: (numbering: (heading: ("{1:一}、", "{1:1}.{2:1}",)))"#),
|
||||
"expected threaded numbering config in driver:\n{src}"
|
||||
);
|
||||
}
|
||||
|
||||
/// FONT-DEPENDENT: compile the mini lesson through the render-stub. Should
|
||||
/// produce zero Error-severity diagnostics. If this fails purely because the
|
||||
/// test host lacks fonts, the failure will be a typst font error — see the
|
||||
@@ -166,6 +213,13 @@ fn driver_internal_span_is_repointed() {
|
||||
/// targets. This proves the whole World → driver → render-package → PDF path,
|
||||
/// not just the stub. Requires system CJK fonts (present on dev + CI via
|
||||
/// fonts-noto-cjk).
|
||||
///
|
||||
/// **This is also the offline-`@preview/numbly` proof (Task 1).** The real
|
||||
/// render package's `src/style.typ` does `#import "@preview/numbly:0.1.0"`, so a
|
||||
/// clean PDF here means the embedded `World` resolved numbly from the vendored
|
||||
/// dir (`render/vendor/typst-packages/preview/numbly/0.1.0/`) with no network /
|
||||
/// package download — had it failed, `build_pdf` would have returned a
|
||||
/// package-not-found error and this test would panic.
|
||||
#[test]
|
||||
fn build_pdf_with_real_render() {
|
||||
let lesson = load_mini();
|
||||
@@ -175,6 +229,14 @@ fn build_pdf_with_real_render() {
|
||||
"real render package missing at {}",
|
||||
render_dir.display()
|
||||
);
|
||||
// Sanity: the numbly the render package imports is actually vendored here.
|
||||
assert!(
|
||||
render_dir
|
||||
.join("vendor/typst-packages/preview/numbly/0.1.0/lib.typ")
|
||||
.is_file(),
|
||||
"vendored numbly missing under {}",
|
||||
render_dir.display()
|
||||
);
|
||||
let engine = Engine::with_render_dir(render_dir);
|
||||
|
||||
for target in ["student", "teacher"] {
|
||||
@@ -183,6 +245,10 @@ fn build_pdf_with_real_render() {
|
||||
.unwrap_or_else(|d| panic!("{target} PDF build failed: {d:#?}"));
|
||||
assert!(pdf.starts_with(b"%PDF"));
|
||||
assert!(pdf.len() > 1024, "{target} PDF too small");
|
||||
eprintln!(
|
||||
"build_pdf_with_real_render: {target} PDF = {} bytes (numbly resolved offline)",
|
||||
pdf.len()
|
||||
);
|
||||
let out = std::env::temp_dir().join(format!("cph-typst-mini-real-{target}.pdf"));
|
||||
std::fs::write(&out, &pdf).expect("write pdf");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user