-
Notifications
You must be signed in to change notification settings - Fork 2
Aspire 13.1.0, .NET 10 support, and TUnit migration #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Update project to target .NET 10.0 and Aspire 13.1.0 - Update LocalStack container image to 4.12.0 - Add logging filter to suppress health check noise - Update documentation (README, CHANGELOG, playground)
BREAKING CHANGE: Test framework replaced - CI/CD pipelines using xUnit commands may need updates - Convert 453 tests (432 unit, 21 integration) to TUnit syntax - Replace [Fact]/[Theory] with [Test] and [InlineData] with [Arguments] - Migrate assertions to TUnit's async-first API (await Assert.That(...)) - Replace IAsyncLifetime with IAsyncInitializer + IAsyncDisposable - Add TestOutputHelper for null-safe test output writing - Configure Microsoft.Testing.Platform runner in global.json - Remove xunit.runner.json configuration files - Update Directory.Packages.props and tests/Directory.Build.props Benefits: - Source-generated test discovery for faster execution - Built on Microsoft.Testing.Platform (VSTest replacement) - Native async assertion support throughout - Better AOT compilation compatibility
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.OpenSSF Scorecard
Scanned Files
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR upgrades the LocalStack Aspire integration to support .NET Aspire 13.1.0 and .NET 10.0, updates the LocalStack container from 4.10.0 to 4.12.0, and migrates the entire test suite (453 tests) from xUnit v3 to TUnit v1.5.70.
Key changes:
- Aspire 13.1.0 and .NET 10 support with multi-targeting (net8.0, net9.0, net10.0)
- LocalStack container updated to 4.12.0
- Complete test framework migration to TUnit with Microsoft.Testing.Platform
- Health check improvements with log suppression for reduced noise
Reviewed changes
Copilot reviewed 52 out of 52 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| Directory.Packages.props | Updated Aspire to 13.1.0, AWS SDKs, analyzers, and added TUnit 1.5.70 |
| Directory.Build.props | Changed default target framework to net10.0 |
| global.json | Updated SDK to 10.0.100 and configured Microsoft.Testing.Platform |
| tests/Directory.Build.props | Replaced xUnit packages with TUnit |
| src/Aspire.Hosting.LocalStack/Aspire.Hosting.LocalStack.csproj | Bumped version to 13.1.0 |
| src/Aspire.Hosting.LocalStack/Container/LocalStackContainerImageTags.cs | Updated container tag to 4.12.0 |
| src/Aspire.Hosting.LocalStack/LocalStackResourceBuilderExtensions.cs | Added logging configuration for health check client |
| src/Aspire.Hosting.LocalStack/Internal/LocalStackHealthCheck.cs | Added exception handling for startup errors |
| tests/**/GlobalUsings.cs | Replaced xUnit imports with TUnit |
| tests/**/*.cs | Converted all tests to TUnit syntax and async assertions |
| tests/**/xunit.runner.json | Removed xUnit configuration files |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #pragma warning disable MA0015 | ||
| ArgumentNullException.ThrowIfNull(expectedLocalStack.Options); | ||
| #pragma warning restore MA0015 |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pragma directive disabling MA0015 (ArgumentNullException.ThrowIfNull should be preferred) appears unnecessary here since the method correctly uses ArgumentNullException.ThrowIfNull on line 58. Consider removing this pragma unless there's a specific reason for the suppression.
| <PackageVersion Include="Aspire.Hosting.AppHost" Version="9.5.2"/> | ||
| <PackageVersion Include="Aspire.Hosting" Version="13.1.0"/> | ||
| <PackageVersion Include="Aspire.Hosting.AppHost" Version="13.1.0"/> | ||
| <PackageVersion Include="Aspire.Hosting.AWS" Version="9.3.0"/> |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Aspire.Hosting.AWS package version (9.3.0) is incompatible with Aspire.Hosting 13.1.0. This package should be updated to 13.1.0 to match the Aspire.Hosting version to avoid potential runtime compatibility issues.
| <PackageVersion Include="Aspire.Hosting.AWS" Version="9.3.0"/> | |
| <PackageVersion Include="Aspire.Hosting.AWS" Version="13.1.0"/> |
| await Assert.That(servicesNode).IsNotNull(); | ||
|
|
||
| // Services should not be running by default (lazy loading) | ||
| if (servicesNode.ContainsKey("sqs")) |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inefficient use of 'ContainsKey' and indexer.
Closes #19 - Update run-unix.sh and run-win.ps1 to use `dotnet test --project` - Required for TUnit/Microsoft.Testing.Platform which doesn't accept positional arguments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 54 out of 54 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
tests/Aspire.Hosting.LocalStack.Unit.Tests/Extensions/LocalStackProjectExtensionsTests.cs
Outdated
Show resolved
Hide resolved
|
@copilot Review the workflow output. Unit tests are failing. Identify the issue and commit a fix. |
|
@Blind-Striker I've opened a new pull request, #21, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@Blind-Striker I've opened a new pull request, #22, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
Hey @copilot check following workflow output 2025-12-18T14:37:42.3304782Z Prepare all required actions
Run ./.github/actions/run-dotnet-tests
with:
project-path: /home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/tests/Aspire.Hosting.LocalStack.Unit.Tests/Aspire.Hosting.LocalStack.Unit.Tests.csproj
results-dir: /home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/test-results/unit
configuration: Release
env:
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_NOLOGO: true
NUGET_PACKAGES: /home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/.nuget/packages
BASE_TEST_RESULTS_DIR: /home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/test-results
UNIT_TEST_PROJECT_PATH: /home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/tests/Aspire.Hosting.LocalStack.Unit.Tests/Aspire.Hosting.LocalStack.Unit.Tests.csproj
INTEGRATION_TEST_PROJECT_PATH: /home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/tests/Aspire.Hosting.LocalStack.Integration.Tests/Aspire.Hosting.LocalStack.Integration.Tests.csproj
BADGE_API_ENDPOINT: https://api.localstackfor.net
DOTNET_ROOT: /usr/share/dotnet
Run "/home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/./.github/actions/run-dotnet-tests/run-unix.sh" \
"/home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/./.github/actions/run-dotnet-tests/run-unix.sh" \
"/home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/tests/Aspire.Hosting.LocalStack.Unit.Tests/Aspire.Hosting.LocalStack.Unit.Tests.csproj" \
"/home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/test-results/unit" \
"Release"
shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
env:
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_NOLOGO: true
NUGET_PACKAGES: /home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/.nuget/packages
BASE_TEST_RESULTS_DIR: /home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/test-results
UNIT_TEST_PROJECT_PATH: /home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/tests/Aspire.Hosting.LocalStack.Unit.Tests/Aspire.Hosting.LocalStack.Unit.Tests.csproj
INTEGRATION_TEST_PROJECT_PATH: /home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/tests/Aspire.Hosting.LocalStack.Integration.Tests/Aspire.Hosting.LocalStack.Integration.Tests.csproj
BADGE_API_ENDPOINT: https://api.localstackfor.net
DOTNET_ROOT: /usr/share/dotnet
📋 Target frameworks: net10.0 net8.0 net9.0
🧪 net10.0 ...
�[m/home/runner/work/dotnet-aspire-for-localstack/dotnet-aspire-for-localstack/tests/Aspire.Hosting.LocalStack.Unit.Tests/bin/Release/net10.0/Aspire.Hosting.LocalStack.Unit.Tests.dll (net10.0) Zero tests ran�[m
Exit code: 5
Standard output: Unknown option '--logger'
�[mUsage Aspire.Hosting.LocalStack.Unit.Tests [option providers] [extension option providers]
Execute a .NET Test Application.
Options:
--config-file
Specifies a testconfig.json file.
--debug
Allows to pause execution in order to attach to the process for debug purposes.
--diagnostic
Enable the diagnostic logging. The default log level is 'Trace'.
The file will be written in the output directory with the name log_[yyMMddHHmmssfff].diag
--diagnostic-file-prefix
Prefix for the log file name that will replace '[log]_.'
--diagnostic-output-directory
Output directory of the diagnostic logging.
If not specified the file will be generated inside the default 'TestResults' directory.
--diagnostic-synchronous-write
Force the built-in file logger to write the log synchronously.
Useful for scenario where you don't want to lose any log (i.e. in case of crash).
Note that this is slowing down the test execution.
--diagnostic-verbosity
Define the level of the verbosity for the --diagnostic.
The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical'.
--exit-on-process-exit
Exit the test process if dependent process exits. PID must be provided.
--filter-uid
Provides a list of test node UIDs to filter by.
--help
Show the command line help.
--ignore-exit-code
Do not report non successful exit value for specific exit codes
(e.g. '--ignore-exit-code 8;9' ignore exit code 8 and 9 and will return 0 in these case)
--info
Display .NET test application information.
--list-tests
List available tests.
--minimum-expected-tests
Specifies the minimum number of tests that are expected to run.
--results-directory
The directory where the test results are going to be placed.
If the specified directory doesn't exist, it's created.
The default is TestResults in the directory that contains the test application.
--timeout
A global test execution timeout.
Takes one argument as string in the format <value>[h|m|s] where 'value' is float.
Extension options:
--adaptive-metrics
Enable detailed metrics logging for adaptive parallelism
--coverage
Collect the code coverage using dotnet-coverage tool.
--coverage-output
The output of the generated code coverage report.
If only filename is specified then the coverage report will be generated in the '--results-directory' directory.
--coverage-output-format
Output file format. Supported values: 'coverage', 'xml' and 'cobertura'
--coverage-settings
The path to the code coverage XML settings file.
--detailed-stacktrace
Display TUnit internals within stack traces
--disable-logo
Disables the TUnit logo when starting a test session
--fail-fast
Cancel the test run after the first test failure
--github-reporter-style
GitHub reporter output style: collapsible (default) or full
--junit-output-path
Path to output JUnit XML file (default: TestResults/{AssemblyName}-junit.xml)
--log-level
Minimum log level for test output: Trace, Debug, Information, Warning, Error, Critical, None (default: Information)
--maximum-failed-tests
Specifies a maximum number of test failures that, when exceeded, will abort the test run.
--maximum-parallel-tests
Override maximum parallel tests (defaults to auto-detected optimal value)
--no-ansi
Disable outputting ANSI escape characters to screen.
--no-progress
Disable reporting progress to screen.
--output
Output verbosity when reporting tests.
Valid values are 'Normal', 'Detailed'. Default is 'Normal'.
--parallelism-strategy
Parallelism strategy: fixed or adaptive (default: adaptive)
--reflection
Enable reflection mode for test discovery and execution (defaults to AOT-optimized mode)
--report-trx
Enable generating TRX report
--report-trx-filename
The name of the generated TRX report
--test-parameter
Custom parameters to pass to TUnit
--treenode-filter
Use a tree filter to filter down the tests to execute
Test run summary: Zero tests ran
error: 1
�[m
�[m total: 0
failed: 0
succeeded: 0
skipped: 0
duration: 205ms
Test run completed with non-success exit code: 1 (see: https://aka.ms/testingplatform/exitcodes)
Process completed with exit code 1.Unit tests are failing, review the code and commit a fix |
|
@Blind-Striker I've opened a new pull request, #23, to work on those changes. Once the pull request is ready, I'll request review from you. |
- Update run-unix.sh and run-win.ps1 to use `dotnet test --project` - Replace --logger trx with --report-trx (native M.T.P. flag) - Fix CD job unit test command for Microsoft.Testing.Platform
Remove unnecessary [NotInParallel("CdkTests")] from CdkBootstrapManagerTests.
The attribute triggered a race condition in TUnit's constraint scheduler on
Linux where queued tests never started after constraint keys were released.
These unit tests don't actually require sequential execution since each test
creates a unique GUID-based temp directory, ensuring complete isolation.
Root cause: TUnit v1.5.70 constraint queue fails to wake queued tests on
Linux when combined with Microsoft.Testing.Platform session coordination.
Symptoms observed:
- 143/144 tests pass, last test never starts
- Debug log shows test "Queueing" but never "Starting" after key release
- Process hangs indefinitely waiting for test that will never run
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 54 out of 54 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
tests/Aspire.Hosting.LocalStack.Unit.Tests/Internal/ConstantsTests.cs
Outdated
Show resolved
Hide resolved
src/Aspire.Hosting.LocalStack/Internal/LocalStackHealthCheck.cs
Outdated
Show resolved
Hide resolved
tests/Aspire.Hosting.LocalStack.Unit.Tests/Extensions/LocalStackProjectExtensionsTests.cs
Outdated
Show resolved
Hide resolved
| public static IEnumerable<(LocalStackLogLevel, string)> AllLogLevelMappingsTestData() | ||
| { | ||
| yield return (LocalStackLogLevel.Trace, "trace"); | ||
| yield return (LocalStackLogLevel.TraceInternal, "trace-internal"); | ||
| yield return (LocalStackLogLevel.Debug, "debug"); | ||
| yield return (LocalStackLogLevel.Info, "info"); | ||
| yield return (LocalStackLogLevel.Warn, "warn"); | ||
| yield return (LocalStackLogLevel.Error, "error"); | ||
| yield return (LocalStackLogLevel.Warning, "warning"); | ||
| } |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The migration from MemberData returning IEnumerable<object[]> to MethodDataSource returning IEnumerable<(LocalStackLogLevel, string)> is correct. However, the method should be marked as static to avoid creating an instance for each test invocation.
- fix(healthcheck): propagate OperationCanceledException during shutdown - test(coverage): add unit tests for LocalStackHealthCheck exception paths - refactor(tests): replace generic throws with TUnit IsSameReferenceAs assertion - refactor(tests): scope TUnitAssertions0005 pragma suppression - chore: update CHANGELOG.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 54 out of 54 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Assert.Equal("4.10.0", imageAnnotation.Tag); | ||
| await Assert.That(imageAnnotation.Registry).IsEqualTo("docker.io"); | ||
| await Assert.That(imageAnnotation.Image).IsEqualTo("localstack/localstack"); | ||
| await Assert.That(imageAnnotation.Tag).IsEqualTo("4.12.0"); |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default container image tag has been updated from 4.10.0 to 4.12.0, but the test assertion still expects 4.10.0. This test needs to be updated to expect the new version.
| Assert.Equal("4.10.0", imageAnnotation.Tag); // Default tag | ||
| await Assert.That(imageAnnotation.Registry).IsEqualTo(customRegistry); | ||
| await Assert.That(imageAnnotation.Image).IsEqualTo("localstack/localstack"); // Default image | ||
| await Assert.That(imageAnnotation.Tag).IsEqualTo("4.12.0"); // Default tag |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test assertion still expects the old container tag 4.10.0, but the default has been updated to 4.12.0. This will cause the test to fail.
| Assert.Equal("4.10.0", imageAnnotation.Tag); // Default tag | ||
| await Assert.That(imageAnnotation.Registry).IsEqualTo("docker.io"); // Default registry | ||
| await Assert.That(imageAnnotation.Image).IsEqualTo(customImage); | ||
| await Assert.That(imageAnnotation.Tag).IsEqualTo("4.12.0"); // Default tag |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test assertion still expects the old container tag 4.10.0, but the default has been updated to 4.12.0. This will cause the test to fail.
| "commandLineArgs": "exec --depsfile ./LocalStack.Lambda.UrlShortener.deps.json --runtimeconfig ./LocalStack.Lambda.UrlShortener.runtimeconfig.json %USERPROFILE%\\.dotnet\\tools\\.store\\amazon.lambda.testtool\\0.11.0\\amazon.lambda.testtool\\0.11.0\\content\\Amazon.Lambda.RuntimeSupport\\net8.0\\Amazon.Lambda.RuntimeSupport.dll LocalStack.Lambda.UrlShortener::LocalStack.Lambda.UrlShortener.Function::FunctionHandler", | ||
| "workingDirectory": ".\\bin\\$(Configuration)\\net8.0" | ||
| "commandLineArgs": "exec --depsfile ./LocalStack.Lambda.UrlShortener.deps.json --runtimeconfig ./LocalStack.Lambda.UrlShortener.runtimeconfig.json %USERPROFILE%\\.dotnet\\tools\\.store\\amazon.lambda.testtool\\0.11.0\\amazon.lambda.testtool\\0.11.0\\content\\Amazon.Lambda.RuntimeSupport\\net10.0\\Amazon.Lambda.RuntimeSupport.dll LocalStack.Lambda.UrlShortener::LocalStack.Lambda.UrlShortener.Function::FunctionHandler", | ||
| "workingDirectory": ".\\bin\\$(Configuration)\\net10.0" |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Lambda launch settings reference net8.0 in comments but have been updated to net10.0. However, the working directory paths should use a variable like $(TargetFramework) instead of hardcoded net10.0 to avoid similar issues in future framework updates.
| "workingDirectory": ".\\bin\\$(Configuration)\\net10.0" | |
| "workingDirectory": ".\\bin\\$(Configuration)\\$(TargetFramework)" |
What does this PR do?
This PR upgrades the LocalStack Aspire integration to support .NET Aspire 13.1.0 and .NET 10.0, updates the LocalStack container to 4.12.0, and migrates the entire test suite from xUnit v3 to TUnit v1.5.70.
Related Issue(s):
🔄 Type of Change
🎯 Aspire Compatibility
🧪 Testing
How has this been tested?
UseLocalStack())WithReference())Test Environment:
📚 Documentation
✅ Code Quality Checklist
🔍 Additional Notes
Breaking Changes:
Test framework migration from xUnit v3 to TUnit - CI/CD pipelines using xUnit-specific commands may need updates. The test runner scripts have been verified to work with TUnit via Microsoft.Testing.Platform.
Performance Impact:
Dependencies:
🎯 Reviewer Focus Areas
Please pay special attention to:
Key Changes Summary
1. Aspire 13.1 & .NET 10 Support
Directory.Build.propsto targetnet10.0as default2. LocalStack Container Update
3. Health Check Improvements
localstack_health_clientto reduce console noise4. TUnit Migration (453 tests)
[Fact]→[Test]and[Theory]→[Test][InlineData]→[Arguments]and[MemberData]→[MethodDataSource]await Assert.That(...).IsEqualTo(...))IAsyncLifetimewithIAsyncInitializer+IAsyncDisposableTestOutputHelperfor null-safe test output writingglobal.jsonBy submitting this pull request, I confirm that: