Skip to content

πŸ§ͺ Test gap analysis β€” 4 gaps found in dotnet runtime (0 tests)Β #459

@github-actions

Description

@github-actions

Test Gap Analysis

Test suite snapshot: 1,225 unit tests, 80 integration tests (tests/compiler_tests.rs), 3 init tests, 8 MCP HTTP tests β€” 1,317 total. All pass. βœ…

Previous issues #376 and #392 are still open. The gaps below are new β€” introduced by the runtimes/dotnet module (added since the last run at v0.24.0 β†’ v0.26.1). The entire dotnet runtime has zero tests.


Priority Gaps

Module Function/Path Why It Matters Suggested Test
runtimes/dotnet/mod.rs generate_dotnet_install() β€” 3 code paths, 0 tests Generates UseDotNet@2 step; the use_global_json path, explicit version path, and default version path all emit different YAML β€” any regression in template formatting is silent Unit test each branch: assert useGlobalJson: true, version: '8.0.x', and the default 8.0.x version string
runtimes/dotnet/mod.rs generate_ensure_nuget_config() β€” 0 tests Generates a bash step that writes a nuget.config file; the feed URL is interpolated into a heredoc β€” a format regression would break NuGet authentication silently Assert the generated step contains the feed URL and the correct XML structure
runtimes/dotnet/extension.rs DotnetExtension::validate() β€” 6 branches, 0 tests Contains three anyhow::bail! paths: (1) config + feed-url mutual exclusion, (2) injection in version, (3) global.json conflict detection when a global.json file exists alongside a pinned version. These are user-facing error messages; any regression silently accepts invalid input. Test each bail path returns the right error message
tests/compiler_tests.rs No end-to-end compilation test for runtimes: dotnet: All other runtimes have at least one integration test: test_lean_runtime_compiled_output() (line 2693). The dotnet runtime is wired into collect_extensions() but its output (UseDotNet@2, NuGetAuthenticate@1, dotnet bash allow-list) has never been verified through a full compile. Add test_dotnet_runtime_compiled_output() similar to test_lean_runtime_compiled_output()

Suggested Test Cases

1. generate_dotnet_install() β€” three variants

// In src/runtimes/dotnet/mod.rs, add a #[cfg(test)] block:
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_generate_dotnet_install_default() {
        let config = DotnetRuntimeConfig::Enabled(true);
        let step = generate_dotnet_install(&config);
        assert!(step.contains("UseDotNet@2"), "should use UseDotNet@2 task");
        assert!(step.contains("version: '8.0.x'"), "default version should be 8.0.x");
        assert!(!step.contains("useGlobalJson"), "should not emit useGlobalJson for default");
    }

    #[test]
    fn test_generate_dotnet_install_explicit_version() {
        let config = DotnetRuntimeConfig::WithOptions(DotnetOptions {
            version: Some("9.0.x".to_string()),
            ..Default::default()
        });
        let step = generate_dotnet_install(&config);
        assert!(step.contains("version: '9.0.x'"), "should use specified version");
        assert!(!step.contains("useGlobalJson"), "should not emit useGlobalJson");
    }

    #[test]
    fn test_generate_dotnet_install_global_json() {
        let config = DotnetRuntimeConfig::WithOptions(DotnetOptions {
            version: Some("global.json".to_string()),
            ..Default::default()
        });
        let step = generate_dotnet_install(&config);
        assert!(step.contains("useGlobalJson: true"), "should emit useGlobalJson: true");
        assert!(!step.contains("version: '"), "should not emit explicit version with useGlobalJson");
    }

    #[test]
    fn test_generate_dotnet_install_global_json_case_insensitive() {
        let config = DotnetRuntimeConfig::WithOptions(DotnetOptions {
            version: Some("Global.JSON".to_string()),
            ..Default::default()
        });
        let step = generate_dotnet_install(&config);
        assert!(step.contains("useGlobalJson: true"), "sentinel should be case-insensitive");
    }
}

2. generate_ensure_nuget_config() β€” feed URL interpolation

