diff --git a/.nuget/Codebelt.Bootstrapper.Web/PackageReleaseNotes.txt b/.nuget/Codebelt.Bootstrapper.Web/PackageReleaseNotes.txt index 3c78178..ded1826 100644 --- a/.nuget/Codebelt.Bootstrapper.Web/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Bootstrapper.Web/PackageReleaseNotes.txt @@ -4,6 +4,9 @@ Availability: .NET 9 and .NET 8 # ALM - CHANGED Dependencies to latest and greatest with respect to TFMs   +# Breaking Changes +- REMOVED WebApplicationBuilderExtensions class in the Codebelt.Bootstrapper.Web namespace (UseBootstrapperLifetime extension method) +  Version 3.0.1 Availability: .NET 9 and .NET 8   diff --git a/CHANGELOG.md b/CHANGELOG.md index 958302b..a71d378 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This major release revisits and refines some of the earlier design decisions to ### Removed - HostedServiceExtensions class in the Codebelt.Bootstrapper namespace (breaking change) +- WebApplicationBuilderExtensions class in the Codebelt.Bootstrapper.Web namespace (breaking change) ### Fixed diff --git a/Codebelt.Bootstrapper.sln b/Codebelt.Bootstrapper.sln index 23dde0f..dcd25bf 100644 --- a/Codebelt.Bootstrapper.sln +++ b/Codebelt.Bootstrapper.sln @@ -47,6 +47,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codebelt.Bootstrapper.Funct EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codebelt.Bootstrapper.Console.FunctionalTests", "test\Codebelt.Bootstrapper.Console.FunctionalTests\Codebelt.Bootstrapper.Console.FunctionalTests.csproj", "{585A199C-4675-4A8A-89B7-EFF824377456}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codebelt.Bootstrapper.Worker.FunctionalTests", "test\Codebelt.Bootstrapper.Worker.FunctionalTests\Codebelt.Bootstrapper.Worker.FunctionalTests.csproj", "{42B4B4ED-95E4-4855-852D-8832E97202CD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codebelt.Bootstrapper.Web.FunctionalTests", "test\Codebelt.Bootstrapper.Web.FunctionalTests\Codebelt.Bootstrapper.Web.FunctionalTests.csproj", "{BD1FF180-06BC-4A5F-A746-A30648FEB509}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -129,6 +133,14 @@ Global {585A199C-4675-4A8A-89B7-EFF824377456}.Debug|Any CPU.Build.0 = Debug|Any CPU {585A199C-4675-4A8A-89B7-EFF824377456}.Release|Any CPU.ActiveCfg = Release|Any CPU {585A199C-4675-4A8A-89B7-EFF824377456}.Release|Any CPU.Build.0 = Release|Any CPU + {42B4B4ED-95E4-4855-852D-8832E97202CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42B4B4ED-95E4-4855-852D-8832E97202CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42B4B4ED-95E4-4855-852D-8832E97202CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42B4B4ED-95E4-4855-852D-8832E97202CD}.Release|Any CPU.Build.0 = Release|Any CPU + {BD1FF180-06BC-4A5F-A746-A30648FEB509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD1FF180-06BC-4A5F-A746-A30648FEB509}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD1FF180-06BC-4A5F-A746-A30648FEB509}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD1FF180-06BC-4A5F-A746-A30648FEB509}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -153,6 +165,8 @@ Global {CC656409-7479-4553-8E9B-0854801E1590} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {8970491F-E0BD-489D-AA7E-617A67C2E39F} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {585A199C-4675-4A8A-89B7-EFF824377456} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {42B4B4ED-95E4-4855-852D-8832E97202CD} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {BD1FF180-06BC-4A5F-A746-A30648FEB509} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {72FB037E-3629-4CDB-812E-D577A3D4FD26} diff --git a/Directory.Packages.props b/Directory.Packages.props index 6de61c3..3839aae 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,7 @@ - + diff --git a/src/Codebelt.Bootstrapper.Web/MinimalWebProgram.cs b/src/Codebelt.Bootstrapper.Web/MinimalWebProgram.cs index ae550d7..156b897 100644 --- a/src/Codebelt.Bootstrapper.Web/MinimalWebProgram.cs +++ b/src/Codebelt.Bootstrapper.Web/MinimalWebProgram.cs @@ -15,8 +15,9 @@ public abstract class MinimalWebProgram : ProgramRoot /// The initialized . protected static WebApplicationBuilder CreateHostBuilder(string[] args) { - return WebApplication.CreateBuilder(args) - .UseBootstrapperLifetime(); + var hb = WebApplication.CreateBuilder(args); + hb.UseBootstrapperLifetime(); + return hb; } } } diff --git a/src/Codebelt.Bootstrapper.Web/WebApplicationBuilderExtensions.cs b/src/Codebelt.Bootstrapper.Web/WebApplicationBuilderExtensions.cs deleted file mode 100644 index 5586ed1..0000000 --- a/src/Codebelt.Bootstrapper.Web/WebApplicationBuilderExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Hosting.Internal; - -namespace Codebelt.Bootstrapper.Web -{ - /// - /// Extension methods for the . - /// - public static class WebApplicationBuilderExtensions - { - /// - /// Listens for Ctrl+C or SIGTERM and calls to start the shutdown process. - /// - /// The to configure. - /// The same instance of the for chaining. - /// Complements the default implementation of . - public static WebApplicationBuilder UseBootstrapperLifetime(this WebApplicationBuilder hostBuilder) - { - hostBuilder.Services.Replace(ServiceDescriptor.Singleton()); - return hostBuilder; - } - } -} diff --git a/test/Codebelt.Bootstrapper.Console.FunctionalTests/Assets/ManualGenericHostFixture.cs b/test/Codebelt.Bootstrapper.Console.FunctionalTests/Assets/ManualGenericHostFixture.cs deleted file mode 100644 index 3ff99f5..0000000 --- a/test/Codebelt.Bootstrapper.Console.FunctionalTests/Assets/ManualGenericHostFixture.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Codebelt.Extensions.Xunit.Hosting; - -namespace Codebelt.Bootstrapper.Console.Assets -{ - public class ManualGenericHostFixture : GenericHostFixture - { - public ManualGenericHostFixture() - { - HostRunnerCallback = host => { }; - } - } -} diff --git a/test/Codebelt.Bootstrapper.Console.FunctionalTests/Assets/ManualMinimalHostFixture.cs b/test/Codebelt.Bootstrapper.Console.FunctionalTests/Assets/ManualMinimalHostFixture.cs deleted file mode 100644 index 6bcb555..0000000 --- a/test/Codebelt.Bootstrapper.Console.FunctionalTests/Assets/ManualMinimalHostFixture.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Codebelt.Extensions.Xunit.Hosting; - -namespace Codebelt.Bootstrapper.Console.Assets -{ - public class ManualMinimalHostFixture : MinimalHostFixture - { - public ManualMinimalHostFixture() - { - HostRunnerCallback = host => { }; - } - } -} diff --git a/test/Codebelt.Bootstrapper.Console.FunctionalTests/Assets/TestHostFixture.cs b/test/Codebelt.Bootstrapper.Console.FunctionalTests/Assets/TestHostFixture.cs index 6dac83b..e7571fb 100644 --- a/test/Codebelt.Bootstrapper.Console.FunctionalTests/Assets/TestHostFixture.cs +++ b/test/Codebelt.Bootstrapper.Console.FunctionalTests/Assets/TestHostFixture.cs @@ -6,7 +6,7 @@ namespace Codebelt.Bootstrapper.Console.Assets { - public class TestHostFixture : GenericHostFixture + public class TestHostFixture : ManagedHostFixture { public override void ConfigureHost(Test hostTest) { diff --git a/test/Codebelt.Bootstrapper.Console.FunctionalTests/LoggerExtensions.cs b/test/Codebelt.Bootstrapper.Console.FunctionalTests/LoggerExtensions.cs deleted file mode 100644 index a1295f9..0000000 --- a/test/Codebelt.Bootstrapper.Console.FunctionalTests/LoggerExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Codebelt.Extensions.Xunit; -using Codebelt.Extensions.Xunit.Hosting; -using Microsoft.Extensions.Logging; - -namespace Codebelt.Bootstrapper.Console -{ - public static class LoggerExtensions - { - /// - /// Returns the associated that is provided when settings up services from . - /// - /// The from which to retrieve the . - /// Returns an implementation of with all logged entries expressed as . - /// - /// cannot be null. - /// - /// - /// does not contain a test store. - /// - public static ITestStore GetTestStore(this ILogger logger, Type loggerType) - { - return logger.GetTestStore(loggerType.FullName); - } - } -} diff --git a/test/Codebelt.Bootstrapper.Console.FunctionalTests/MinimalConsoleHostedServiceTest.cs b/test/Codebelt.Bootstrapper.Console.FunctionalTests/MinimalConsoleHostedServiceTest.cs index 456b5c7..6386ea1 100644 --- a/test/Codebelt.Bootstrapper.Console.FunctionalTests/MinimalConsoleHostedServiceTest.cs +++ b/test/Codebelt.Bootstrapper.Console.FunctionalTests/MinimalConsoleHostedServiceTest.cs @@ -1,6 +1,4 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; +using System.Threading.Tasks; using Codebelt.Bootstrapper.Console.Assets; using Codebelt.Extensions.Xunit; using Codebelt.Extensions.Xunit.Hosting; @@ -16,17 +14,6 @@ public class MinimalConsoleHostedServiceTest : Test { public MinimalConsoleHostedServiceTest(ITestOutputHelper output) : base(output) { - AppDomain.CurrentDomain.UnhandledException += (sender, e) => - { - var exception = e.ExceptionObject as Exception; - Debug.WriteLine($"Unhandled exception: {exception?.Message}"); - }; - - TaskScheduler.UnobservedTaskException += (sender, e) => - { - Debug.WriteLine($"Unobserved task exception: {e.Exception.Message}"); - e.SetObserved(); - }; } [Fact] @@ -42,30 +29,6 @@ public async Task StartAsync_ShouldInvokeRunAsyncInTestConsoleStartup() .UseMinimalConsoleProgram(); }); - //var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5)); - - //if (SynchronizationContext.Current == null) - //{ - // // normal ASP.Net Core environment does not have a synchronization context, - // // no problem with await here, it will be executed on the thread pool - // await test.Host.StartAsync(); - // await test.Host.WaitForShutdownAsync(); - //} - //else - //{ - // // xunit uses it's own SynchronizationContext that allows a maximum thread count - // // equal to the logical cpu count (that is 1 on our single cpu build agents). So - // // when we're trying to await something here, the task get's scheduled to xunit's - // // synchronization context, which is already at it's limit running the test thread - // // so we end up in a deadlock here. - // // solution is to run the await explicitly on the thread pool by using Task.Run - // Task.Run(async () => - // { - // await test.Host.StartAsync(); - // await test.Host.WaitForShutdownAsync(); - // }).Wait(); - //} - await test.Host.WaitForShutdownAsync(); var loggerStore = test.Host.Services.GetRequiredService>().GetTestStore(); diff --git a/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestHostFixture.cs b/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestHostFixture.cs index 82901e6..55750dc 100644 --- a/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestHostFixture.cs +++ b/test/Codebelt.Bootstrapper.FunctionalTests/Assets/TestHostFixture.cs @@ -6,7 +6,7 @@ namespace Codebelt.Bootstrapper.Assets { - public class TestHostFixture : GenericHostFixture + public class TestHostFixture : ManagedHostFixture { public override void ConfigureHost(Test hostTest) { diff --git a/test/Codebelt.Bootstrapper.Web.FunctionalTests/Assets/TestStartup.cs b/test/Codebelt.Bootstrapper.Web.FunctionalTests/Assets/TestStartup.cs new file mode 100644 index 0000000..ca5d316 --- /dev/null +++ b/test/Codebelt.Bootstrapper.Web.FunctionalTests/Assets/TestStartup.cs @@ -0,0 +1,38 @@ +using Cuemon.Extensions.Hosting; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Bootstrapper.Web.Assets +{ + public class TestStartup : WebStartup + { + public TestStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } + + public override void ConfigurePipeline(IApplicationBuilder app) + { + if (Environment.IsLocalDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", async context => + { + await context.Response.WriteAsync("Hello World!"); + }); + }); + } + } +} diff --git a/test/Codebelt.Bootstrapper.Web.FunctionalTests/Codebelt.Bootstrapper.Web.FunctionalTests.csproj b/test/Codebelt.Bootstrapper.Web.FunctionalTests/Codebelt.Bootstrapper.Web.FunctionalTests.csproj new file mode 100644 index 0000000..3cafe5e --- /dev/null +++ b/test/Codebelt.Bootstrapper.Web.FunctionalTests/Codebelt.Bootstrapper.Web.FunctionalTests.csproj @@ -0,0 +1,11 @@ + + + + Codebelt.Bootstrapper.Web + + + + + + + diff --git a/test/Codebelt.Bootstrapper.Web.FunctionalTests/MinimalWebProgramTest.cs b/test/Codebelt.Bootstrapper.Web.FunctionalTests/MinimalWebProgramTest.cs new file mode 100644 index 0000000..da4633c --- /dev/null +++ b/test/Codebelt.Bootstrapper.Web.FunctionalTests/MinimalWebProgramTest.cs @@ -0,0 +1,51 @@ +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Cuemon.Extensions.Hosting; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Xunit; +using Xunit.Abstractions; + +namespace Codebelt.Bootstrapper.Web +{ + public class MinimalWebProgramTest : Test + { + public MinimalWebProgramTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ExecuteAsync_ShouldSimulateAMinimalWebApplication() + { + using var test = await MinimalWebHostTestFactory.RunWithHostBuilderContextAsync((context, services) => + { + services.AddXunitTestLogging(TestOutput); + }, (context, app) => + { + if (context.HostingEnvironment.IsLocalDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", async context => + { + await context.Response.WriteAsync("Hello World!"); + }); + }); + }, hb => + { + hb.UseBootstrapperLifetime(); + }); + + var helloWorld = await test.Content.ReadAsStringAsync(); + + Assert.Equal("Hello World!", helloWorld); + } + } +} diff --git a/test/Codebelt.Bootstrapper.Web.FunctionalTests/WebStartupTest.cs b/test/Codebelt.Bootstrapper.Web.FunctionalTests/WebStartupTest.cs new file mode 100644 index 0000000..39a59d6 --- /dev/null +++ b/test/Codebelt.Bootstrapper.Web.FunctionalTests/WebStartupTest.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Web.Assets; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Hosting; +using Xunit; +using Xunit.Abstractions; + +namespace Codebelt.Bootstrapper.Web +{ + public class WebStartupTest : Test + { + public WebStartupTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ConfigurePipeline_ShouldSimulateAWebApplication() + { + using var test = await WebHostTestFactory.RunWithHostBuilderContextAsync((context, services) => + { + services.AddXunitTestLogging(TestOutput); + }, hostSetup: hb => + { + hb.UseBootstrapperLifetime() + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseTestServer(o => o.PreserveExecutionContext = true); + webBuilder.UseStartup(); + }); + }); + + var helloWorld = await test.Content.ReadAsStringAsync(); + + Assert.Equal("Hello World!", helloWorld); + } + } +} diff --git a/test/Codebelt.Bootstrapper.Worker.FunctionalTests/Assets/FakeHostedService.cs b/test/Codebelt.Bootstrapper.Worker.FunctionalTests/Assets/FakeHostedService.cs new file mode 100644 index 0000000..c290b89 --- /dev/null +++ b/test/Codebelt.Bootstrapper.Worker.FunctionalTests/Assets/FakeHostedService.cs @@ -0,0 +1,53 @@ +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Codebelt.Bootstrapper.Worker.Assets +{ + public class FakeHostedService : BackgroundService + { + private readonly TaskCompletionSource _tsc = new(); + private readonly ILogger _logger; + private readonly IHostLifetimeEvents _events; + private bool _gracefulShutdown; + + public FakeHostedService(ILogger logger, IHostLifetimeEvents events) + { + _logger = logger; + + events.OnApplicationStartedCallback = () => + { + logger.LogInformation("Started"); + _tsc.SetResult(); + }; + events.OnApplicationStoppingCallback = () => + { + _gracefulShutdown = true; + logger.LogWarning("Stopping and cleaning .."); + Thread.Sleep(TimeSpan.FromMilliseconds(125)); // simulate graceful shutdown + logger.LogWarning(".. done!"); + }; + events.OnApplicationStoppedCallback = () => logger.LogCritical("Stopped"); + + _events = events; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await _tsc.Task.ConfigureAwait(false); + var i = 1; + while (!stoppingToken.IsCancellationRequested) + { + if (_gracefulShutdown) { return; } + _logger.LogInformation("Worker running in iterations: {iteration}", i); + i++; + await Task.Delay(TimeSpan.FromMilliseconds(1000), stoppingToken); + } + } + } +} diff --git a/test/Codebelt.Bootstrapper.Worker.FunctionalTests/Assets/TestStartup.cs b/test/Codebelt.Bootstrapper.Worker.FunctionalTests/Assets/TestStartup.cs new file mode 100644 index 0000000..8ac50cb --- /dev/null +++ b/test/Codebelt.Bootstrapper.Worker.FunctionalTests/Assets/TestStartup.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Bootstrapper.Worker.Assets +{ + public class TestStartup : WorkerStartup + { + public TestStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddHostedService(); + } + } +} diff --git a/test/Codebelt.Bootstrapper.Worker.FunctionalTests/Codebelt.Bootstrapper.Worker.FunctionalTests.csproj b/test/Codebelt.Bootstrapper.Worker.FunctionalTests/Codebelt.Bootstrapper.Worker.FunctionalTests.csproj new file mode 100644 index 0000000..7095a81 --- /dev/null +++ b/test/Codebelt.Bootstrapper.Worker.FunctionalTests/Codebelt.Bootstrapper.Worker.FunctionalTests.csproj @@ -0,0 +1,11 @@ + + + + Codebelt.Bootstrapper.Worker + + + + + + + diff --git a/test/Codebelt.Bootstrapper.Worker.FunctionalTests/MinimalWorkerProgramTest.cs b/test/Codebelt.Bootstrapper.Worker.FunctionalTests/MinimalWorkerProgramTest.cs new file mode 100644 index 0000000..7eea6df --- /dev/null +++ b/test/Codebelt.Bootstrapper.Worker.FunctionalTests/MinimalWorkerProgramTest.cs @@ -0,0 +1,52 @@ +using System; +using System.Threading; +using Codebelt.Extensions.Xunit; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Worker.Assets; +using Codebelt.Extensions.Xunit.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +namespace Codebelt.Bootstrapper.Worker +{ + public class MinimalWorkerProgramTest : Test + { + public MinimalWorkerProgramTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ExecuteAsync_ShouldSimulateAMinimalWorkerService() + { + await using var test = MinimalHostTestFactory.Create(services => + { + services.AddXunitTestLogging(TestOutput); + services.AddHostedService(); + }, hb => + { + hb.UseBootstrapperLifetime(); + }, new SelfManagedMinimalHostFixture()); + + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); + + await test.Host.StartAsync(cts.Token); + await test.Host.WaitForShutdownAsync(cts.Token); + + var loggerStore = test.Host.Services.GetRequiredService>().GetTestStore(); + Assert.Collection(loggerStore.Query(), + entry => Assert.Equal("Information: Started", entry.Message), + entry => Assert.Equal("Information: Worker running in iterations: 1", entry.Message), + entry => Assert.Equal("Information: Worker running in iterations: 2", entry.Message), + entry => Assert.Equal("Information: Worker running in iterations: 3", entry.Message), + entry => Assert.Equal("Information: Worker running in iterations: 4", entry.Message), + entry => Assert.Equal("Information: Worker running in iterations: 5", entry.Message), + entry => Assert.Equal("Warning: Stopping and cleaning ..", entry.Message), + entry => Assert.Equal("Warning: .. done!", entry.Message), + entry => Assert.Equal("Critical: Stopped", entry.Message) + ); + } + } +} diff --git a/test/Codebelt.Bootstrapper.Worker.FunctionalTests/WorkerStartupTest.cs b/test/Codebelt.Bootstrapper.Worker.FunctionalTests/WorkerStartupTest.cs new file mode 100644 index 0000000..edb8c78 --- /dev/null +++ b/test/Codebelt.Bootstrapper.Worker.FunctionalTests/WorkerStartupTest.cs @@ -0,0 +1,52 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Worker.Assets; +using Codebelt.Extensions.Xunit; +using Codebelt.Extensions.Xunit.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +namespace Codebelt.Bootstrapper.Worker +{ + public class WorkerStartupTest : Test + { + public WorkerStartupTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ExecuteAsync_ShouldSimulateAWorkerService() + { + await using var test = HostTestFactory.Create(services => + { + services.AddXunitTestLogging(TestOutput); + }, hb => + { + hb.UseBootstrapperLifetime() + .UseBootstrapperStartup(); + }, new SelfManagedHostFixture()); + + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); + + await test.Host.StartAsync(cts.Token); + await test.Host.WaitForShutdownAsync(cts.Token); + + var loggerStore = test.Host.Services.GetRequiredService>().GetTestStore(); + Assert.Collection(loggerStore.Query(), + entry => Assert.Equal("Information: Started", entry.Message), + entry => Assert.Equal("Information: Worker running in iterations: 1", entry.Message), + entry => Assert.Equal("Information: Worker running in iterations: 2", entry.Message), + entry => Assert.Equal("Information: Worker running in iterations: 3", entry.Message), + entry => Assert.Equal("Information: Worker running in iterations: 4", entry.Message), + entry => Assert.Equal("Information: Worker running in iterations: 5", entry.Message), + entry => Assert.Equal("Warning: Stopping and cleaning ..", entry.Message), + entry => Assert.Equal("Warning: .. done!", entry.Message), + entry => Assert.Equal("Critical: Stopped", entry.Message) + ); + } + } +}