Skip to content

πŸ§ͺ Test gap analysis β€” 4 persistent gaps in compile/, mcp_metadata.rsΒ #112

@github-actions

Description

@github-actions

Test Gap Analysis

Test suite snapshot: 418 unit tests, 29 integration tests (compiler), 5 MCP firewall tests, 9 proxy tests β€” 461 total. All tests passing. Up 6 since previous run (2026-03-31); the core gaps from issue #22 remain unresolved.


Priority Gaps

Module Function/Path Why It Matters Suggested Test
compile/standalone.rs generate_allowed_domains Security-critical β€” controls the egress network allowlist passed to AWF. Wrong domains = agent can reach blocked hosts or be denied needed ones. Test with MCP enabled (verify MCP hosts included), with network.allow hosts (verify user hosts appended), with blocked-only config
compile/standalone.rs generate_setup_job, generate_teardown_job, generate_prepare_steps, generate_finalize_steps, generate_agentic_depends_on, generate_memory_download, generate_memory_prompt 7 of 9 non-trivial private helpers have zero tests; only generate_firewall_config is covered Test each helper returns correct YAML structure with expected pool/displayName/steps
compile/onees.rs generate_agent_context_root, generate_mcp_configuration, generate_inline_steps, generate_setup_job, generate_teardown_job Module has 0 unit tests; single integration test only checks no \{\{ marker }} strings remain Unit-test each helper in isolation; add 1ES fixture for setup+teardown
compile/common.rs replace_with_indent, generate_repositories, generate_checkout_steps, generate_source_path, generate_pipeline_path, generate_acquire_ado_token, generate_copilot_ado_env, generate_executor_ado_env, format_step_yaml 9 public helpers have no unit tests; replace_with_indent is the foundational indent-preserving template substitution used everywhere See suggested cases below
mcp_metadata.rs get_tools, has_tool, builtin_mcp_names, tool_names 4 of 7 public methods untested β€” metadata lookups are used by the firewall config generator Test has_tool with known/unknown MCP and tool names; builtin_mcp_names returns only non-custom entries

Suggested Test Cases

1. generate_allowed_domains β€” security-critical egress allowlist

#[test]
fn test_generate_allowed_domains_includes_core_hosts() {
    let fm = minimal_front_matter(); // no MCPs, no network config
    let domains = generate_allowed_domains(&fm);
    assert!(domains.contains("dev.azure.com"), "core ADO host must be present");
    assert!(domains.contains("github.com"), "core GitHub host must be present");
}

#[test]
fn test_generate_allowed_domains_includes_mcp_hosts() {
    let mut fm = minimal_front_matter();
    fm.mcp_servers.insert("ado".to_string(), McpConfig::Enabled(true));
    let domains = generate_allowed_domains(&fm);
    // ado MCP requires *.dev.azure.com β€” verify it's in the list
    assert!(domains.contains("dev.azure.com"));
}

#[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 must be included");
}

#[test]
fn test_generate_allowed_domains_disabled_mcp_not_included() {
    let mut fm = minimal_front_matter();
    fm.mcp_servers.insert("kusto".to_string(), McpConfig::Enabled(false));
    let domains_without = generate_allowed_domains(&fm);
    fm.mcp_servers.insert("kusto".to_string(), McpConfig::Enabled(true));
    let domains_with = generate_allowed_domains(&fm);
    // If kusto adds distinct hosts, the enabled version should have more
    let _ = (domains_without, domains_with); // assert host counts or specific kusto domains
}

2. replace_with_indent β€” foundational template substitution

#[test]
fn test_replace_with_indent_preserves_leading_spaces() {
    let template = "    \{\{ foo }}";
    let result = replace_with_indent(template, "\{\{ foo }}", "line1\nline2");
    assert_eq!(result, "    line1\n    line2");
}

#[test]
fn test_replace_with_indent_no_match_returns_original() {
    let template = "hello \{\{ bar }}";
    let result = replace_with_indent(template, "\{\{ foo }}", "replacement");
    assert_eq!(result, "hello \{\{ bar }}");
}