#[test]
fn test_generate_ensure_nuget_config_contains_feed_url() {
    let config = DotnetRuntimeConfig::WithOptions(DotnetOptions {
        feed_url: Some("(pkgs.dev.azure.com/redacted),
        ..Default::default()
    });
    let step = generate_ensure_nuget_config(&config);
    assert!(step.contains("(pkgs.dev.azure.com/redacted)
    assert!(step.contains("<packageSources>"), "should emit valid nuget.config XML");
    assert!(step.contains("nuget.config"), "step should reference nuget.config");
}

#[test]
fn test_generate_ensure_nuget_config_default_feed() {
    let config = DotnetRuntimeConfig::Enabled(true);
    let step = generate_ensure_nuget_config(&config);
    assert!(step.contains("(api.nuget.org/redacted), "default feed is nuget.org");
}

3. DotnetExtension::validate() β€” error paths

// In src/runtimes/dotnet/extension.rs, add a #[cfg(test)] block:
#[cfg(test)]
mod tests {
    use super::*;
    use crate::compile::types::FrontMatter;
    use crate::compile::extensions::CompileContext;
    use std::path::Path;

    fn make_ctx<'a>(compile_dir: Option<&'a Path>) -> CompileContext<'a> {
        CompileContext {
            agent_name: "test-agent",
            front_matter: &FrontMatter::default(),
            compile_dir,
        }
    }

    #[test]
    fn test_validate_config_and_feed_url_mutually_exclusive() {
        let ext = DotnetExtension::new(DotnetRuntimeConfig::WithOptions(DotnetOptions {
            feed_url: Some("(pkgs.dev.azure.com/redacted),
            config: Some("nuget.config".to_string()),
            ..Default::default()
        }));
        let result = ext.validate(&make_ctx(None));
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("mutually exclusive"), "error should mention mutual exclusion");
    }

    #[test]
    fn test_validate_global_json_conflict_errors() {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("global.json"), r#"{"sdk":{"version":"8.0.100"}}"#).unwrap();
        let ext = DotnetExtension::new(DotnetRuntimeConfig::WithOptions(DotnetOptions {
            version: Some("8.0.x".to_string()),  // explicit version + global.json = error
            ..Default::default()
        }));
        let result = ext.validate(&make_ctx(Some(dir.path())));
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("global.json"), "error should mention global.json conflict");
    }

    #[test]
    fn test_validate_global_json_sentinel_with_global_json_ok() {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("global.json"), r#"{"sdk":{"version":"8.0.100"}}"#).unwrap();
        let ext = DotnetExtension::new(DotnetRuntimeConfig::WithOptions(DotnetOptions {
            version: Some("global.json".to_string()),  // sentinel = OK
            ..Default::default()
        }));
        let result = ext.validate(&make_ctx(Some(dir.path())));
        assert!(result.is_ok(), "sentinel should suppress the global.json conflict error");
    }
}

4. End-to-end compilation test

// In tests/compiler_tests.rs, add after test_lean_runtime_compiled_output:
#[test]
fn test_dotnet_runtime_compiled_output() {
    let temp_dir = TempDir::new().unwrap();
    let input = format!(
        "---\nname: \"dotnet-agent-{}\"\ndescription: \"Test dotnet runtime\"\n\
        runtimes:\n  dotnet: true\nsafe-outputs:\n  noop: \{\{}}\n---\n\n## Dotnet Agent\n",
        std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs()
    );
    let input_path = temp_dir.path().join("dotnet-agent.md");
    let output_path = temp_dir.path().join("dotnet-agent.yml");
    std::fs::write(&input_path, &input).unwrap();

    let result = Compiler::compile_file(&input_path, &output_path, &CompileOptions::default());
    assert!(result.is_ok(), "dotnet runtime compilation failed: {:?}", result.err());

    let compiled = std::fs::read_to_string(&output_path).unwrap();
    assert!(compiled.contains("UseDotNet@2"), "Should include UseDotNet@2 install task");
    assert!(compiled.contains("dotnet"), "Should include dotnet in bash allow-list");
    // nuget.org should be in the network allow-list
    assert!(compiled.contains("nuget.org") || compiled.contains("dotnet"),
        "Should include .NET ecosystem domains");
}

#[test]
fn test_dotnet_runtime_with_feed_url_compiled_output() {
    let temp_dir = TempDir::new().unwrap();
    let feed = "(pkgs.dev.azure.com/redacted)
    let input = format!(
        "---\nname: \"dotnet-feed-agent-{}\"\ndescription: \"Test dotnet runtime with feed\"\n\
        runtimes:\n  dotnet:\n    version: \"8.0.x\"\n    feed-url: \"{}\"\n\
        safe-outputs:\n  noop: \{\{}}\n---\n\n## Dotnet Feed Agent\n",
        std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
        feed
    );
    let input_path = temp_dir.path().join("dotnet-feed-agent.md");
    let output_path = temp_dir.path().join("dotnet-feed-agent.yml");
    std::fs::write(&input_path, &input).unwrap();

    let result = Compiler::compile_file(&input_path, &output_path, &CompileOptions::default());
    assert!(result.is_ok(), "dotnet+feed compilation failed: {:?}", result.err());

    let compiled = std::fs::read_to_string(&output_path).unwrap();
    assert!(compiled.contains("UseDotNet@2"), "Should include UseDotNet@2");
    assert!(compiled.contains("NuGetAuthenticate@1"), "Should include NuGetAuthenticate@1 when feed-url is set");
    assert!(compiled.contains("nuget.config"), "Should emit ensure-nuget.config step when feed-url is set");
}

Coverage Summary

Module Public Fns Tests Coverage Estimate
runtimes/dotnet/mod.rs generate_dotnet_install, generate_nuget_authenticate, generate_ensure_nuget_config, DotnetRuntimeConfig methods (Γ—5) 0 ~0%
runtimes/dotnet/extension.rs DotnetExtension::validate (6 branches), DotnetExtension::prepare_steps (3 paths) 0 ~0%
tests/compiler_tests.rs dotnet end-to-end 0 fixtures, 0 tests 0%

This issue was created by the automated test gap finder. Previous runs: 2026-05-01 (#376, open), 2026-05-04 (#392, open). Modules audited this cycle: runtimes/dotnet (new since v0.24.0). Total tests found: 1,317 (up from 1,278 at last run).

Generated by Test Gap Finder Β· ● 711.7K Β· β—·

Metadata

Metadata

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