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
    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