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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions