weavatrix-rust 2.1.0

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
use super::model::TestEvidence;
use crate::operations::optional_bool;
use blazingly_json::{Value, json};
use std::collections::BTreeSet;

pub(super) fn build_test_evidence(impact: &Value, args: &Value) -> Result<TestEvidence, String> {
    let suggested = suggested_tests(impact);
    let requested = requested_tests(args)?;
    if optional_bool(args, "run_tests")?.unwrap_or(false) {
        return Err(
            "run_tests=true is invalid for the process-free verified_change tool; execute tests externally and supply their evidence"
                .to_owned(),
        );
    }
    let reason = if requested.is_empty() {
        "no test command was requested"
    } else {
        "verified_change is process-free; execute the requested tests externally and attach their results"
    };
    let value = json!({
        "state": "COMPLETE",
        "requested": requested,
        "suggested_files": suggested,
        "execution": {
            "present": false,
            "reason": reason
        }
    });
    Ok(TestEvidence {
        suggested,
        requested,
        value,
    })
}

fn requested_tests(args: &Value) -> Result<Vec<String>, String> {
    args.get("tests")
        .map(|value| {
            value
                .as_array()
                .ok_or_else(|| "tests must be an array of strings".to_owned())?
                .iter()
                .map(|item| {
                    item.as_str()
                        .map(str::to_owned)
                        .ok_or_else(|| "tests must contain only strings".to_owned())
                })
                .collect()
        })
        .transpose()
        .map(Option::unwrap_or_default)
}

fn suggested_tests(impact: &Value) -> Vec<String> {
    impact["impacted_nodes"]
        .as_array()
        .into_iter()
        .flatten()
        .filter_map(|node| {
            node["span"]["file"]
                .as_str()
                .or_else(|| node["source_file"].as_str())
        })
        .filter(|path| {
            let lower = path.to_ascii_lowercase();
            lower.contains("/test")
                || lower.contains("\\test")
                || lower.contains(".test.")
                || lower.contains(".spec.")
                || lower.ends_with("_test.go")
        })
        .map(str::to_owned)
        .collect::<BTreeSet<_>>()
        .into_iter()
        .take(30)
        .collect()
}