main-spex 0.20.0

AI-first development framework that turns specifications into deterministic, verifiable code
#[cfg(test)]
mod tests {
    use super::super::*;
    use std::collections::{HashMap, HashSet};
    use std::path::PathBuf;
    use tempfile::tempdir;

    use crate::support::testing;

    fn write_candidate(package_path: &std::path::Path, rel: &str, content: &str) -> ScopedFile {
        let abs = package_path.join(rel);
        if let Some(parent) = abs.parent() {
            std::fs::create_dir_all(parent).expect("parent dirs");
        }
        std::fs::write(&abs, content).expect("write candidate");
        ScopedFile {
            abs,
            rel: PathBuf::from(rel),
        }
    }

    fn known(ids: &[&str]) -> HashMap<SpecId, HashSet<AcId>> {
        ids.iter()
            .map(|id| (testing::spec_id(id), HashSet::new()))
            .collect()
    }

    fn known_with_acs(id: &str, ac_ids: &[&str]) -> HashMap<SpecId, HashSet<AcId>> {
        HashMap::from([(
            testing::spec_id(id),
            ac_ids.iter().map(|ac| testing::ac_id(ac)).collect(),
        )])
    }

    #[test]
    fn flags_malformed_annotation() {
        let dir = tempdir().expect("temp dir");
        let candidates = vec![write_candidate(
            dir.path(),
            "src/lib.rs",
            "// @spec impl list-pizza-menu\n// @spec bogus list-pizza-menu\n",
        )];
        let ctx = PackageCtx {
            package_path: dir.path(),
            spex_dir: dir.path(),
            candidates: &candidates,
        };
        let findings = validate_annotations(&ctx, &known(&["list-pizza-menu"]));

        assert_eq!(findings.len(), 1);
        assert_eq!(check_of(&findings[0]), FORMAT_CHECK);
        assert_eq!(
            findings[0].location,
            Some(Location::at_line("src/lib.rs", 2))
        );
    }

    #[test]
    fn flags_orphan_reference_and_ignores_known_ids() {
        let dir = tempdir().expect("temp dir");
        let candidates = vec![write_candidate(
            dir.path(),
            "src/lib.rs",
            "// @spec impl list-pizza-menu#in-stock-only, rate-limit\n// @spec test unknown-spec#some-ac\n",
        )];
        let ctx = PackageCtx {
            package_path: dir.path(),
            spex_dir: dir.path(),
            candidates: &candidates,
        };
        let mut known_ids = known(&["list-pizza-menu", "rate-limit"]);
        known_ids.insert(
            testing::spec_id("list-pizza-menu"),
            HashSet::from([testing::ac_id("in-stock-only")]),
        );
        let findings = validate_annotations(&ctx, &known_ids);

        assert_eq!(findings.len(), 1);
        assert_eq!(check_of(&findings[0]), ORPHAN_CHECK);
        assert!(findings[0].message.contains("unknown-spec"));
    }

    #[test]
    fn skips_markdown_and_spex_project_dirs() {
        let dir = tempdir().expect("temp dir");
        let candidates = vec![
            write_candidate(dir.path(), "docs/notes.md", "@spec bogus\n"),
            write_candidate(dir.path(), "specs/example.yaml", "@spec bogus\n"),
        ];
        let ctx = PackageCtx {
            package_path: dir.path(),
            spex_dir: dir.path(),
            candidates: &candidates,
        };
        let findings = validate_annotations(&ctx, &HashMap::new());

        assert!(findings.is_empty());
    }

    #[test]
    fn skips_the_spex_dir_nested_in_the_package_but_not_its_code() {
        let dir = tempdir().expect("temp dir");
        let candidates = vec![
            write_candidate(dir.path(), "spex/specs/example.yaml", "@spec bogus\n"),
            write_candidate(dir.path(), "spex/rules/example/check.sh", "@spec bogus\n"),
            write_candidate(dir.path(), "spex/schemas/spec.schema.json", "@spec bogus\n"),
            write_candidate(dir.path(), "src/lib.rs", "// @spec impl bogus\n"),
        ];
        let spex_dir = dir.path().join("spex");
        let ctx = PackageCtx {
            package_path: dir.path(),
            spex_dir: &spex_dir,
            candidates: &candidates,
        };
        let findings = validate_annotations(&ctx, &HashMap::new());

        assert_eq!(findings.len(), 1);
        assert_eq!(findings[0].location, Some(Location::in_file("src/lib.rs")));
    }

