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 Β· β·
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/dotnetmodule (added since the last run at v0.24.0 β v0.26.1). The entire dotnet runtime has zero tests.Priority Gaps
runtimes/dotnet/mod.rsgenerate_dotnet_install()β 3 code paths, 0 testsUseDotNet@2step; theuse_global_jsonpath, explicit version path, and default version path all emit different YAML β any regression in template formatting is silentuseGlobalJson: true,version: '8.0.x', and the default8.0.xversion stringruntimes/dotnet/mod.rsgenerate_ensure_nuget_config()β 0 testsnuget.configfile; the feed URL is interpolated into a heredoc β a format regression would break NuGet authentication silentlyruntimes/dotnet/extension.rsDotnetExtension::validate()β 6 branches, 0 testsanyhow::bail!paths: (1)config+feed-urlmutual exclusion, (2) injection inversion, (3)global.jsonconflict detection when aglobal.jsonfile exists alongside a pinned version. These are user-facing error messages; any regression silently accepts invalid input.tests/compiler_tests.rsruntimes: dotnet:test_lean_runtime_compiled_output()(line 2693). The dotnet runtime is wired intocollect_extensions()but its output (UseDotNet@2,NuGetAuthenticate@1,dotnetbash allow-list) has never been verified through a full compile.test_dotnet_runtime_compiled_output()similar totest_lean_runtime_compiled_output()Suggested Test Cases
1.
generate_dotnet_install()β three variants2.
generate_ensure_nuget_config()β feed URL interpolation3.
DotnetExtension::validate()β error paths4. End-to-end compilation test
Coverage Summary
runtimes/dotnet/mod.rsgenerate_dotnet_install,generate_nuget_authenticate,generate_ensure_nuget_config,DotnetRuntimeConfigmethods (Γ5)runtimes/dotnet/extension.rsDotnetExtension::validate(6 branches),DotnetExtension::prepare_steps(3 paths)tests/compiler_tests.rsThis 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).