git-slop 0.16.4

A deterministic repository token-defragmenter for humans and AI agents.
Documentation
mod tests {
    use super::*;
    use tempfile::tempdir;

    fn fixture_pack(root: &Path) {
        fs::create_dir_all(root.join("policies")).expect("policy directory");
        fs::write(
            root.join("git-slop-policy.yaml"),
            r#"schema_version: 1
id: com.example.test-policy
name: Test policy
description: Test-only policy pack.
version: 1.0.0
license: CC0-1.0
min_git_slop_version: 0.16.0
entrypoints: [policies/rule.md]
applicability: [advise]
rules:
  - id: com.example.test-policy.rule
    text: Cite the supplied evidence.
    applicability: [advise]
    severity: error
    consequence: revise
    required_evidence: [citations]
    insufficient_evidence: abstain
"#,
        )
        .expect("manifest");
        fs::write(root.join("policies/rule.md"), "# Rule\n").expect("entrypoint");
    }

    #[test]
    fn path_and_text_normalization_fail_closed() {
        assert!(safe_relative_path("policies/../README.md", "policies/", &[".md"]).is_err());
        assert!(safe_relative_path("/policies/rule.md", "policies/", &[".md"]).is_err());
        assert!(safe_relative_path("policies/rule.sh", "policies/", &[".md"]).is_err());
        assert!(normalized_text(vec![0xff], "fixture").is_err());
        assert!(normalized_text(b"unsafe\0text".to_vec(), "fixture").is_err());
        assert!(normalized_text("e\u{301}".as_bytes().to_vec(), "fixture").is_err());
        assert_eq!(
            normalized_text(b"one\r\ntwo\r".to_vec(), "fixture").expect("normalized text"),
            "one\ntwo\n"
        );
    }

    #[test]
    fn pack_loader_enforces_file_size_count_and_declared_paths() {
        let oversized = tempdir().expect("oversized pack");
        fixture_pack(oversized.path());
        fs::write(
            oversized.path().join("policies/rule.md"),
            vec![b'x'; MAX_FILE_BYTES + 1],
        )
        .expect("oversized entrypoint");
        assert!(
            load_and_validate_pack(oversized.path())
                .expect_err("oversized pack must fail")
                .to_string()
                .contains("exceeds")
        );

        let undeclared = tempdir().expect("undeclared pack");
        fixture_pack(undeclared.path());
        fs::write(undeclared.path().join("unexpected.txt"), "undeclared\n")
            .expect("undeclared file");
        assert!(
            load_and_validate_pack(undeclared.path())
                .expect_err("undeclared file must fail")
                .to_string()
                .contains("undeclared")
        );

        let excessive = tempdir().expect("excessive pack");
        fixture_pack(excessive.path());
        for index in 0..MAX_FILES {
            fs::write(excessive.path().join(format!("extra-{index}.txt")), "x")
                .expect("extra file");
        }
        assert!(
            load_and_validate_pack(excessive.path())
                .expect_err("excessive file count must fail")
                .to_string()
                .contains("file limit")
        );
    }

    #[test]
    fn duplicate_rules_and_non_normalized_optional_text_fail_closed() {
        let duplicate = tempdir().expect("duplicate pack");
        fixture_pack(duplicate.path());
        let manifest = fs::read_to_string(duplicate.path().join("git-slop-policy.yaml"))
            .expect("manifest");
        let duplicated_rule = manifest
            .split("  - id:")
            .nth(1)
            .expect("rule body");
        fs::write(
            duplicate.path().join("git-slop-policy.yaml"),
            format!("{manifest}  - id:{duplicated_rule}"),
        )
        .expect("duplicate manifest");
        assert!(
            load_and_validate_pack(duplicate.path())
                .expect_err("duplicate rule must fail")
                .to_string()
                .contains("duplicate policy rule id")
        );

        let unicode = tempdir().expect("Unicode pack");
        fixture_pack(unicode.path());
        fs::write(unicode.path().join("README.md"), "Cafe\u{301}\n")
            .expect("decomposed README");
        assert!(
            load_and_validate_pack(unicode.path())
                .expect_err("decomposed Unicode must fail")
                .to_string()
                .contains("NFC")
        );
    }
}