diff --git a/examples/AwsLambda.Host.Example.Testing/Tests/LambdaHostTest.cs b/examples/AwsLambda.Host.Example.Testing/Tests/LambdaHostTest.cs index 9d736874..bcab7c64 100644 --- a/examples/AwsLambda.Host.Example.Testing/Tests/LambdaHostTest.cs +++ b/examples/AwsLambda.Host.Example.Testing/Tests/LambdaHostTest.cs @@ -28,20 +28,18 @@ public async Task LambdaHost_CanStartWithoutError() [Fact] public async Task LambdaHost_CrashesWithBadConfiguration_ThrowsException() { - await using var factory = new LambdaApplicationFactory().WithWebHostBuilder( - builder => - { - builder.ConfigureServices( - (_, services) => + await using var factory = new LambdaApplicationFactory().WithHostBuilder(builder => + { + builder.ConfigureServices( + (_, services) => + { + services.Configure(options => { - services.Configure(options => - { - options.BootstrapOptions.RuntimeApiEndpoint = "http://localhost:3002"; - }); - } - ); - } - ); + options.BootstrapOptions.RuntimeApiEndpoint = "http://localhost:3002"; + }); + } + ); + }); var client = factory.CreateClient(); // No need to wait for next request - server handles this automatically diff --git a/src/AwsLambda.Host.Testing/LambdaApplicationFactory.cs b/src/AwsLambda.Host.Testing/LambdaApplicationFactory.cs index ae4fca38..440371dc 100644 --- a/src/AwsLambda.Host.Testing/LambdaApplicationFactory.cs +++ b/src/AwsLambda.Host.Testing/LambdaApplicationFactory.cs @@ -8,8 +8,6 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization; using AwsLambda.Host.Builder.Extensions; using AwsLambda.Host.Options; using Microsoft.Extensions.Configuration; @@ -24,10 +22,9 @@ namespace AwsLambda.Host.Testing; /// /// A type in the entry point assembly of the application. /// Typically the Startup or Program classes can be used. -public partial class LambdaApplicationFactory : IDisposable, IAsyncDisposable +public class LambdaApplicationFactory : IDisposable, IAsyncDisposable where TEntryPoint : class { - // private readonly List _clients = []; private readonly List> _derivedFactories = []; private Action _configuration; private bool _disposed; @@ -68,7 +65,7 @@ public partial class LambdaApplicationFactory : IDisposable, IAsync /// /// Gets the of factories created from this factory /// by further customizing the when calling - /// . + /// . /// public IReadOnlyList> Factories => _derivedFactories.AsReadOnly(); @@ -93,7 +90,6 @@ public virtual IServiceProvider Services get { EnsureServer(); - // return _host?.Services ?? _server.Host.Services; return _host!.Services; } } @@ -107,9 +103,6 @@ public virtual async ValueTask DisposeAsync() if (_disposedAsync) return; - // foreach (var client in _clients) - // client.Dispose(); - foreach (var factory in _derivedFactories) await ((IAsyncDisposable)factory).DisposeAsync().ConfigureAwait(false); @@ -155,21 +148,22 @@ public LambdaClient CreateClient() /// An to configure the . /// /// A new . - public LambdaApplicationFactory WithWebHostBuilder( + public LambdaApplicationFactory WithHostBuilder( Action configuration - ) => WithWebHostBuilderCore(configuration); + ) => WithHostBuilderCore(configuration); + + internal virtual LambdaTestServer CreateServer() => new(); - internal virtual LambdaApplicationFactory WithWebHostBuilderCore( + internal virtual LambdaApplicationFactory WithHostBuilderCore( Action configuration ) { var factory = new DelegatedLambdaApplicationFactory( ClientOptions, - // CreateServer, + CreateServer, CreateHost, CreateHostBuilder, GetTestAssemblies, - ConfigureClient, builder => { _configuration(builder); @@ -202,7 +196,7 @@ private void EnsureServer() { { HostDefaults.ApplicationKey, - typeof(TEntryPoint).Assembly.GetName()?.Name ?? string.Empty + typeof(TEntryPoint).Assembly.GetName().Name ?? string.Empty }, } ); @@ -237,10 +231,7 @@ private void ConfigureHostBuilder(IHostBuilder hostBuilder) SetContentRoot(hostBuilder); _configuration(hostBuilder); - var serializerOptions = new JsonSerializerOptions(); - var routeManager = new LambdaRuntimeRouteManager(); - - _server = new LambdaTestServer(serializerOptions, routeManager); + _server = CreateServer(); // set Lambda Bootstrap Http Client hostBuilder.ConfigureServices(services => @@ -250,7 +241,7 @@ private void ConfigureHostBuilder(IHostBuilder hostBuilder) services.PostConfigure(options => { if (string.IsNullOrEmpty(options.BootstrapOptions.RuntimeApiEndpoint)) - options.BootstrapOptions.RuntimeApiEndpoint = "localhost:3002"; + options.BootstrapOptions.RuntimeApiEndpoint = "localhost"; }); }); @@ -262,10 +253,7 @@ private void ConfigureHostBuilder(IHostBuilder hostBuilder) private void SetContentRoot(IHostBuilder builder) { - var fromFile = File.Exists("MvcTestingAppManifest.json"); - var contentRoot = fromFile - ? GetContentRootFromFile("MvcTestingAppManifest.json") - : GetContentRootFromAssembly(); + var contentRoot = GetContentRootFromAssembly(); if (contentRoot != null) builder.UseContentRoot(contentRoot); @@ -308,22 +296,6 @@ string solutionRelativePath ); } - private static string? GetContentRootFromFile(string file) - { - var data = JsonSerializer.Deserialize( - File.ReadAllBytes(file), - CustomJsonSerializerContext.Default.IDictionaryStringString - )!; - var key = typeof(TEntryPoint).Assembly.GetName().FullName; - - // If the `ContentRoot` is not provided in the app manifest, then return null - // and fallback to setting the content root relative to the entrypoint's assembly. - if (!data.TryGetValue(key, out var contentRoot)) - return null; - - return contentRoot == "~" ? AppContext.BaseDirectory : contentRoot; - } - private string? GetContentRootFromAssembly() { var metadataAttributes = GetContentRootMetadataAttributes( @@ -332,9 +304,8 @@ string solutionRelativePath ); string? contentRoot = null; - for (var i = 0; i < metadataAttributes.Length; i++) + foreach (var contentRootAttribute in metadataAttributes) { - var contentRootAttribute = metadataAttributes[i]; var contentRootCandidate = Path.Combine( AppContext.BaseDirectory, contentRootAttribute.ContentRootPath @@ -392,7 +363,7 @@ protected virtual IEnumerable GetTestAssemblies() var context = DependencyContext.Default; if (context == null || context.CompileLibraries.Count == 0) // The app domain friendly name will be populated in full framework. - return new[] { Assembly.Load(AppDomain.CurrentDomain.FriendlyName) }; + return [Assembly.Load(AppDomain.CurrentDomain.FriendlyName)]; var runtimeProjectLibraries = context.RuntimeLibraries.ToDictionary( r => r.Name, @@ -422,9 +393,12 @@ protected virtual IEnumerable GetTestAssemblies() return testAssemblies; } - catch (Exception) { } + catch (Exception) + { + // Ignore + } - return Array.Empty(); + return []; } private static void EnsureDepsFile() @@ -479,17 +453,6 @@ protected virtual IHost CreateHost(IHostBuilder builder) /// The for the application. protected virtual void ConfigureWebHost(IHostBuilder builder) { } - /// - /// Configures instances created by this . - /// - /// The instance getting configured. - protected virtual void ConfigureClient(HttpClient client) - { - ArgumentNullException.ThrowIfNull(client); - - client.BaseAddress = new Uri("http://localhost"); - } - /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -502,64 +465,55 @@ protected virtual void Dispose(bool disposing) if (_disposed) return; - if (disposing) - if (!_disposedAsync) - DisposeAsync().AsTask().ConfigureAwait(false).GetAwaiter().GetResult(); + if (disposing && !_disposedAsync) + DisposeAsync().AsTask().ConfigureAwait(false).GetAwaiter().GetResult(); _disposed = true; } - [JsonSerializable(typeof(IDictionary))] - private sealed partial class CustomJsonSerializerContext : JsonSerializerContext; - private sealed class DelegatedLambdaApplicationFactory : LambdaApplicationFactory { - private readonly Action _configureClient; - - // private readonly Func _createServer; private readonly Func _createHost; private readonly Func _createHostBuilder; + private readonly Func _createServer; private readonly Func> _getTestAssemblies; public DelegatedLambdaApplicationFactory( LambdaApplicationFactoryClientOptions options, - // Func createServer, + Func createServer, Func createHost, Func createHostBuilder, Func> getTestAssemblies, - Action configureClient, Action configureWebHost ) { ClientOptions = options; - // _createServer = createServer; + _createServer = createServer; _createHost = createHost; _createHostBuilder = createHostBuilder; _getTestAssemblies = getTestAssemblies; - _configureClient = configureClient; _configuration = configureWebHost; } protected override IHost CreateHost(IHostBuilder builder) => _createHost(builder); + internal override LambdaTestServer CreateServer() => _createServer(); + protected override IHostBuilder? CreateHostBuilder() => _createHostBuilder(); protected override IEnumerable GetTestAssemblies() => _getTestAssemblies(); protected override void ConfigureWebHost(IHostBuilder builder) => _configuration(builder); - protected override void ConfigureClient(HttpClient client) => _configureClient(client); - - internal override LambdaApplicationFactory WithWebHostBuilderCore( + internal override LambdaApplicationFactory WithHostBuilderCore( Action configuration ) => new DelegatedLambdaApplicationFactory( ClientOptions, - // _createServer, + _createServer, _createHost, _createHostBuilder, _getTestAssemblies, - _configureClient, builder => { _configuration(builder);