From 49d334e877297c85591a18f9cb6814e62920fbd5 Mon Sep 17 00:00:00 2001 From: Jasmin Oster Date: Wed, 25 Oct 2023 15:41:03 +0200 Subject: [PATCH] Add several tests for application startup --- .../HostApplicationBuilderExtensionsTests.cs | 61 +++++++++++++++++++ .../Extensions/GenericHostExtensions.cs | 28 +++++++++ .../HostApplicationBuilderExtensions.cs | 23 ++++--- ...rviceCollectionsHostedServiceExtensions.cs | 2 +- src/Anexia.E5E/Runtime/E5ERuntimeOptions.cs | 5 +- 5 files changed, 109 insertions(+), 10 deletions(-) create mode 100644 src/Anexia.E5E.Tests/Extensions/HostApplicationBuilderExtensionsTests.cs create mode 100644 src/Anexia.E5E/Extensions/GenericHostExtensions.cs diff --git a/src/Anexia.E5E.Tests/Extensions/HostApplicationBuilderExtensionsTests.cs b/src/Anexia.E5E.Tests/Extensions/HostApplicationBuilderExtensionsTests.cs new file mode 100644 index 0000000..9e15561 --- /dev/null +++ b/src/Anexia.E5E.Tests/Extensions/HostApplicationBuilderExtensionsTests.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; +using System.Text.Json; + +using Anexia.E5E.Exceptions; +using Anexia.E5E.Extensions; +using Anexia.E5E.Runtime; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +using Xunit; + +namespace Anexia.E5E.Tests.Extensions; + +public class HostApplicationBuilderExtensionsTests +{ + [Fact] + public void EmptyListOfArgumentsThrowsException() + { + Assert.Throws(() => + Host.CreateDefaultBuilder().ConfigureE5E(Array.Empty()).Build()); + } + + [Fact] + public void IncorrectListOfArgumentsThrowsException() + { + Assert.Throws(() => + Host.CreateDefaultBuilder().ConfigureE5E(new[] { "foo", "bar" }).Build()); + } + + [Fact] + public void MetadataIsReturned() + { + using var _ = Console.Out; + using var sw = new StringWriter(); + Console.SetOut(sw); + + var expected = JsonSerializer.Serialize(new E5ERuntimeMetadata()); + Host.CreateDefaultBuilder().ConfigureE5E(new[] { "metadata" }).Build().RunE5E(); + + Assert.Equal(expected, sw.ToString()); + } + + [Fact] + public void ShouldReadEscapedNullBytes() + { + var host = Host.CreateDefaultBuilder().ConfigureE5E(new[] { "entrypoint", "\\0", "1", "\\0" }).Build(); + var got = host.Services.GetRequiredService(); + var expected = new E5ERuntimeOptions("entrypoint", "\0", "\0", true, false); + + Assert.Equal(expected, got); + } + + [Fact] + public void StopsInvocationWithMissingEntrypoint() + { + var host = Host.CreateDefaultBuilder().ConfigureE5E(new[] { "entrypoint", "\\0", "1", "\\0" }).Build(); + Assert.Throws(() => host.Run()); + } +} diff --git a/src/Anexia.E5E/Extensions/GenericHostExtensions.cs b/src/Anexia.E5E/Extensions/GenericHostExtensions.cs new file mode 100644 index 0000000..6fe9d18 --- /dev/null +++ b/src/Anexia.E5E/Extensions/GenericHostExtensions.cs @@ -0,0 +1,28 @@ +using System.Text.Json; + +using Anexia.E5E.Runtime; +using Anexia.E5E.Serialization; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Anexia.E5E.Extensions; + +public static class GenericHostExtensions +{ + public static void RunE5E(this IHost host) => host.RunE5EAsync().GetAwaiter().GetResult(); + + public static Task RunE5EAsync(this IHost host, CancellationToken cancellationToken = default) + { + var runtime = host.Services.GetRequiredService(); + if (!runtime.WriteMetadataOnStartup) + return host.RunAsync(cancellationToken); + + var lifetime = host.Services.GetRequiredService(); + var metadata = JsonSerializer.Serialize(new E5ERuntimeMetadata(), E5EJsonSerializerOptions.Default); + Console.Out.Write(metadata); + lifetime.StopApplication(); + + return Task.CompletedTask; + } +} diff --git a/src/Anexia.E5E/Extensions/HostApplicationBuilderExtensions.cs b/src/Anexia.E5E/Extensions/HostApplicationBuilderExtensions.cs index 9dd87ee..c0bedb0 100644 --- a/src/Anexia.E5E/Extensions/HostApplicationBuilderExtensions.cs +++ b/src/Anexia.E5E/Extensions/HostApplicationBuilderExtensions.cs @@ -2,11 +2,15 @@ using Anexia.E5E.Abstractions; using Anexia.E5E.Exceptions; +using Anexia.E5E.Functions; using Anexia.E5E.Hosting; using Anexia.E5E.Runtime; +using Anexia.E5E.Serialization; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace Anexia.E5E.Extensions; @@ -23,17 +27,19 @@ public static IHostBuilder ConfigureE5E(this IHostBuilder hb, string[] args) throw new E5EMissingArgumentsException( $"There were no arguments given to the {nameof(ConfigureE5E)} method."); - if (args[0] == "metadata") + hb.ConfigureServices((_, services) => { - var metadata = JsonSerializer.Serialize(new E5ERuntimeMetadata()); - Console.Out.WriteLine(metadata); - Environment.Exit(0); - return hb; - } + services.TryAddEntrypointServiceResolver(); + services.TryAddSingleton(); + }); + + if (args[0] == "metadata") + return ConfigureE5E(hb, E5ERuntimeOptions.WriteMetadata); if (args.Length != 4) throw new E5EMissingArgumentsException($"Expected exactly four arguments given, got {args.Length}"); + var entrypoint = args[0]; var stdoutTerminationSequence = args[1].Replace("\\0", "\0"); var keepAlive = args[2] == "1"; @@ -47,8 +53,9 @@ public static IHostBuilder ConfigureE5E(this IHostBuilder hb, E5ERuntimeOptions { hb.ConfigureServices((_, services) => { - services.AddSingleton(); - services.AddSingleton(options); + services.TryAddEntrypointServiceResolver(); + services.TryAddSingleton(); + services.TryAddSingleton(options); services.AddHostedService(); }); diff --git a/src/Anexia.E5E/Extensions/ServiceCollectionsHostedServiceExtensions.cs b/src/Anexia.E5E/Extensions/ServiceCollectionsHostedServiceExtensions.cs index cb6e56c..317e306 100644 --- a/src/Anexia.E5E/Extensions/ServiceCollectionsHostedServiceExtensions.cs +++ b/src/Anexia.E5E/Extensions/ServiceCollectionsHostedServiceExtensions.cs @@ -38,7 +38,7 @@ public static IServiceCollection AddE5EFunction(this IServiceCollection services return services; } - private static void TryAddEntrypointServiceResolver(this IServiceCollection services) + internal static void TryAddEntrypointServiceResolver(this IServiceCollection services) { services.TryAddScoped(svc => () => { diff --git a/src/Anexia.E5E/Runtime/E5ERuntimeOptions.cs b/src/Anexia.E5E/Runtime/E5ERuntimeOptions.cs index dacb61d..b1217db 100644 --- a/src/Anexia.E5E/Runtime/E5ERuntimeOptions.cs +++ b/src/Anexia.E5E/Runtime/E5ERuntimeOptions.cs @@ -1,4 +1,7 @@ namespace Anexia.E5E.Runtime; public record E5ERuntimeOptions(string Entrypoint, string StdoutTerminationSequence, - string DaemonExecutionTerminationSequence, bool KeepAlive); + string DaemonExecutionTerminationSequence, bool KeepAlive, bool WriteMetadataOnStartup = false) +{ + internal static readonly E5ERuntimeOptions WriteMetadata = new("", "", "", false, true); +}