* Avoid null endpoint DTO metadata in tests * Enforce console logs hub read permission * Remove unused console logs hub import * Support mapped endpoint metadata in auth tests * Reduce console log capture throughput impact * Address Copilot console logs review * Refactor task scheduling to support tenant-level background work and enhance logging functionality. * Introduce ConsoleStreamHook for stdout/stderr tee and enhance logging validation. Adjust test cases and startup warnings for distributed lock provider usage. * Refactor console logging pipeline with capture optimization and new ConsoleLogsHost; update tests accordingly. * Add Ansi SGR parser for console logs and associated unit tests * Remove ANSI color renderings and parsers; integrate ConsoleLogScopeAccessor for improved logging context with workflow instance ID support. * Address console logs code quality feedback * Address PR review feedback * Preserve console logs extension points * Stabilize console logs host lifecycle * Address final automated review comments * Tighten console log capture shutdown * Address console log review feedback * Address follow-up review feedback * Cover final review feedback * Avoid recursive console provider initialization * Guard console host lease shutdown * Preserve console log scope and provider lifetime * Correlate console log scope fallback * Tighten console scope correlation * Expose host services during provider construction * Redact ANSI-normalized console lines * Remove `ConsoleCaptureTee` and related services and tests * Use pipeline contributors for console log context * Update CShells package versions to 0.0.24-preview.132 * Filter live console logs by workflow instance * Enhance console logging with activity execution metadata and extend test coverage. * Address console logs stream consumption comment
163 lines
8 KiB
C#
163 lines
8 KiB
C#
using ConsoleLogStreaming.Core;
|
|
using ConsoleLogStreaming.Core.Capture;
|
|
using ConsoleLogStreaming.Core.Hosting;
|
|
using ConsoleLogStreaming.Core.Providers;
|
|
using Elsa.Diagnostics.ConsoleLogs.Contracts;
|
|
using Elsa.Diagnostics.ConsoleLogs.Extensions;
|
|
using Elsa.Diagnostics.ConsoleLogs.Services;
|
|
using Elsa.Workflows;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace Elsa.Diagnostics.ConsoleLogs.UnitTests;
|
|
|
|
[Collection(ConsoleHostStateCollection.Name)]
|
|
public class ConsoleLogsRegistrationTests : IAsyncLifetime
|
|
{
|
|
public async Task InitializeAsync()
|
|
{
|
|
await ConsoleLogStreamingHost.ShutdownAsync();
|
|
ConsoleStreamHook.Uninstall();
|
|
}
|
|
|
|
public async Task DisposeAsync()
|
|
{
|
|
await ConsoleLogStreamingHost.ShutdownAsync();
|
|
ConsoleStreamHook.Uninstall();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddConsoleLogsServices_ResolvesHostOwnedSingletons()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddConsoleLogsServices();
|
|
|
|
await using var serviceProvider = services.BuildServiceProvider();
|
|
|
|
Assert.IsType<InMemoryConsoleLogProvider>(serviceProvider.GetRequiredService<IConsoleLogProvider>());
|
|
var contextAccessor = serviceProvider.GetRequiredService<ConsoleLogContextAccessor>();
|
|
Assert.Same(ConsoleLogStreamingHost.SourceRegistry, serviceProvider.GetRequiredService<IConsoleLogSourceRegistry>());
|
|
Assert.Same(ConsoleLogStreamingHost.RedactionPipeline, serviceProvider.GetRequiredService<IConsoleLogRedactionPipeline>());
|
|
Assert.Same(contextAccessor, serviceProvider.GetRequiredService<IConsoleLogContextAccessor>());
|
|
Assert.Same(contextAccessor, serviceProvider.GetRequiredService<IConsoleLogMetadataAccessor>());
|
|
AssertConsoleLogPipelineContributors(serviceProvider);
|
|
Assert.Contains(serviceProvider.GetServices<IHostedService>(), x => x.GetType() == typeof(ConsoleLogStreamingHostedService));
|
|
Assert.Same(ConsoleLogStreamingHost.Capture, serviceProvider.GetRequiredService<IConsoleLogCapture>());
|
|
|
|
var services2 = new ServiceCollection();
|
|
services2.AddConsoleLogsServices();
|
|
await using var serviceProvider2 = services2.BuildServiceProvider();
|
|
|
|
Assert.Same(serviceProvider.GetRequiredService<IConsoleLogProvider>(), serviceProvider2.GetRequiredService<IConsoleLogProvider>());
|
|
Assert.Same(serviceProvider.GetRequiredService<IConsoleLogSourceRegistry>(), serviceProvider2.GetRequiredService<IConsoleLogSourceRegistry>());
|
|
Assert.Same(serviceProvider.GetRequiredService<ConsoleLogContextAccessor>(), serviceProvider2.GetRequiredService<ConsoleLogContextAccessor>());
|
|
Assert.Same(contextAccessor, ConsoleLogStreamingHost.MetadataAccessor);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddConsoleLogsServices_RegistersHostOwnedDependencies()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddConsoleLogsServices();
|
|
|
|
await using var serviceProvider = services.BuildServiceProvider();
|
|
|
|
foreach (var hostedService in serviceProvider.GetServices<IHostedService>())
|
|
await hostedService.StartAsync(CancellationToken.None);
|
|
|
|
var contextAccessor = serviceProvider.GetRequiredService<ConsoleLogContextAccessor>();
|
|
Assert.Same(ConsoleLogStreamingHost.Provider, serviceProvider.GetRequiredService<IConsoleLogProvider>());
|
|
Assert.Same(ConsoleLogStreamingHost.SourceRegistry, serviceProvider.GetRequiredService<IConsoleLogSourceRegistry>());
|
|
Assert.Same(ConsoleLogStreamingHost.RedactionPipeline, serviceProvider.GetRequiredService<IConsoleLogRedactionPipeline>());
|
|
Assert.Same(ConsoleLogStreamingHost.Formatter, serviceProvider.GetRequiredService<ConsoleLineFormatter>());
|
|
Assert.Same(contextAccessor, serviceProvider.GetRequiredService<IConsoleLogContextAccessor>());
|
|
Assert.Same(contextAccessor, serviceProvider.GetRequiredService<IConsoleLogMetadataAccessor>());
|
|
AssertConsoleLogPipelineContributors(serviceProvider);
|
|
|
|
foreach (var hostedService in serviceProvider.GetServices<IHostedService>())
|
|
await hostedService.StopAsync(CancellationToken.None);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ShutdownAsync_ResetsReferenceLeases()
|
|
{
|
|
ConsoleLogStreamingHost.AddReference();
|
|
ConsoleLogStreamingHost.EnsureInitialized();
|
|
|
|
await ConsoleLogStreamingHost.ShutdownAsync();
|
|
|
|
ConsoleLogStreamingHost.AddReference();
|
|
ConsoleLogStreamingHost.EnsureInitialized();
|
|
await ConsoleLogStreamingHost.ReleaseReferenceAsync();
|
|
|
|
var provider = new CustomProvider();
|
|
Assert.True(ConsoleLogStreamingHost.ConfigureProvider(_ => provider));
|
|
Assert.Same(provider, ConsoleLogStreamingHost.Provider);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddConsoleLogsHost_RegistersHostedFlushService()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddConsoleLogsHost();
|
|
|
|
await using var serviceProvider = services.BuildServiceProvider();
|
|
|
|
var contextAccessor = serviceProvider.GetRequiredService<ConsoleLogContextAccessor>();
|
|
Assert.Contains(serviceProvider.GetServices<IHostedService>(), x => x.GetType() == typeof(ConsoleLogStreamingHostedService));
|
|
Assert.Same(contextAccessor, serviceProvider.GetRequiredService<IConsoleLogContextAccessor>());
|
|
Assert.Same(contextAccessor, serviceProvider.GetRequiredService<IConsoleLogMetadataAccessor>());
|
|
AssertConsoleLogPipelineContributors(serviceProvider);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddConsoleLogsHost_AppliesConfigurationWhenCalledBeforeFirstAccess()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddConsoleLogsHost(options => options.RecentCapacity = 17);
|
|
|
|
using var serviceProvider = services.BuildServiceProvider();
|
|
|
|
Assert.Equal(17, serviceProvider.GetRequiredService<IOptions<ConsoleLogStreaming.Core.Options.ConsoleLogOptions>>().Value.RecentCapacity);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddConsoleLogsHost_DoesNotInitializeHostDuringRegistration()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddConsoleLogsHost();
|
|
|
|
Assert.True(ConsoleLogStreamingHost.Configure(options => options.RecentCapacity = 23));
|
|
|
|
using var serviceProvider = services.BuildServiceProvider();
|
|
|
|
Assert.Equal(23, serviceProvider.GetRequiredService<IOptions<ConsoleLogStreaming.Core.Options.ConsoleLogOptions>>().Value.RecentCapacity);
|
|
}
|
|
|
|
private static void AssertConsoleLogPipelineContributors(IServiceProvider serviceProvider)
|
|
{
|
|
Assert.Contains(serviceProvider.GetServices<IWorkflowExecutionPipelineContributor>(), x => x.GetType() == typeof(ConsoleLogWorkflowExecutionPipelineContributor));
|
|
Assert.Contains(serviceProvider.GetServices<IActivityExecutionPipelineContributor>(), x => x.GetType() == typeof(ConsoleLogActivityExecutionPipelineContributor));
|
|
}
|
|
|
|
private sealed class CustomProvider : IConsoleLogProvider
|
|
{
|
|
public ValueTask PublishAsync(ConsoleLogStreaming.Core.Models.ConsoleLogLine line, CancellationToken cancellationToken = default) => ValueTask.CompletedTask;
|
|
|
|
public ValueTask<ConsoleLogStreaming.Core.Models.RecentConsoleLogsResult> GetRecentAsync(ConsoleLogStreaming.Core.Models.ConsoleLogFilter filter, CancellationToken cancellationToken = default) =>
|
|
ValueTask.FromResult(new ConsoleLogStreaming.Core.Models.RecentConsoleLogsResult());
|
|
|
|
public async IAsyncEnumerable<ConsoleLogStreaming.Core.Models.ConsoleLogStreamingItem> SubscribeAsync(
|
|
ConsoleLogStreaming.Core.Models.ConsoleLogFilter filter,
|
|
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
|
{
|
|
await Task.CompletedTask;
|
|
yield break;
|
|
}
|
|
|
|
public ValueTask<IReadOnlyCollection<ConsoleLogStreaming.Core.Models.ConsoleLogSource>> ListSourcesAsync(CancellationToken cancellationToken = default) =>
|
|
ValueTask.FromResult<IReadOnlyCollection<ConsoleLogStreaming.Core.Models.ConsoleLogSource>>([]);
|
|
}
|
|
}
|