Skip to content

πŸ§ͺ Test gap analysis β€” 20+ gaps found in compile helpers, onees.rs, and mcp_metadata.rsΒ #22

@github-actions

Description

@github-actions

Test Gap Analysis

Test suite snapshot: 240 unit tests, 23 integration tests (9 compiler + 5 mcp_firewall + 9 proxy), 4 test fixtures

Previous gaps from the last run (types.rs, execute.rs, common.rs trigger functions, pipeline-trigger fixture) have been resolved in commit d767022. This cycle audited the remaining helpers.


Priority Gaps

Module Function/Path Why It Matters Suggested Test
compile/standalone.rs generate_allowed_domains Security-critical β€” determines which hosts agents can reach over the network; wrong output lets agents exfiltrate data Test that default hosts appear, user-specified network.allow entries are appended, and MCP-specific hosts are included
compile/onees.rs generate_mcp_configuration Security-relevant β€” controls which MCP service connections are wired up in 1ES pipelines; custom MCPs should be silently excluded Test that Enabled(true) MCPs generate service connection YAML, custom MCPs with command: are filtered out, and Enabled(false) MCPs are skipped
compile/onees.rs All 5 helpers + no 1ES integration test The 1ES compile path (target: 1es) has no integration test verifying zero unreplaced \{\{ marker }} tokens in output Add a test mirroring test_compiled_output_no_unreplaced_markers but compiling fixtures/1es-test-agent.md
mcp_metadata.rs has_tool, get_tools, builtin_mcp_names, tool_names has_tool is used by the firewall for allow-list enforcement; a wrong result could expose or block tools unexpectedly Test known tool names return true, unknown names return false, non-existent MCPs return false
compile/common.rs generate_source_path, generate_pipeline_path These paths feed into Stage 2 execution and integrity checks; wrong paths cause silent runtime failures Test root vs repo workspace produces correct $(Build.SourcesDirectory) prefix
compile/standalone.rs generate_acquire_ado_token, generate_copilot_ado_env These inject secrets into the pipeline β€” wrong output leaks tokens or breaks auth Test None service connection β†’ empty string; Some("my-conn") β†’ contains correct AzureCLI@2 step and env vars

Suggested Test Cases

1. generate_allowed_domains β€” network allow-list includes user hosts and MCP hosts

#[test]
fn test_generate_allowed_domains_includes_user_network_allow() {
    let mut fm = minimal_front_matter();
    fm.network = Some(NetworkConfig {
        allow: vec!["*.mycompany.com".to_string()],
        blocked: vec![],
    });
    let domains = generate_allowed_domains(&fm);
    assert!(domains.contains("*.mycompany.com"), "user-specified host should appear");
    assert!(domains.contains("github.com"), "core hosts should always appear");
}

#[test]
fn test_generate_allowed_domains_includes_mcp_hosts() {
    let mut fm = minimal_front_matter();
    fm.mcp_servers.insert("kusto".to_string(), McpConfig::Enabled(true));
    let domains = generate_allowed_domains(&fm);
    // kusto MCP requires its own endpoints
    assert!(!domains.is_empty());
    assert!(domains.contains("github.com"));
}

2. generate_mcp_configuration (onees.rs) β€” service connections and custom MCP exclusion

#[test]
fn test_generate_mcp_configuration_builtin_enabled() {
    let mut mcps = HashMap::new();
    mcps.insert("ado".to_string(), McpConfig::Enabled(true));
    let result = generate_mcp_configuration(&mcps);
    assert!(result.contains("ado:"));
    assert!(result.contains("serviceConnection: mcp-ado-service-connection"));
}

#[test]
fn test_generate_mcp_configuration_custom_mcp_excluded() {
    let mut mcps = HashMap::new();
    mcps.insert("my-tool".to_string(), McpConfig::WithOptions(McpOptions {
        command: Some("node".to_string()),
        args: vec!["server.js".to_string()],
        allowed: vec!["*".to_string()],
        ..Default::default()
    }));
    let result = generate_mcp_configuration(&mcps);
    assert!(!result.contains("my-tool"), "custom MCPs must be excluded from 1ES config");
}

#[test]
fn test_generate_mcp_configuration_disabled_mcp_excluded() {
    let mut mcps = HashMap::new();
    mcps.insert("ado".to_string(), McpConfig::Enabled(false));
    let result = generate_mcp_configuration(&mcps);
    assert!(!result.contains("ado:"));
}

3. 1ES integration test β€” no unreplaced markers

