From 46db0d1e94466175870247ad527316081ac8eb3a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 01:10:40 +0000 Subject: [PATCH 1/2] Initial plan From 336290773e8663800d0705d1ee197b6c723102c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 01:20:27 +0000 Subject: [PATCH 2/2] Sync with TemplateDotNetTool: Polyfill, LangVersion latest, ArgumentNullException.ThrowIfNull, SonarAnalyzer update, test-developer agent updates Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com> --- .github/agents/test-developer.md | 61 +++++++++++++++++++ src/DemaConsulting.SonarMark/Context.cs | 6 ++ .../DemaConsulting.SonarMark.csproj | 18 +++++- src/DemaConsulting.SonarMark/Validation.cs | 3 + .../DemaConsulting.SonarMark.Tests.csproj | 11 +++- 5 files changed, 93 insertions(+), 6 deletions(-) diff --git a/.github/agents/test-developer.md b/.github/agents/test-developer.md index 02eb76b..91f62df 100644 --- a/.github/agents/test-developer.md +++ b/.github/agents/test-developer.md @@ -64,6 +64,23 @@ public void ClassName_MethodUnderTest_Scenario_ExpectedBehavior() - Failure-testing and error handling scenarios - Verifying internal behavior beyond requirement scope +### Test Source Filters + +Test links in `requirements.yaml` can include a source filter prefix to restrict which test results count as +evidence. These filters are critical for platform and framework requirements - **do not remove them**. + +- `windows@TestName` - proves the test passed on a Windows platform +- `ubuntu@TestName` - proves the test passed on a Linux (Ubuntu) platform +- `net8.0@TestName` - proves the test passed under the .NET 8 target framework +- `net9.0@TestName` - proves the test passed under the .NET 9 target framework +- `net10.0@TestName` - proves the test passed under the .NET 10 target framework +- `dotnet8.x@TestName` - proves the self-validation test ran on a machine with .NET 8.x runtime +- `dotnet9.x@TestName` - proves the self-validation test ran on a machine with .NET 9.x runtime +- `dotnet10.x@TestName` - proves the self-validation test ran on a machine with .NET 10.x runtime + +Removing a source filter means a test result from any environment can satisfy the requirement, which invalidates +the evidence-based proof that the tool works on a specific platform or framework. + ### SonarMark-Specific - **NOT self-validation tests** - those are handled by Software Developer Agent @@ -71,6 +88,50 @@ public void ClassName_MethodUnderTest_Scenario_ExpectedBehavior() - Use MSTest V4 testing framework - Follow existing naming conventions in the test suite +### MSTest V4 Best Practices + +Common anti-patterns to avoid (not exhaustive): + +1. **Avoid Assertions in Catch Blocks (MSTEST0058)** - Instead of wrapping code in try/catch and asserting in the + catch block, use `Assert.ThrowsExactly()`: + + ```csharp + var ex = Assert.ThrowsExactly(() => SomeWork()); + Assert.Contains("Some message", ex.Message); + ``` + +2. **Avoid using Assert.IsTrue / Assert.IsFalse for equality checks** - Use `Assert.AreEqual` / + `Assert.AreNotEqual` instead, as it provides better failure messages: + + ```csharp + // ❌ Bad: Assert.IsTrue(result == expected); + // ✅ Good: Assert.AreEqual(expected, result); + ``` + +3. **Avoid non-public test classes and methods** - Test classes and `[TestMethod]` methods must be `public` or + they will be silently ignored: + + ```csharp + // ❌ Bad: internal class MyTests + // ✅ Good: public class MyTests + ``` + +4. **Avoid Assert.IsTrue(collection.Count == N)** - Use `Assert.HasCount` for count assertions: + + ```csharp + // ❌ Bad: Assert.IsTrue(collection.Count == 3); + // ✅ Good: Assert.HasCount(3, collection); + ``` + +5. **Avoid Assert.IsTrue for string prefix checks** - Use `Assert.StartsWith` instead of wrapping + `string.StartsWith` in `Assert.IsTrue`, as it produces clearer failure messages that show the expected prefix + and actual value: + + ```csharp + // ❌ Bad: Assert.IsTrue(value.StartsWith("prefix")); + // ✅ Good: Assert.StartsWith("prefix", value); + ``` + ## Defer To - **Requirements Agent**: For test strategy and coverage requirements diff --git a/src/DemaConsulting.SonarMark/Context.cs b/src/DemaConsulting.SonarMark/Context.cs index 8ca31ed..e2a1f47 100644 --- a/src/DemaConsulting.SonarMark/Context.cs +++ b/src/DemaConsulting.SonarMark/Context.cs @@ -120,6 +120,9 @@ private Context() /// Thrown when arguments are invalid. public static Context Create(string[] args) { + // Validate that args is not null + ArgumentNullException.ThrowIfNull(args); + return Create(args, null); } @@ -132,6 +135,9 @@ public static Context Create(string[] args) /// Thrown when arguments are invalid. public static Context Create(string[] args, Func? httpClientFactory) { + // Validate that args is not null + ArgumentNullException.ThrowIfNull(args); + // Parse command-line arguments into structured form var parser = new ArgumentParser(); parser.ParseArguments(args); diff --git a/src/DemaConsulting.SonarMark/DemaConsulting.SonarMark.csproj b/src/DemaConsulting.SonarMark/DemaConsulting.SonarMark.csproj index db19065..bf29e75 100644 --- a/src/DemaConsulting.SonarMark/DemaConsulting.SonarMark.csproj +++ b/src/DemaConsulting.SonarMark/DemaConsulting.SonarMark.csproj @@ -3,7 +3,7 @@ Exe net8.0;net9.0;net10.0 - 12 + latest enable enable @@ -34,7 +34,7 @@ true - True + true true true latest @@ -48,13 +48,25 @@ + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/DemaConsulting.SonarMark/Validation.cs b/src/DemaConsulting.SonarMark/Validation.cs index 255956e..5a4227d 100644 --- a/src/DemaConsulting.SonarMark/Validation.cs +++ b/src/DemaConsulting.SonarMark/Validation.cs @@ -48,6 +48,9 @@ internal static class Validation /// The context containing command line arguments and program state. public static void Run(Context context) { + // Validate that context is not null + ArgumentNullException.ThrowIfNull(context); + // Print validation header PrintValidationHeader(context); diff --git a/test/DemaConsulting.SonarMark.Tests/DemaConsulting.SonarMark.Tests.csproj b/test/DemaConsulting.SonarMark.Tests/DemaConsulting.SonarMark.Tests.csproj index a571dd0..d8566b4 100644 --- a/test/DemaConsulting.SonarMark.Tests/DemaConsulting.SonarMark.Tests.csproj +++ b/test/DemaConsulting.SonarMark.Tests/DemaConsulting.SonarMark.Tests.csproj @@ -9,7 +9,7 @@ false true - True + true true @@ -18,13 +18,18 @@ latest + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + @@ -35,7 +40,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive