canic-host 0.98.5

Host-side build, install, deployment, and fleet-template library for Canic workspaces
Documentation
use super::*;
use crate::cargo_metadata::{
    CargoMetadataDependencyKind, CargoMetadataNode, CargoMetadataResolve, CargoMetadataTarget,
};
use serde_json::json;

#[test]
fn package_tree_correlation_reconstructs_normal_edges_and_features() {
    let metadata = metadata();
    let selected = &metadata.packages[0];
    let separator = TREE_FIELD_SEPARATOR;
    let tree = format!(
        "0role v1.0.0 (/workspace/role){separator}role{separator}\
         \n1canic v1.0.0 (/workspace/canic){separator}canic{separator}metrics\
         \n1domain v1.0.0 (/workspace/domain){separator}domain{separator}"
    );

    let evidence =
        correlate_package_tree(&metadata, &metadata, selected, &tree).expect("valid graph");

    assert_eq!(evidence.selected_package_id, "role@1");
    assert_eq!(
        evidence.edges["role@1"],
        vec![
            CargoGraphEdge {
                alias: "canic".to_string(),
                package_id: "canic@1".to_string(),
            },
            CargoGraphEdge {
                alias: "domain".to_string(),
                package_id: "domain@1".to_string(),
            },
        ]
    );
    assert_eq!(
        evidence.packages["canic@1"].enabled_features,
        BTreeSet::from(["metrics".to_string()])
    );
}

#[test]
fn package_tree_correlation_rejects_ambiguous_aliases() {
    let mut metadata = metadata();
    metadata.resolve.as_mut().expect("resolve").nodes[0]
        .deps
        .push(normal_edge("framework", "canic@1"));
    let selected = &metadata.packages[0];
    let separator = TREE_FIELD_SEPARATOR;
    let tree = format!(
        "0role v1.0.0 (/workspace/role){separator}role{separator}\
         \n1canic v1.0.0 (/workspace/canic){separator}canic{separator}metrics"
    );

    assert!(
        correlate_package_tree(&metadata, &metadata, selected, &tree)
            .expect_err("ambiguous edge")
            .contains("ambiguous normal dependency aliases")
    );
}

#[test]
fn package_tree_correlation_rejects_depth_gaps() {
    let metadata = metadata();
    let selected = &metadata.packages[0];
    let separator = TREE_FIELD_SEPARATOR;
    let tree = format!(
        "0role v1.0.0 (/workspace/role){separator}role{separator}\
         \n2canic v1.0.0 (/workspace/canic){separator}canic{separator}metrics"
    );

    assert!(
        correlate_package_tree(&metadata, &metadata, selected, &tree)
            .expect_err("depth gap")
            .contains("skips a parent depth")
    );
}

#[test]
fn target_filtered_metadata_excludes_tree_only_packages() {
    let catalog = metadata();
    let mut target_metadata = metadata();
    target_metadata.resolve.as_mut().expect("resolve").nodes[0]
        .deps
        .retain(|dependency| dependency.pkg != "domain@1");
    let selected = &catalog.packages[0];
    let separator = TREE_FIELD_SEPARATOR;
    let tree = format!(
        "0role v1.0.0 (/workspace/role){separator}role{separator}\
         \n1canic v1.0.0 (/workspace/canic){separator}canic{separator}metrics\
         \n1domain v1.0.0 (/workspace/domain){separator}domain{separator}"
    );

    let evidence = correlate_package_tree(&catalog, &target_metadata, selected, &tree)
        .expect("target intersection");

    assert!(!evidence.packages.contains_key("domain@1"));
    assert_eq!(
        evidence.edges["role@1"],
        vec![CargoGraphEdge {
            alias: "canic".to_string(),
            package_id: "canic@1".to_string(),
        }]
    );
}

fn metadata() -> CargoMetadata {
    CargoMetadata {
        packages: vec![
            package("role", "role@1", "/workspace/role/Cargo.toml"),
            package("canic", "canic@1", "/workspace/canic/Cargo.toml"),
            package("domain", "domain@1", "/workspace/domain/Cargo.toml"),
        ],
        resolve: Some(CargoMetadataResolve {
            nodes: vec![
                CargoMetadataNode {
                    id: "role@1".to_string(),
                    deps: vec![
                        normal_edge("canic", "canic@1"),
                        normal_edge("domain", "domain@1"),
                    ],
                },
                CargoMetadataNode {
                    id: "canic@1".to_string(),
                    deps: Vec::new(),
                },
                CargoMetadataNode {
                    id: "domain@1".to_string(),
                    deps: Vec::new(),
                },
            ],
        }),
        workspace_root: PathBuf::from("/workspace"),
    }
}

fn package(name: &str, id: &str, manifest_path: &str) -> CargoMetadataPackage {
    CargoMetadataPackage {
        id: id.to_string(),
        name: name.to_string(),
        version: "1.0.0".to_string(),
        source: None,
        manifest_path: PathBuf::from(manifest_path),
        metadata: Some(json!({})),
        dependencies: Vec::new(),
        features: BTreeMap::new(),
        targets: vec![CargoMetadataTarget {
            name: name.replace('-', "_"),
            kind: vec!["lib".to_string()],
            src_path: PathBuf::from(manifest_path).with_file_name("src/lib.rs"),
        }],
    }
}

fn normal_edge(name: &str, package_id: &str) -> CargoMetadataNodeDependency {
    CargoMetadataNodeDependency {
        name: name.to_string(),
        pkg: package_id.to_string(),
        dep_kinds: vec![CargoMetadataDependencyKind { kind: None }],
    }
}