// ------------------------------------------------------------ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. // ------------------------------------------------------------ using System; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Xunit; using Xunit.Sdk; namespace Dapr.Actors.AspNetCore.IntegrationTest { public class HostingTests { [Fact] public void MapActorsHandlers_WithoutAddActors_Throws() { var exception = Assert.Throws(() => { // Initializes web pipeline which will trigger the exception we throw. // // NOTE: in 3.1 TestServer.CreateClient triggers the failure, in 5.0 it's Host.Start using var host = CreateHost(); var server = host.GetTestServer(); var client = server.CreateClient(); }); Assert.Equal( "The ActorRuntime service is not registered with the dependency injection container. " + "Call AddActors() inside ConfigureServices() to register the actor runtime and actor types.", exception.Message); } [Fact] public async Task MapActorsHandlers_IncludesHealthChecks() { using var factory = new AppWebApplicationFactory(); var httpClient = factory.CreateClient(); var response = await httpClient.GetAsync("/healthz"); await Assert2XXStatusAsync(response); } // We add our own health check on /healthz with worse priority than one // that would be added by a user. Make sure this works and the if the user // adds their own health check it will win. [Fact] public async Task MapActorsHandlers_ActorHealthCheckDoesNotConflict() { using var host = CreateHost(); var server = host.GetTestServer(); var httpClient = server.CreateClient(); var response = await httpClient.GetAsync("/healthz"); await Assert2XXStatusAsync(response); var text = await response.Content.ReadAsStringAsync(); Assert.Equal("Ice Cold, Solid Gold!", text); } // Regression test for #434 [Fact] public async Task MapActorsHandlers_WorksWithFallbackRoute() { using var host = CreateHost(); var server = host.GetTestServer(); var httpClient = server.CreateClient(); var response = await httpClient.GetAsync("/dapr/config"); await Assert2XXStatusAsync(response); } private static IHost CreateHost() where TStartup : class { var builder = Host .CreateDefaultBuilder() .ConfigureLogging(b => { // shhhh b.SetMinimumLevel(LogLevel.None); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); webBuilder.UseTestServer(); }); var host = builder.Build(); try { host.Start(); } catch { host.Dispose(); throw; } return host; } private class BadStartup { public void ConfigureServices(IServiceCollection services) { // no call to AddActors here. That's bad! services.AddRouting(); services.AddHealthChecks(); } public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapActorsHandlers(); }); } } private class FallbackRouteStartup { public void ConfigureServices(IServiceCollection services) { services.AddActors(default); } public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseEndpoints(endpoints => { // This routing feature registers a "route of last resort" which is what // was tripping out Actors prior to changing how they are registered. endpoints.MapFallback(context => { throw new InvalidTimeZoneException("This should not be called!"); }); endpoints.MapActorsHandlers(); }); } } private class HealthCheckStartup { public void ConfigureServices(IServiceCollection services) { services.AddActors(default); } public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapHealthChecks("/healthz", new HealthCheckOptions() { // Write something different so we know this one is called. ResponseWriter = async (httpContext, report) => { await httpContext.Response.WriteAsync( report.Status == HealthStatus.Healthy ? "Ice Cold, Solid Gold!" : "Oh Noes!"); }, }); endpoints.MapActorsHandlers(); }); } } private async Task Assert2XXStatusAsync(HttpResponseMessage response) { if (response.IsSuccessStatusCode) { return; } if (response.Content == null) { throw new XunitException($"The response failed with a {response.StatusCode} and no body."); } // We assume a textual response. #YOLO var text = await response.Content.ReadAsStringAsync(); throw new XunitException($"The response failed with a {response.StatusCode} and body:" + Environment.NewLine + text); } } }