#[test]
fn test_replace_with_indent_empty_replacement_removes_line() {
    let template = "before\n    \{\{ foo }}\nafter";
    let result = replace_with_indent(template, "\{\{ foo }}", "");
    assert!(!result.contains("\{\{ foo }}"));
}

3. generate_acquire_ado_token β€” ADO token acquisition step

#[test]
fn test_generate_acquire_ado_token_with_service_connection() {
    let result = generate_acquire_ado_token(Some("my-arm-sc"), "SC_READ_TOKEN");
    assert!(result.contains("AzureCLI@2"));
    assert!(result.contains("my-arm-sc"));
    assert!(result.contains("SC_READ_TOKEN"));
    assert!(result.contains("issecret=true"), "token must be marked secret");
}

#[test]
fn test_generate_acquire_ado_token_none_returns_empty() {
    assert_eq!(generate_acquire_ado_token(None, "SC_READ_TOKEN"), "");
}

#[test]
fn test_generate_copilot_ado_env_with_connection() {
    let result = generate_copilot_ado_env(Some("my-sc"));
    assert!(result.contains("AZURE_DEVOPS_EXT_PAT: $(SC_READ_TOKEN)"));
    assert!(result.contains("SYSTEM_ACCESSTOKEN: $(SC_READ_TOKEN)"));
}

#[test]
fn test_generate_executor_ado_env_none_returns_empty() {
    assert_eq!(generate_executor_ado_env(None), "");
}

4. mcp_metadata β€” has_tool / builtin_mcp_names / tool_names

#[test]
fn test_has_tool_known_mcp_and_tool() {
    let registry = McpMetadataRegistry::bundled();
    // Pick a known built-in MCP (e.g. "ado") and one of its tools
    let mcp_name = registry.mcp_names().into_iter().next().unwrap();
    let tools = registry.get_tools(mcp_name).unwrap();
    let first_tool = tools[0].name.as_str();
    assert!(registry.has_tool(mcp_name, first_tool));
}

#[test]
fn test_has_tool_unknown_tool_returns_false() {
    let registry = McpMetadataRegistry::bundled();
    assert!(!registry.has_tool("ado", "this_tool_does_not_exist_xyz"));
}

#[test]
fn test_builtin_mcp_names_excludes_custom() {
    let registry = McpMetadataRegistry::bundled();
    let names = registry.builtin_mcp_names();
    // builtin names should be non-empty and should not include anything with "custom" marker
    assert!(!names.is_empty());
}

#[test]
fn test_tool_names_returns_nonempty_for_known_mcp() {
    let registry = McpMetadataRegistry::bundled();
    let mcp = registry.mcp_names().into_iter().next().unwrap();
    assert!(!registry.tool_names(mcp).is_empty());
}

#[test]
fn test_get_tools_unknown_mcp_returns_none() {
    let registry = McpMetadataRegistry::bundled();
    assert!(registry.get_tools("nonexistent_mcp_xyz").is_none());
}

5. compile/onees.rs β€” generate_agent_context_root / generate_mcp_configuration

#[test]
fn test_generate_agent_context_root_repo() {
    assert_eq!(generate_agent_context_root("repo"), "$(Build.Repository.Name)");
}

#[test]
fn test_generate_agent_context_root_root() {
    assert_eq!(generate_agent_context_root("root"), ".");
}

#[test]
fn test_generate_mcp_configuration_single_mcp() {
    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("mcp-ado-service-connection"));
}

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

Coverage Summary

Module Public Fns Tests Coverage Estimate
compile/standalone.rs 9 7 ~25% (only generate_firewall_config covered)
compile/onees.rs 5 private + 2 trait 0 unit 0% unit (1 integration marker check only)
compile/common.rs 25 37 ~55% (9 pub fns have zero tests)
mcp_metadata.rs 7 3 ~40% (4 methods untested)
All other modules β€” 414 Well-covered

Automated test gap finder β€” run 2026-04-01. Previous run: 2026-03-31 (issue #22 closed, gaps unresolved). Modules audited this cycle: all 29 source files. Tests: 461 total (+6 since last run).

Generated by Test Gap Finder Β· β—·

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • dev.azure.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "dev.azure.com"

See Network Configuration for more information.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions