//! Integration tests for `cph_model::load_bundle` (ADR-0037): an ordered //! arrangement of self-contained lessons, loaded from `bundle.toml`. use std::path::PathBuf; use cph_diag::DiagCode; use cph_model::load_bundle; fn fixture(name: &str) -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures") .join(name) } #[test] fn valid_bundle_loads_lessons_in_order_with_overrides() { let (bundle, diags) = load_bundle(&fixture("bundle-valid")); let bundle = bundle.expect("valid bundle fixture must produce a Bundle"); assert!( diags.is_empty(), "valid bundle fixture must have no diagnostics, got: {diags:?}" ); assert_eq!(bundle.info.title, "测试合集"); assert_eq!( bundle.info.authors, vec!["张老师".to_string(), "李老师".to_string()] ); assert_eq!(bundle.lessons.len(), 2); // lesson-a: no explicit `target` in bundle.toml -> falls back to the // lesson's own first declared target ("student"). assert_eq!(bundle.lessons[0].path, PathBuf::from("lesson-a")); assert_eq!(bundle.lessons[0].target, "student"); assert_eq!(bundle.lessons[0].lesson.info.title, "课时A"); // lesson-b: explicit `target = "teacher"` in bundle.toml, overriding the // lesson's own single declared target (also "teacher" here, but the point // is the bundle entry's `target` wins regardless). assert_eq!(bundle.lessons[1].path, PathBuf::from("lesson-b")); assert_eq!(bundle.lessons[1].target, "teacher"); assert_eq!(bundle.lessons[1].lesson.info.title, "课时B"); // The bundle's own targets are collected exactly like a lesson's. assert_eq!(bundle.target_names(), vec!["merged"]); } #[test] fn missing_lesson_folder_yields_part_path_missing_and_is_skipped() { let (bundle, diags) = load_bundle(&fixture("bundle-missing-lesson")); let bundle = bundle.expect("must still produce a best-effort Bundle"); assert!( bundle.lessons.is_empty(), "the missing lesson is skipped, not placeholder'd" ); let missing: Vec<_> = diags .iter() .filter(|d| d.code == DiagCode::PartPathMissing) .collect(); assert_eq!( missing.len(), 1, "exactly one PartPathMissing expected, got: {diags:?}" ); } #[test] fn malformed_bundle_toml_is_a_hard_failure() { let (bundle, diags) = load_bundle(&fixture("bundle-malformed")); assert!(bundle.is_none(), "malformed bundle.toml is a hard failure"); assert_eq!(diags.len(), 1); assert_eq!(diags[0].code, DiagCode::ManifestMalformed); } #[test] fn missing_bundle_toml_is_a_hard_failure() { let (bundle, diags) = load_bundle(&fixture("does-not-exist-at-all")); assert!(bundle.is_none()); assert_eq!(diags.len(), 1); assert_eq!(diags[0].code, DiagCode::ManifestMalformed); }