    #[test]
    fn skips_schemas_dir_in_any_package() {
        let dir = tempdir().expect("temp dir");
        let candidates = vec![write_candidate(
            dir.path(),
            "schemas/spec.schema.json",
            "@spec bogus\n",
        )];
        let ctx = PackageCtx {
            package_path: dir.path(),
            spex_dir: dir.path(),
            candidates: &candidates,
        };
        let findings = validate_annotations(&ctx, &HashMap::new());

        assert!(findings.is_empty());
    }

    #[test]
    fn flags_unknown_ac_id_but_allows_known_ones() {
        let dir = tempdir().expect("temp dir");
        let candidates = vec![write_candidate(
            dir.path(),
            "src/lib.rs",
            "// @spec impl delivery-time#estimate-on-delivery\n// @spec impl delivery-time#xxx\n",
        )];
        let ctx = PackageCtx {
            package_path: dir.path(),
            spex_dir: dir.path(),
            candidates: &candidates,
        };
        let findings = validate_annotations(
            &ctx,
            &known_with_acs("delivery-time", &["estimate-on-delivery"]),
        );

        assert_eq!(findings.len(), 1);
        assert_eq!(check_of(&findings[0]), ORPHAN_CHECK);
        assert!(findings[0].message.contains("xxx"));
        assert!(findings[0].message.contains("delivery-time"));
    }

    #[test]
    fn does_not_truncate_an_id_with_an_uppercase_letter_into_a_known_prefix() {
        let dir = tempdir().expect("temp dir");
        let candidates = vec![write_candidate(
            dir.path(),
            "src/lib.rs",
            "// @spec impl delivery-timeX#estimate-on-delivery\n",
        )];
        let ctx = PackageCtx {
            package_path: dir.path(),
            spex_dir: dir.path(),
            candidates: &candidates,
        };
        let findings = validate_annotations(
            &ctx,
            &known_with_acs("delivery-time", &["estimate-on-delivery"]),
        );

        assert_eq!(findings.len(), 1);
        assert_eq!(check_of(&findings[0]), FORMAT_CHECK);
        assert_eq!(
            findings[0].location,
            Some(Location::at_line("src/lib.rs", 1))
        );
    }

    fn validate_content(content: &str, known_ids: &HashMap<SpecId, HashSet<AcId>>) -> Vec<Finding> {
        let dir = tempdir().expect("temp dir");
        let candidates = vec![write_candidate(dir.path(), "src/lib.rs", content)];
        let ctx = PackageCtx {
            package_path: dir.path(),
            spex_dir: dir.path(),
            candidates: &candidates,
        };
        validate_annotations(&ctx, known_ids)
    }

    fn locations(findings: &[Finding]) -> Vec<String> {
        findings
            .iter()
            .map(|finding| {
                finding
                    .location
                    .as_ref()
                    .expect("finding location")
                    .to_string()
            })
            .collect()
    }

    fn check_of(finding: &Finding) -> &str {
        finding.source.baseline_key().expect("check source").1
    }

    #[test]
    fn flags_an_annotation_without_a_known_layer_or_ids() {
        let findings = validate_content(
            "// @spec xx\n// @spec ggg valid_id\n// @spec test\n",
            &known(&["a"]),
        );

        assert!(
            findings
                .iter()
                .all(|finding| check_of(finding) == FORMAT_CHECK)
        );
        assert_eq!(
            locations(&findings),
            vec!["src/lib.rs:1", "src/lib.rs:2", "src/lib.rs:3"]
        );
    }

    #[test]
    fn flags_an_id_that_continues_past_its_valid_part_without_an_orphan_finding() {
        let findings = validate_content(
            "// @spec impl a_b\n\
             // @spec impl a, B\n\
             // @spec impl a#b#c\n\
             // @spec impl aB\n\
             // @spec impl a--b\n\
             // @spec impl a\u{430}\n",
            &known(&["a"]),
        );

        assert!(
            findings
                .iter()
                .all(|finding| check_of(finding) == FORMAT_CHECK)
        );
        assert_eq!(
            locations(&findings),
            vec![
                "src/lib.rs:1",
                "src/lib.rs:2",
                "src/lib.rs:3",
                "src/lib.rs:4",
                "src/lib.rs:5",
                "src/lib.rs:6",
            ]
        );
    }

    #[test]
    fn accepts_annotations_followed_by_a_comment_or_string_closer() {
        let findings = validate_content(
            "/* @spec impl a */\n\
             x-spec: \"@spec contract a\"\n\
             <!-- @spec impl a -->\n\
             // @spec impl a.\n\
             // @spec test a, b\n",
            &known(&["a", "b"]),
        );

        assert!(findings.is_empty());
    }
}