main-spex 0.23.0

AI-first development framework that turns specifications into deterministic, verifiable code
#[cfg(test)]
mod tests {
    use super::super::*;
    use crate::support::testing;

    fn spec(coverage_ignore: Vec<CoverageIgnore>) -> Spec {
        Spec {
            status: Status::Active,
            coverage_ignore,
            ..testing::spec("a")
        }
    }

    fn ignore(layer: CoverageLayer, ac: Option<&str>) -> CoverageIgnore {
        CoverageIgnore {
            layer,
            ac: ac.map(testing::ac_id),
        }
    }

    #[test]
    fn package_selected_matches_any_requested_package() {
        let api_and_web = Spec {
            packages: vec!["api".to_string(), "web".to_string()],
            ..testing::spec("a")
        };
        let web = Spec {
            packages: vec!["web".to_string()],
            ..testing::spec("b")
        };
        let without_packages = testing::spec("c");

        assert!(api_and_web.package_selected(&["api"]));
        assert!(!web.package_selected(&["api"]));
        assert!(without_packages.package_selected(&[]));
    }

    #[test]
    fn layer_ignored_true_for_whole_layer_entry() {
        let spec = spec(vec![ignore(CoverageLayer::Contract, None)]);
        assert!(spec.layer_ignored(CoverageLayer::Contract));
        assert!(!spec.layer_ignored(CoverageLayer::Impl));
    }

    #[test]
    fn ac_ignored_true_when_layer_is_ignored_for_whole_spec() {
        let spec = spec(vec![ignore(CoverageLayer::Test, None)]);
        assert!(spec.ac_ignored(&testing::ac_id("any-ac"), CoverageLayer::Test));
    }

    #[test]
    fn ac_ignored_true_when_ac_specifically_ignored() {
        let spec = spec(vec![ignore(
            CoverageLayer::Impl,
            Some("no-null-typed-string"),
        )]);
        assert!(spec.ac_ignored(&testing::ac_id("no-null-typed-string"), CoverageLayer::Impl));
        assert!(!spec.ac_ignored(&testing::ac_id("other-ac"), CoverageLayer::Impl));
        assert!(!spec.ac_ignored(&testing::ac_id("no-null-typed-string"), CoverageLayer::Test));
    }

    #[test]
    fn ac_ignored_false_with_no_coverage_ignore() {
        let spec = spec(Vec::new());
        assert!(!spec.ac_ignored(&testing::ac_id("any-ac"), CoverageLayer::Impl));
    }

    #[test]
    fn ac_contract_required_respects_whole_layer_ignore() {
        let mut spec = spec(vec![ignore(CoverageLayer::Contract, None)]);
        spec.contract = ContractLevel::ForEachAc;
        assert!(!spec.ac_contract_required(&testing::ac_id("any-ac")));
    }

    #[test]
    fn ac_contract_required_only_under_for_each_ac() {
        let mut spec = spec(Vec::new());
        spec.contract = ContractLevel::Single;
        assert!(!spec.ac_contract_required(&testing::ac_id("any-ac")));
        spec.contract = ContractLevel::ForEachAc;
        assert!(spec.ac_contract_required(&testing::ac_id("any-ac")));
    }

    #[test]
    fn ac_contract_required_respects_per_ac_ignore() {
        let mut spec = spec(vec![ignore(CoverageLayer::Contract, Some("waived-ac"))]);
        spec.contract = ContractLevel::ForEachAc;
        assert!(!spec.ac_contract_required(&testing::ac_id("waived-ac")));
        assert!(spec.ac_contract_required(&testing::ac_id("other-ac")));
    }

    #[test]
    fn from_config_keeps_the_path_and_numbers_criteria_by_source_line() {
        let raw = "id: a\ncontract: single\nacceptance_criteria:\n  - id: one\n    must: x\n  - id: two\n    must: y\n";
        let config = serde_yaml_ng::from_str(raw).expect("parse spec config");

        let spec = Spec::from_config(config, PathBuf::from("/spex/specs/a.yaml"), raw);

        assert_eq!(spec.path, PathBuf::from("/spex/specs/a.yaml"));
        let lines: Vec<usize> = spec.acceptance_criteria.iter().map(|ac| ac.line).collect();
        assert_eq!(lines, vec![4, 6]);
    }

    #[test]
    fn from_config_treats_absent_is_done_as_done() {
        let raw = "id: a\ncontract: single\nacceptance_criteria:\n  - id: one\n    must: x\n  - id: two\n    must: y\n    is_done: false\n";
        let config = serde_yaml_ng::from_str(raw).expect("parse spec config");

        let spec = Spec::from_config(config, PathBuf::from("a.yaml"), raw);

        let done: Vec<bool> = spec
            .acceptance_criteria
            .iter()
            .map(|ac| ac.is_done)
            .collect();
        assert_eq!(done, vec![true, false]);
    }
}