#[test]
fn test_1es_fixture_compiled_output_no_unreplaced_markers() {
    let fixture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("fixtures")
        .join("1es-test-agent.md");
    let output_path = std::env::temp_dir().join("1es-test-agent.yml");

    let status = std::process::Command::new(env!("CARGO_BIN_EXE_ado-aw"))
        .args(["compile", fixture_path.to_str().unwrap(), "-o", output_path.to_str().unwrap()])
        .status()
        .expect("Failed to run ado-aw compile");

    assert!(status.success(), "1ES compilation should succeed");

    let output = fs::read_to_string(&output_path).expect("Should read 1ES output");
    // No unreplaced \{\{ markers }} should remain (but $\{\{ }} template expressions are OK)
    let unreplaced: Vec<_> = output.lines()
        .filter(|l| {
            let s = l.trim();
            s.contains("\{\{") && !s.contains("$\{\{")
        })
        .collect();
    assert!(unreplaced.is_empty(), "Unreplaced markers found: {:?}", unreplaced);
}

4. mcp_metadata.rs β€” has_tool and get_tools

#[test]
fn test_has_tool_returns_true_for_known_tool() {
    let metadata = McpMetadataFile::bundled();
    // "ado" MCP should have at least one tool; pick a known one
    let tools = metadata.get_tools("ado").expect("ado should have tools");
    let first_tool = &tools[0].name;
    assert!(metadata.has_tool("ado", first_tool));
}

#[test]
fn test_has_tool_returns_false_for_unknown_tool() {
    let metadata = McpMetadataFile::bundled();
    assert!(!metadata.has_tool("ado", "nonexistent_tool_xyz"));
}

#[test]
fn test_has_tool_returns_false_for_unknown_mcp() {
    let metadata = McpMetadataFile::bundled();
    assert!(!metadata.has_tool("nonexistent_mcp", "any_tool"));
}

#[test]
fn test_builtin_mcp_names_returns_only_builtins() {
    let metadata = McpMetadataFile::bundled();
    let builtins = metadata.builtin_mcp_names();
    assert!(builtins.contains(&"ado"));
    // All returned names should be marked builtin
    for name in &builtins {
        let mcp = metadata.get(name).unwrap();
        assert!(mcp.builtin, "{} should be builtin", name);
    }
}

#[test]
fn test_tool_names_returns_names_for_known_mcp() {
    let metadata = McpMetadataFile::bundled();
    let names = metadata.tool_names("ado");
    assert!(!names.is_empty(), "ado should have tool names");
}

#[test]
fn test_tool_names_returns_empty_for_unknown_mcp() {
    let metadata = McpMetadataFile::bundled();
    let names = metadata.tool_names("nonexistent");
    assert!(names.is_empty());
}

5. generate_source_path / generate_pipeline_path (common.rs)

#[test]
fn test_generate_source_path_root_workspace() {
    let path = std::path::Path::new("/agents/my-agent.md");
    let result = generate_source_path(path);
    assert!(result.contains("agents/my-agent.md"));
}

#[test]
fn test_generate_pipeline_path_filename_only() {
    let path = std::path::Path::new("/pipelines/my-pipeline.yml");
    let result = generate_pipeline_path(path);
    assert!(result.contains("my-pipeline.yml"));
    assert!(!result.contains("/pipelines/"), "should use workspace-relative path");
}

6. generate_acquire_ado_token / generate_copilot_ado_env

#[test]
fn test_generate_acquire_ado_token_none_returns_empty() {
    let result = generate_acquire_ado_token(&None);
    assert!(result.is_empty());
}

#[test]
fn test_generate_acquire_ado_token_some_returns_azure_cli_step() {
    let result = generate_acquire_ado_token(&Some("my-arm-connection".to_string()));
    assert!(result.contains("AzureCLI@2"));
    assert!(result.contains("my-arm-connection"));
    assert!(result.contains("SC_ACCESS_TOKEN"));
}

#[test]
fn test_generate_copilot_ado_env_none_returns_empty() {
    let result = generate_copilot_ado_env(&None);
    assert!(result.is_empty());
}

#[test]
fn test_generate_copilot_ado_env_some_includes_token_vars() {
    let result = generate_copilot_ado_env(&Some("my-conn".to_string()));
    assert!(result.contains("AZURE_DEVOPS_EXT_PAT"));
    assert!(result.contains("SYSTEM_ACCESSTOKEN"));
    assert!(result.contains("SC_ACCESS_TOKEN"));
}

Coverage Summary

Module Public Fns Private Fns Tests Key Gap
compile/standalone.rs 1 10 7 generate_allowed_domains and 9 other helpers untested
compile/onees.rs 0 (impl via trait) 5 0 All helpers untested; no 1ES integration test
mcp_metadata.rs 7 0 3 has_tool, get_tools, builtin_mcp_names, tool_names untested
compile/common.rs 22 0 33 generate_source_path, generate_pipeline_path, generate_repositories, generate_checkout_steps, replace_with_indent, format_step_yaml untested

This issue was created by the automated test gap finder. Previous run: 2026-03-09 (resolved in #21). Modules audited this cycle: all 23 source modules. Total tests found: 263 (240 unit + 23 integration).

Generated by Test Gap Finder Β· β—·

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions