diff --git a/.github/workflows/main-build.yaml b/.github/workflows/main-build.yaml
index 2e98281b..3207e8b0 100644
--- a/.github/workflows/main-build.yaml
+++ b/.github/workflows/main-build.yaml
@@ -86,7 +86,7 @@ jobs:
run: dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true
- name: Run tests
- run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./coverage
+ run: dotnet test --no-build --configuration Release --verbosity normal --results-directory ./coverage --coverage --coverage-output-format cobertura
- name: Stop SonarQube
env:
@@ -99,7 +99,7 @@ jobs:
- name: Upload coverage reports
uses: codecov/codecov-action@v5
with:
- files: ./coverage/**/coverage.cobertura.xml
+ files: ./coverage/**/*.cobertura.xml
disable_search: true
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
\ No newline at end of file
diff --git a/.github/workflows/pr-build.yaml b/.github/workflows/pr-build.yaml
index 1dc945b3..a9c8e0ff 100644
--- a/.github/workflows/pr-build.yaml
+++ b/.github/workflows/pr-build.yaml
@@ -107,7 +107,7 @@ jobs:
run: dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true
- name: Run tests
- run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./coverage
+ run: dotnet test --no-build --configuration Release --verbosity normal --results-directory ./coverage --coverage --coverage-output-format cobertura
- name: Stop SonarQube
env:
@@ -120,7 +120,7 @@ jobs:
- name: Upload coverage reports
uses: codecov/codecov-action@v5
with:
- files: ./coverage/**/coverage.cobertura.xml
+ files: ./coverage/**/*.cobertura.xml
disable_search: true
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
diff --git a/CLAUDE.md b/CLAUDE.md
index bb29332c..4de85d18 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -82,6 +82,64 @@ internal async Task MyTest(
- Combine both: use `[AutoNSubstituteData]` with `[Frozen]` to inject configured mocks from a manual
fixture
+# Test Commands
+
+Task wrappers (recommended):
+
+```bash
+task test:all # dotnet test (Release)
+task test:verbose # dotnet test (detailed logs)
+task test:coverage # dotnet test + coverage
+task test:watch # dotnet watch ... test
+```
+
+Direct `dotnet` commands:
+```bash
+# Run the full test suite (solution-level)
+DOTNET_NOLOGO=1 dotnet test --configuration Release
+
+# Faster inner loop: run a single target framework (projects are multi-targeted)
+DOTNET_NOLOGO=1 dotnet test --configuration Release -f net10.0
+
+# Run a single test project
+DOTNET_NOLOGO=1 dotnet test \
+ --project tests/MinimalLambda.UnitTests/MinimalLambda.UnitTests.csproj \
+ --configuration Release -f net10.0
+
+# Verbose output for debugging
+DOTNET_NOLOGO=1 dotnet test --configuration Release --logger "console;verbosity=detailed"
+```
+
+Run a single test (xUnit v3 + Microsoft.Testing.Platform)
+
+This repo pins the test runner in `global.json`:
+
+```json
+"test": {"runner": "Microsoft.Testing.Platform"}
+```
+
+```bash
+# list tests (copy fully-qualified name)
+DOTNET_NOLOGO=1 dotnet test \
+ --project tests/MinimalLambda.UnitTests/MinimalLambda.UnitTests.csproj \
+ -f net10.0 -v q \
+ --list-tests --no-progress --no-ansi
+
+# run one test method
+DOTNET_NOLOGO=1 dotnet test \
+ --project tests/MinimalLambda.UnitTests/MinimalLambda.UnitTests.csproj \
+ -f net10.0 -v q \
+ --filter-method "MyNamespace.MyTestClass.MyTestMethod" \
+ --minimum-expected-tests 1 \
+ --no-progress --no-ansi
+
+# handy filters
+DOTNET_NOLOGO=1 dotnet test --project tests/MinimalLambda.UnitTests/MinimalLambda.UnitTests.csproj -f net10.0 -v q \
+ --filter-class "MyNamespace.MyTestClass" --minimum-expected-tests 1 --no-progress --no-ansi
+DOTNET_NOLOGO=1 dotnet test --project tests/MinimalLambda.UnitTests/MinimalLambda.UnitTests.csproj -f net10.0 -v q \
+ --filter-namespace "MyNamespace.Tests" --minimum-expected-tests 1 --no-progress --no-ansi
+```
+
# C# 14 Extension Members - Valid Syntax
## This is VALID C# 14 syntax - do NOT change it
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a5021eb3..482713cd 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -293,8 +293,8 @@ dotnet test tests/MinimalLambda.UnitTests
# Run with verbose output
dotnet test --verbosity detailed
-# Run with coverage (if configured)
-dotnet test /p:CollectCoverage=true
+# Run with coverage (MTP)
+dotnet test --coverage --coverage-output-format cobertura
```
### Test Requirements
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 830af210..4df426c3 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -50,6 +50,7 @@
+
diff --git a/global.json b/global.json
index 672cfe47..4ea65709 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,9 @@
{
"sdk": {
"rollForward": "latestMinor",
- "version": "10.0.102"
+ "version": "10.0.101"
+ },
+ "test": {
+ "runner": "Microsoft.Testing.Platform"
}
-}
\ No newline at end of file
+}
diff --git a/opencode.json b/opencode.json
new file mode 100644
index 00000000..81bc9f48
--- /dev/null
+++ b/opencode.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://opencode.ai/config.json",
+ "permission": {
+ "git": "ask"
+ }
+}
\ No newline at end of file
diff --git a/src/MinimalLambda.Testing/LambdaApplicationFactory.cs b/src/MinimalLambda.Testing/LambdaApplicationFactory.cs
index a10e0b2c..e2c5bbfc 100644
--- a/src/MinimalLambda.Testing/LambdaApplicationFactory.cs
+++ b/src/MinimalLambda.Testing/LambdaApplicationFactory.cs
@@ -286,6 +286,13 @@ private void ConfigureHostBuilder(
if (string.IsNullOrEmpty(options.BootstrapOptions.RuntimeApiEndpoint))
options.BootstrapOptions.RuntimeApiEndpoint = "localhost";
});
+
+ services.PostConfigure(options =>
+ {
+ var minShutdownTimeout = TimeSpan.FromSeconds(30);
+ if (options.ShutdownTimeout < minShutdownTimeout)
+ options.ShutdownTimeout = minShutdownTimeout;
+ });
});
// Build the host but DON'T start it - server will start it
diff --git a/tasks/TestTasks.yml b/tasks/TestTasks.yml
index 92b53388..6c25f337 100644
--- a/tasks/TestTasks.yml
+++ b/tasks/TestTasks.yml
@@ -14,7 +14,7 @@ tasks:
silent: true
cmds:
- echo "🧪 Running unit tests"
- - dotnet test --configuration Release --filter "Category=Unit" --no-build
+ - dotnet test --configuration Release --filter-trait "Category=Unit" --no-build
- echo "✅ Unit tests completed!"
coverage:
@@ -22,7 +22,7 @@ tasks:
silent: true
cmds:
- echo "📊 Running tests with coverage collection"
- - dotnet test --configuration Release --collect:"XPlat Code Coverage" --no-build
+ - dotnet test --configuration Release --coverage --coverage-output-format cobertura --no-build
- echo "✅ Coverage collection completed!"
watch:
@@ -30,7 +30,7 @@ tasks:
silent: true
cmds:
- echo "👀 Starting test watch mode"
- - dotnet watch --project tests/MinimalLambda.Tests test --configuration Release
+ - dotnet watch --project tests/MinimalLambda.UnitTests test --configuration Release
verbose:
desc: Run tests with detailed output for debugging
diff --git a/tests/MinimalLambda.Envelopes.UnitTests/MinimalLambda.Envelopes.UnitTests.csproj b/tests/MinimalLambda.Envelopes.UnitTests/MinimalLambda.Envelopes.UnitTests.csproj
index 3aedce4e..775bccf4 100644
--- a/tests/MinimalLambda.Envelopes.UnitTests/MinimalLambda.Envelopes.UnitTests.csproj
+++ b/tests/MinimalLambda.Envelopes.UnitTests/MinimalLambda.Envelopes.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net8.0;net9.0;net10.0
preview
@@ -17,6 +17,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/tests/MinimalLambda.OpenTelemetry.UnitTests/MinimalLambda.OpenTelemetry.UnitTests.csproj b/tests/MinimalLambda.OpenTelemetry.UnitTests/MinimalLambda.OpenTelemetry.UnitTests.csproj
index e8940ea9..6e6496bd 100644
--- a/tests/MinimalLambda.OpenTelemetry.UnitTests/MinimalLambda.OpenTelemetry.UnitTests.csproj
+++ b/tests/MinimalLambda.OpenTelemetry.UnitTests/MinimalLambda.OpenTelemetry.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net9.0;net10.0
preview
@@ -26,6 +26,7 @@
+
all
diff --git a/tests/MinimalLambda.SourceGenerators.UnitTests/MinimalLambda.SourceGenerators.UnitTests.csproj b/tests/MinimalLambda.SourceGenerators.UnitTests/MinimalLambda.SourceGenerators.UnitTests.csproj
index 56bd27fd..03343d24 100644
--- a/tests/MinimalLambda.SourceGenerators.UnitTests/MinimalLambda.SourceGenerators.UnitTests.csproj
+++ b/tests/MinimalLambda.SourceGenerators.UnitTests/MinimalLambda.SourceGenerators.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net8.0;net9.0;net10.0
latest
@@ -18,6 +18,7 @@
+
diff --git a/tests/MinimalLambda.Testing.UnitTests/Tests/MinimalLambda.Testing.UnitTests/MinimalLambda.Testing.UnitTests.csproj b/tests/MinimalLambda.Testing.UnitTests/Tests/MinimalLambda.Testing.UnitTests/MinimalLambda.Testing.UnitTests.csproj
index b9a2609d..ef7cfe69 100644
--- a/tests/MinimalLambda.Testing.UnitTests/Tests/MinimalLambda.Testing.UnitTests/MinimalLambda.Testing.UnitTests.csproj
+++ b/tests/MinimalLambda.Testing.UnitTests/Tests/MinimalLambda.Testing.UnitTests/MinimalLambda.Testing.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net8.0;net9.0;net10.0
enable
@@ -23,6 +23,7 @@
+
all
diff --git a/tests/MinimalLambda.UnitTests/MinimalLambda.UnitTests.csproj b/tests/MinimalLambda.UnitTests/MinimalLambda.UnitTests.csproj
index 7cf404d3..20855508 100644
--- a/tests/MinimalLambda.UnitTests/MinimalLambda.UnitTests.csproj
+++ b/tests/MinimalLambda.UnitTests/MinimalLambda.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
net8.0;net9.0;net10.0
enable
@@ -33,6 +33,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive