weavatrix-rust 2.16.1

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
mod dify_support;

use dify_support::engine;
use weavatrix_rust::{Analyzer, SourceInput};

fn labels_of(engine: &weavatrix_rust::Weavatrix) -> Vec<String> {
    engine
        .state()
        .graph()
        .nodes()
        .iter()
        .map(|node| node.label.clone())
        .collect()
}

#[test]
fn conversation_read_and_write_stay_typed() {
    let (_fixture, engine) = engine();
    let graph = engine.state().graph();
    let remember = graph
        .nodes()
        .iter()
        .find(|node| node.label == "Remember Email")
        .expect("assigner");
    let variable = graph
        .nodes()
        .iter()
        .find(|node| {
            node.label == "conversation:customer_email" && node.id.as_str().starts_with("symbol:")
        })
        .expect("conversation variable");
    let start = graph
        .nodes()
        .iter()
        .find(|node| {
            node.label == "Start"
                && node
                    .span
                    .as_ref()
                    .is_some_and(|span| span.file.contains("conversation-assign"))
        })
        .expect("start");
    let writes = graph
        .edges()
        .iter()
        .filter(|edge| {
            edge.source.as_str() == remember.id.as_str()
                && edge.target.as_str() == variable.id.as_str()
                && edge.kind.as_str() == "writes_variable"
        })
        .count();
    let reads = graph
        .edges()
        .iter()
        .filter(|edge| {
            edge.source.as_str() == remember.id.as_str()
                && edge.target.as_str() == start.id.as_str()
                && edge.kind.as_str() == "reads_variable"
        })
        .count();
    assert_eq!(
        writes, 1,
        "assigner must write the scoped conversation variable"
    );
    assert_eq!(reads, 1, "assigner must read start_node.email");
}

#[test]
fn assigner_v2_items_write_the_same_conversation_variable() {
    let (_fixture, engine) = engine();
    let graph = engine.state().graph();
    let remember = graph
        .nodes()
        .iter()
        .find(|node| node.label == "Remember")
        .expect("v2 assigner");
    let variable = graph
        .nodes()
        .iter()
        .find(|node| {
            node.label == "conversation:message" && node.id.as_str().starts_with("symbol:")
        })
        .expect("v2 conversation variable");
    let answer = graph
        .nodes()
        .iter()
        .find(|node| node.label == "Answer")
        .expect("answer");
    assert!(
        graph.edges().iter().any(|edge| {
            edge.source.as_str() == remember.id.as_str()
                && edge.target.as_str() == variable.id.as_str()
                && edge.kind.as_str() == "writes_variable"
        }),
        "v2 items[] must write conversation.message"
    );
    assert!(
        graph.edges().iter().any(|edge| {
            edge.source.as_str() == answer.id.as_str()
                && edge.target.as_str() == variable.id.as_str()
        }),
        "answer must consume the same variable"
    );
    assert!(
        graph
            .nodes()
            .iter()
            .all(|node| node.label != "marker:start_node.documentary"),
        "description markers stay documentary"
    );
}

#[test]
fn iteration_children_keep_parent_scope() {
    let (_fixture, engine) = engine();
    let labels = labels_of(&engine);
    assert!(
        labels
            .iter()
            .any(|label| label == "parent:each_item"
                || label.starts_with("scope:iteration:each_item")),
        "{labels:?}"
    );
    assert!(
        labels.iter().any(|label| label == "Format Item"),
        "{labels:?}"
    );
    assert!(
        labels.iter().any(|label| label == "binding:item"),
        "{labels:?}"
    );
}

#[test]
fn env_model_is_recorded_and_secrets_are_omitted() {
    let (_fixture, mut engine) = engine();
    let labels = labels_of(&engine);
    assert!(
        labels
            .iter()
            .any(|label| label.contains("model:env:CHAT_MODEL")),
        "{labels:?}"
    );
    assert!(
        labels.iter().any(|label| label == "env:CHAT_MODEL"),
        "{labels:?}"
    );
    assert!(
        labels
            .iter()
            .all(|label| !label.contains("sk-not-a-real-key")),
        "{labels:?}"
    );
    let context = weavatrix_rust::tools::call(
        &mut engine,
        "dify_context",
        blazingly_json::json!({"label": "Env LLM"}),
    )
    .unwrap();
    let text = blazingly_json::to_string(&context).unwrap();
    assert!(!text.contains("sk-not-a-real-key"), "{text}");
    assert!(!text.to_ascii_lowercase().contains("openai_api_key") || text.contains("env:redacted"));
}

#[test]
fn unicode_title_keeps_a_raw_locator() {
    let raw = include_str!("fixtures/dify/unicode-title.yml");
    let snapshot = Analyzer::default()
        .analyze_sources(
            std::env::current_dir().unwrap(),
            "unicode",
            [SourceInput {
                path: "apps/unicode-title.yml".into(),
                bytes: raw.as_bytes().to_vec(),
                content_hash: None,
            }],
        )
        .unwrap();
    let labels = snapshot
        .nodes
        .iter()
        .map(|node| node.label.as_str())
        .collect::<Vec<_>>();
    assert!(
        labels
            .iter()
            .any(|label| label.contains("Привет") || label.contains("Узел")),
        "{labels:?}"
    );
}

#[test]
fn code_declarations_are_inventory_not_execution() {
    let (_fixture, engine) = engine();
    let labels = labels_of(&engine);
    assert!(
        labels.iter().any(|label| label.contains("code:decl:main")),
        "{labels:?}"
    );
    assert!(
        labels.iter().any(|label| label == "output:result"),
        "{labels:?}"
    );
}