elsa-core/test/unit/Elsa.Diagnostics.ConsoleLogs.UnitTests/Capture/ConsoleCaptureTeeTests.cs
Sipke Schoorstra 0d305d276e
[codex] Enforce console logs hub read permission (#7533)
* 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
2026-05-23 15:57:27 +02:00

174 lines
6.4 KiB
C#

using Elsa.Diagnostics.ConsoleLogs.Contracts;
using Elsa.Diagnostics.ConsoleLogs.Services;
namespace Elsa.Diagnostics.ConsoleLogs.UnitTests.Capture;
public class ConsoleCaptureTeeTests
{
[Fact]
public async Task StartAsync_PreservesOriginalConsoleOutputAndPublishesLine()
{
var originalOut = Console.Out;
var originalError = Console.Error;
using var consoleOutput = new StringWriter();
var provider = new CapturingProvider();
var registry = new ConsoleLogSourceRegistry(Microsoft.Extensions.Options.Options.Create(new ConsoleLogsOptions()));
var options = Microsoft.Extensions.Options.Options.Create(new ConsoleLogsOptions());
var capture = new ConsoleCaptureTee(provider, registry, new ConsoleLogRedactor(options), new ConsoleLineFormatter(options), options);
try
{
Console.SetOut(consoleOutput);
await capture.StartAsync();
Console.WriteLine("hello");
await WaitForLineAsync(provider);
Assert.Equal($"hello{Environment.NewLine}", consoleOutput.ToString());
Assert.Single(provider.Lines);
Assert.Equal("hello", provider.Lines[0].Text);
Assert.Equal(ConsoleLogStream.Stdout, provider.Lines[0].Stream);
}
finally
{
await capture.StopAsync();
Console.SetOut(originalOut);
Console.SetError(originalError);
}
}
[Fact]
public async Task StartAsync_WhenAlreadyStarted_DoesNotWrapConsoleTwice()
{
var originalOut = Console.Out;
var originalError = Console.Error;
using var consoleOutput = new StringWriter();
var provider = new CapturingProvider();
var registry = new ConsoleLogSourceRegistry(Microsoft.Extensions.Options.Options.Create(new ConsoleLogsOptions()));
var options = Microsoft.Extensions.Options.Options.Create(new ConsoleLogsOptions());
var capture = new ConsoleCaptureTee(provider, registry, new ConsoleLogRedactor(options), new ConsoleLineFormatter(options), options);
try
{
Console.SetOut(consoleOutput);
await capture.StartAsync();
await capture.StartAsync();
Console.WriteLine("hello");
await WaitForLineAsync(provider);
Assert.Equal($"hello{Environment.NewLine}", consoleOutput.ToString());
Assert.Single(provider.Lines);
}
finally
{
await capture.StopAsync();
await capture.StopAsync();
Console.SetOut(originalOut);
Console.SetError(originalError);
}
}
[Fact]
public async Task StartAsync_RedactsBeforePublishingToProvider()
{
var originalOut = Console.Out;
var originalError = Console.Error;
using var consoleOutput = new StringWriter();
var provider = new CapturingProvider();
var registry = new ConsoleLogSourceRegistry(Microsoft.Extensions.Options.Options.Create(new ConsoleLogsOptions()));
var options = Microsoft.Extensions.Options.Options.Create(new ConsoleLogsOptions());
var capture = new ConsoleCaptureTee(provider, registry, new ConsoleLogRedactor(options), new ConsoleLineFormatter(options), options);
try
{
Console.SetOut(consoleOutput);
await capture.StartAsync();
Console.WriteLine("token=secret-value");
await WaitForLineAsync(provider);
Assert.Equal("[Redacted]", provider.Lines[0].Text);
}
finally
{
await capture.StopAsync();
Console.SetOut(originalOut);
Console.SetError(originalError);
}
}
[Fact]
public async Task StopAsync_WhenStartedTwice_KeepsCaptureActiveUntilSecondStop()
{
var originalOut = Console.Out;
var originalError = Console.Error;
using var consoleOutput = new StringWriter();
var provider = new CapturingProvider();
var registry = new ConsoleLogSourceRegistry(Microsoft.Extensions.Options.Options.Create(new ConsoleLogsOptions()));
var options = Microsoft.Extensions.Options.Options.Create(new ConsoleLogsOptions());
var capture = new ConsoleCaptureTee(provider, registry, new ConsoleLogRedactor(options), new ConsoleLineFormatter(options), options);
try
{
Console.SetOut(consoleOutput);
await capture.StartAsync();
await capture.StartAsync();
await capture.StopAsync();
Console.WriteLine("still captured");
await WaitForLineAsync(provider);
Assert.Single(provider.Lines);
Assert.Equal("still captured", provider.Lines[0].Text);
await capture.StopAsync();
Console.WriteLine("not captured");
await Task.Delay(50);
Assert.Single(provider.Lines);
}
finally
{
await capture.StopAsync();
Console.SetOut(originalOut);
Console.SetError(originalError);
}
}
private static async Task WaitForLineAsync(CapturingProvider provider)
{
var timeout = DateTimeOffset.UtcNow.AddSeconds(2);
while (provider.Lines.Count == 0 && DateTimeOffset.UtcNow < timeout)
await Task.Delay(10);
}
private sealed class CapturingProvider : IConsoleLogProvider
{
public List<ConsoleLogLine> Lines { get; } = [];
public ValueTask PublishAsync(ConsoleLogLine line, CancellationToken cancellationToken = default)
{
Lines.Add(line);
return ValueTask.CompletedTask;
}
public ValueTask<RecentConsoleLogsResult> GetRecentAsync(ConsoleLogFilter filter, CancellationToken cancellationToken = default)
{
return ValueTask.FromResult(new RecentConsoleLogsResult(Lines));
}
public async IAsyncEnumerable<ConsoleLogStreamItem> SubscribeAsync(ConsoleLogFilter filter, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
await Task.CompletedTask;
foreach (var line in Lines)
yield return ConsoleLogStreamItem.FromLine(line);
}
public ValueTask<IReadOnlyCollection<ConsoleLogSource>> ListSourcesAsync(CancellationToken cancellationToken = default)
{
return ValueTask.FromResult<IReadOnlyCollection<ConsoleLogSource>>([]);
}
}
}