diff --git a/.github/workflows/pipelines.yml b/.github/workflows/pipelines.yml index 2f4dfa2..608db0c 100644 --- a/.github/workflows/pipelines.yml +++ b/.github/workflows/pipelines.yml @@ -21,7 +21,7 @@ on: jobs: build: name: 🛠️ Build - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: configuration: [Debug, Release] @@ -62,7 +62,7 @@ jobs: pack: name: 📦 Pack - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: configuration: [Debug, Release] @@ -80,10 +80,16 @@ jobs: uploadPackedArtifact: true version: ${{ needs.build.outputs.version }} - sonarcloud: - name: 🔬 Code Quality Analysis + test: + name: 🧪 Test needs: [build] - runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, windows-2022] + configuration: [Debug, Release] + runs-on: ${{ matrix.os }} + timeout-minutes: 15 steps: - name: Checkout uses: codebeltnet/git-checkout@v1 @@ -93,64 +99,43 @@ jobs: with: includePreview: true - - name: Install .NET Tool - Sonar Scanner - uses: codebeltnet/dotnet-tool-install-sonarscanner@v1 - - - name: Restore Dependencies - uses: codebeltnet/dotnet-restore@v2 - - - name: Run SonarCloud Analysis - uses: codebeltnet/sonarcloud-scan@v1 - with: - token: ${{ secrets.SONAR_TOKEN }} - organization: geekle - projectKey: bootstrapper - version: ${{ needs.build.outputs.version }} + - name: Install .NET Tool - Report Generator + uses: codebeltnet/dotnet-tool-install-reportgenerator@v1 - - name: Build - uses: codebeltnet/dotnet-build@v2 + - name: Test with ${{ matrix.configuration }} build + uses: codebeltnet/dotnet-test@v3 with: + configuration: ${{ matrix.configuration }} buildSwitches: -p:SkipSignAssembly=true - uploadBuildArtifact: false - - name: Finalize SonarCloud Analysis - uses: codebeltnet/sonarcloud-scan-finalize@v1 - with: - token: ${{ secrets.SONAR_TOKEN }} + sonarcloud: + name: call-sonarcloud + needs: [build,test] + uses: codebeltnet/jobs-sonarcloud/.github/workflows/default.yml@v1 + with: + organization: geekle + projectKey: bootstrapper + version: ${{ needs.build.outputs.version }} + secrets: inherit + + codecov: + name: call-codecov + needs: [build,test] + uses: codebeltnet/jobs-codecov/.github/workflows/default.yml@v1 + with: + repository: codebeltnet/bootstrapper + secrets: inherit codeql: - name: 🛡️ Security Analysis - needs: [build] - runs-on: ubuntu-22.04 - steps: - - name: Checkout - uses: codebeltnet/git-checkout@v1 - - - name: Install .NET - uses: codebeltnet/install-dotnet@v1 - with: - includePreview: true - - - name: Restore Dependencies - uses: codebeltnet/dotnet-restore@v2 - - - name: Prepare CodeQL SAST Analysis - uses: codebeltnet/codeql-scan@v1 - - - name: Build - uses: codebeltnet/dotnet-build@v2 - with: - buildSwitches: -p:SkipSignAssembly=true - uploadBuildArtifact: false - - - name: Finalize CodeQL SAST Analysis - uses: codebeltnet/codeql-scan-finalize@v1 + name: call-codeql + needs: [build,test] + uses: codebeltnet/jobs-codeql/.github/workflows/default.yml@v1 deploy: if: github.event_name != 'pull_request' name: 🚀 Deploy v${{ needs.build.outputs.version }} - runs-on: ubuntu-22.04 - needs: [build,pack,sonarcloud,codeql] + runs-on: ubuntu-24.04 + needs: [build, pack, test, sonarcloud, codecov, codeql] environment: Production steps: - uses: codebeltnet/nuget-push@v1 diff --git a/Codebelt.Bootstrapper.sln b/Codebelt.Bootstrapper.sln index 9922c94..e4684f1 100644 --- a/Codebelt.Bootstrapper.sln +++ b/Codebelt.Bootstrapper.sln @@ -39,6 +39,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codebelt.Bootstrapper.Minim EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codebelt.Bootstrapper.MinimalWorker.App", "app\Codebelt.Bootstrapper.MinimalWorker.App\Codebelt.Bootstrapper.MinimalWorker.App.csproj", "{E87D127B-4CD1-4009-962E-EEF70A522C13}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codebelt.Bootstrapper.Tests", "test\Codebelt.Bootstrapper.Tests\Codebelt.Bootstrapper.Tests.csproj", "{CC656409-7479-4553-8E9B-0854801E1590}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codebelt.Bootstrapper.FunctionalTests", "test\Codebelt.Bootstrapper.FunctionalTests\Codebelt.Bootstrapper.FunctionalTests.csproj", "{8970491F-E0BD-489D-AA7E-617A67C2E39F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -109,6 +115,14 @@ Global {E87D127B-4CD1-4009-962E-EEF70A522C13}.Debug|Any CPU.Build.0 = Debug|Any CPU {E87D127B-4CD1-4009-962E-EEF70A522C13}.Release|Any CPU.ActiveCfg = Release|Any CPU {E87D127B-4CD1-4009-962E-EEF70A522C13}.Release|Any CPU.Build.0 = Release|Any CPU + {CC656409-7479-4553-8E9B-0854801E1590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC656409-7479-4553-8E9B-0854801E1590}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC656409-7479-4553-8E9B-0854801E1590}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC656409-7479-4553-8E9B-0854801E1590}.Release|Any CPU.Build.0 = Release|Any CPU + {8970491F-E0BD-489D-AA7E-617A67C2E39F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8970491F-E0BD-489D-AA7E-617A67C2E39F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8970491F-E0BD-489D-AA7E-617A67C2E39F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8970491F-E0BD-489D-AA7E-617A67C2E39F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -130,6 +144,8 @@ Global {3C43A01F-51E7-4166-91D5-E2088E68F4E5} = {28409021-5670-4008-9061-DBAAA0FC4DA6} {497AD6CD-1E7E-4B4D-ACE5-CDD005A14F96} = {28409021-5670-4008-9061-DBAAA0FC4DA6} {E87D127B-4CD1-4009-962E-EEF70A522C13} = {28409021-5670-4008-9061-DBAAA0FC4DA6} + {CC656409-7479-4553-8E9B-0854801E1590} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {8970491F-E0BD-489D-AA7E-617A67C2E39F} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {72FB037E-3629-4CDB-812E-D577A3D4FD26} diff --git a/Directory.Build.props b/Directory.Build.props index c107324..6e78383 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -73,6 +73,6 @@ - + diff --git a/Directory.Packages.props b/Directory.Packages.props index c60caa3..a26b3ba 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,26 +4,26 @@ - - - - + + + + - + - - - - - + + + + + - - + + diff --git a/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestBackgroundService.cs b/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestBackgroundService.cs new file mode 100644 index 0000000..ceb5176 --- /dev/null +++ b/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestBackgroundService.cs @@ -0,0 +1,30 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Codebelt.Bootstrapper.Assets +{ + public class TestBackgroundService : BackgroundService + { + private readonly ILogger _logger; + + public TestBackgroundService(ILogger logger) + { + _logger = logger; + } + + public TimeSpan Elapsed { get; private set; } = TimeSpan.Zero; + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + var sw = Stopwatch.StartNew(); + await this.WaitForApplicationStartedAnnouncementAsync(stoppingToken).ConfigureAwait(false); + sw.Stop(); + Elapsed = sw.Elapsed; + _logger.LogInformation("TestBackgroundService started after {Elapsed}", Elapsed); + } + } +} diff --git a/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestFixture.cs b/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestFixture.cs new file mode 100644 index 0000000..52f0c27 --- /dev/null +++ b/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestFixture.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Codebelt.Extensions.Xunit.Hosting; +using Codebelt.Extensions.Xunit; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Bootstrapper.Assets +{ + public class TestHostFixture : HostFixture + { + public override void ConfigureHost(Test hostTest) + { + var hb = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((context, config) => + { + ConfigureCallback(config.Build(), context.HostingEnvironment); + }) + .ConfigureServices((context, services) => + { + Configuration = context.Configuration; + HostingEnvironment = context.HostingEnvironment; + ConfigureServicesCallback(services); + }) + .ConfigureHostConfiguration(builder => + { + builder.AddInMemoryCollection(new Dictionary + { + { HostDefaults.ApplicationKey, hostTest.CallerType.Assembly.GetName().Name } + }); + }); + ConfigureHostCallback(hb); + Host = hb.Build(); + } + } +} diff --git a/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestStartup.cs b/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestStartup.cs new file mode 100644 index 0000000..d8461ab --- /dev/null +++ b/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestStartup.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Bootstrapper.Assets +{ + public class TestStartup : StartupRoot + { + public TestStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } + } +} diff --git a/test/Codebelt.Bootstrapper.FunctionalTests/BootstrapperLifetimeTest.cs b/test/Codebelt.Bootstrapper.FunctionalTests/BootstrapperLifetimeTest.cs new file mode 100644 index 0000000..79d35fb --- /dev/null +++ b/test/Codebelt.Bootstrapper.FunctionalTests/BootstrapperLifetimeTest.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading; +using Codebelt.Bootstrapper.Assets; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting; +using Microsoft.Extensions.Hosting; +using Xunit; +using Xunit.Abstractions; + +namespace Codebelt.Bootstrapper +{ + public class BootstrapperLifetimeTest : Test + { + private bool _started = false; + private bool _stopping = false; + private bool _stopped = false; + + public BootstrapperLifetimeTest(ITestOutputHelper output) : base(output) + { + BootstrapperLifetime.OnApplicationStartedCallback = () => { _started = true; }; + BootstrapperLifetime.OnApplicationStoppingCallback = () => { _stopping = true; }; + BootstrapperLifetime.OnApplicationStoppedCallback = () => { _stopped = true; }; + } + + [Fact] + public void OnApplicationStartedCallback_ShouldBeInvokedWhenStartingHost() + { + using var test = GenericHostTestFactory.Create(services => + { + services.AddXunitTestLoggingOutputHelperAccessor(); + services.AddXunitTestLogging(TestOutput); + }, hb => + { + hb.UseBootstrapperLifetime(); + hb.UseBootstrapperStartup(); + }, new TestHostFixture()); + + test.Host.Start(); + + Assert.True(_started); + } + + [Fact] + public void OnApplicationStoppingCallback_OnApplicationStoppedCallback_ShouldBeInvokedWhenStoppingHost() + { + using var test = GenericHostTestFactory.Create(services => + { + services.AddXunitTestLoggingOutputHelperAccessor(); + services.AddXunitTestLogging(TestOutput); + }, hb => + { + hb.UseBootstrapperLifetime(); + hb.UseBootstrapperStartup(); + }, new TestHostFixture()); + + test.Host.Start(); + test.Host.StopAsync().GetAwaiter().GetResult(); + + Assert.True(_stopping && _stopped); + } + } +} diff --git a/test/Codebelt.Bootstrapper.FunctionalTests/Codebelt.Bootstrapper.FunctionalTests.csproj b/test/Codebelt.Bootstrapper.FunctionalTests/Codebelt.Bootstrapper.FunctionalTests.csproj new file mode 100644 index 0000000..5e9152a --- /dev/null +++ b/test/Codebelt.Bootstrapper.FunctionalTests/Codebelt.Bootstrapper.FunctionalTests.csproj @@ -0,0 +1,19 @@ + + + + Codebelt.Bootstrapper + + + + + + <_Parameter1>DisableTestParallelization = true + <_Parameter1_IsLiteral>true + + + + + + + + diff --git a/test/Codebelt.Bootstrapper.FunctionalTests/HostApplicationBuilderExtensionsTest.cs b/test/Codebelt.Bootstrapper.FunctionalTests/HostApplicationBuilderExtensionsTest.cs new file mode 100644 index 0000000..0b7c598 --- /dev/null +++ b/test/Codebelt.Bootstrapper.FunctionalTests/HostApplicationBuilderExtensionsTest.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Assets; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using Xunit.Abstractions; +using static System.Net.Mime.MediaTypeNames; + +namespace Codebelt.Bootstrapper +{ + public class HostApplicationBuilderExtensionsTest : Test + { + public HostApplicationBuilderExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void UseBootstrapperLifetime_ShouldRegisterBootstrapperLifetime() + { + var host = Host.CreateApplicationBuilder() + .UseBootstrapperLifetime() + .Build(); + + var bootstrapperLifetime = host.Services.GetService(); + + Assert.NotNull(bootstrapperLifetime); + Assert.IsType(bootstrapperLifetime); + } + } +} diff --git a/test/Codebelt.Bootstrapper.FunctionalTests/HostBuilderExtensionsTest.cs b/test/Codebelt.Bootstrapper.FunctionalTests/HostBuilderExtensionsTest.cs new file mode 100644 index 0000000..defe9b4 --- /dev/null +++ b/test/Codebelt.Bootstrapper.FunctionalTests/HostBuilderExtensionsTest.cs @@ -0,0 +1,49 @@ +using Codebelt.Bootstrapper.Assets; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using Xunit.Abstractions; + +namespace Codebelt.Bootstrapper +{ + public class HostBuilderExtensionsTest : Test + { + public HostBuilderExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void UseBootstrapperLifetime_ShouldRegisterBootstrapperLifetime() + { + using var test = GenericHostTestFactory.Create(services => + { + }, hb => + { + hb.UseBootstrapperLifetime(); + }, new TestHostFixture()); + + var bootstrapperLifetime = test.ServiceProvider.GetService(); + + Assert.NotNull(bootstrapperLifetime); + Assert.IsType(bootstrapperLifetime); + } + + [Fact] + public void UseBootstrapperStartup_ShouldRegisterStartupFactory() + { + using var test = GenericHostTestFactory.Create(services => + { + }, hb => + { + hb.UseBootstrapperStartup(); + }, new TestHostFixture()); + + var startupFactory = test.ServiceProvider.GetService>(); + + Assert.NotNull(startupFactory); + Assert.IsType>(startupFactory); + } + } +} diff --git a/test/Codebelt.Bootstrapper.FunctionalTests/HostedServiceExtensionsTest.cs b/test/Codebelt.Bootstrapper.FunctionalTests/HostedServiceExtensionsTest.cs new file mode 100644 index 0000000..a830336 --- /dev/null +++ b/test/Codebelt.Bootstrapper.FunctionalTests/HostedServiceExtensionsTest.cs @@ -0,0 +1,49 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Assets; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using Xunit.Abstractions; + +namespace Codebelt.Bootstrapper +{ + public class HostedServiceExtensionsTest : Test + { + public HostedServiceExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task WaitForApplicationStartedAnnouncementAsync_MustWaitForApplicationStarted() + { + var timeToWait = TimeSpan.FromMilliseconds(50); + var started = false; + BootstrapperLifetime.OnApplicationStartedCallback = () => + { + Thread.Sleep(timeToWait); + }; + + using var test = GenericHostTestFactory.Create(services => + { + services.AddXunitTestLoggingOutputHelperAccessor(); + services.AddXunitTestLogging(TestOutput); + services.AddHostedService(); + }, hb => + { + hb.UseBootstrapperLifetime(); + }, new TestHostFixture()); + + var bgs = test.ServiceProvider.GetRequiredService() as TestBackgroundService; + + await test.Host.StartAsync().ConfigureAwait(false); + + await Task.Delay(timeToWait).ConfigureAwait(false); + + Assert.True(bgs.Elapsed >= timeToWait, $"{bgs.Elapsed} >= {timeToWait}"); + } + } +} diff --git a/test/Codebelt.Bootstrapper.Tests/Assets/StartupRootUnsafeAccessor.cs b/test/Codebelt.Bootstrapper.Tests/Assets/StartupRootUnsafeAccessor.cs new file mode 100644 index 0000000..fca1272 --- /dev/null +++ b/test/Codebelt.Bootstrapper.Tests/Assets/StartupRootUnsafeAccessor.cs @@ -0,0 +1,15 @@ +using System.Runtime.CompilerServices; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Bootstrapper.Assets +{ + public static class StartupRootUnsafeAccessor + { + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Configuration")] + public static extern IConfiguration GetConfiguration(StartupRoot startup); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Environment")] + public static extern IHostEnvironment GetEnvironment(StartupRoot startup); + } +} diff --git a/test/Codebelt.Bootstrapper.Tests/Assets/TestStartupRoot.cs b/test/Codebelt.Bootstrapper.Tests/Assets/TestStartupRoot.cs new file mode 100644 index 0000000..34df4c4 --- /dev/null +++ b/test/Codebelt.Bootstrapper.Tests/Assets/TestStartupRoot.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Bootstrapper.Assets +{ + public class TestStartupRoot : StartupRoot + { + public TestStartupRoot(IConfiguration configuration, IHostEnvironment environment) + : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + // Add test services here + services.AddSingleton("TestService"); + } + } +} diff --git a/test/Codebelt.Bootstrapper.Tests/Codebelt.Bootstrapper.Tests.csproj b/test/Codebelt.Bootstrapper.Tests/Codebelt.Bootstrapper.Tests.csproj new file mode 100644 index 0000000..dc95b98 --- /dev/null +++ b/test/Codebelt.Bootstrapper.Tests/Codebelt.Bootstrapper.Tests.csproj @@ -0,0 +1,11 @@ + + + + Codebelt.Bootstrapper + + + + + + + diff --git a/test/Codebelt.Bootstrapper.Tests/StartupRootTest.cs b/test/Codebelt.Bootstrapper.Tests/StartupRootTest.cs new file mode 100644 index 0000000..65d5471 --- /dev/null +++ b/test/Codebelt.Bootstrapper.Tests/StartupRootTest.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.IO; +using Codebelt.Bootstrapper.Assets; +using Codebelt.Extensions.Xunit; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Hosting.Internal; +using Xunit; +using Xunit.Abstractions; + +namespace Codebelt.Bootstrapper +{ + public class StartupRootTest : Test + { + private readonly IConfiguration _configuration; + private readonly IHostEnvironment _environment; + + public StartupRootTest(ITestOutputHelper output) : base(output) + { + var inMemorySettings = new Dictionary { + {"TestKey", "TestValue"} + }; + + _configuration = new ConfigurationBuilder() + .AddInMemoryCollection(inMemorySettings) + .Build(); + + _environment = new HostingEnvironment() + { + EnvironmentName = Environments.Development, + ApplicationName = "TestApp", + ContentRootPath = Directory.GetCurrentDirectory() + }; + } + + [Fact] + public void ConfigurationProperty_ShouldReturnInjectedConfiguration() + { + // Arrange + var startup = new TestStartupRoot(_configuration, _environment); + + // Act + var configuration = StartupRootUnsafeAccessor.GetConfiguration(startup); + + // Assert + Assert.Equal(_configuration, configuration); + } + + + + [Fact] + public void EnvironmentProperty_ShouldReturnInjectedEnvironment() + { + // Arrange + var startup = new TestStartupRoot(_configuration, _environment); + + // Act + var environment = StartupRootUnsafeAccessor.GetEnvironment(startup); + + // Assert + Assert.Equal(_environment, environment); + } + + [Fact] + public void ConfigureServices_ShouldAddServicesToServiceCollection() + { + // Arrange + var services = new ServiceCollection(); + var startup = new TestStartupRoot(_configuration, _environment); + + // Act + startup.ConfigureServices(services); + var serviceProvider = services.BuildServiceProvider(); + var testService = serviceProvider.GetService(); + + // Assert + Assert.Equal("TestService", testService); + } + } +} diff --git a/testenvironments.json b/testenvironments.json new file mode 100644 index 0000000..328e82b --- /dev/null +++ b/testenvironments.json @@ -0,0 +1,15 @@ +{ + "version": "1", + "environments": [ + { + "name": "WSL-Ubuntu", + "type": "wsl", + "wslDistribution": "Ubuntu-24.04" + }, + { + "name": "Docker-Ubuntu", + "type": "docker", + "dockerImage": "gimlichael/ubuntu-testrunner:net8.0.407-9.0.202" + } + ] +}