CLI entry trace warning capture and sprint status sync
This commit is contained in:
@@ -197,30 +197,29 @@ public sealed class CommandHandlersTests
|
||||
public async Task HandleScanEntryTraceAsync_WarnsWhenResultMissing()
|
||||
{
|
||||
var originalExit = Environment.ExitCode;
|
||||
var console = new TestConsole();
|
||||
var originalConsole = AnsiConsole.Console;
|
||||
|
||||
var backend = new StubBackendClient(new JobTriggerResult(true, "Accepted", null, null));
|
||||
var provider = BuildServiceProvider(backend);
|
||||
AnsiConsole.Console = console;
|
||||
var loggerProvider = new TestLoggerProvider();
|
||||
var provider = BuildServiceProvider(backend, loggerProvider: loggerProvider);
|
||||
|
||||
try
|
||||
{
|
||||
await CommandHandlers.HandleScanEntryTraceAsync(
|
||||
var output = await CaptureTestConsoleAsync(console => CommandHandlers.HandleScanEntryTraceAsync(
|
||||
provider,
|
||||
"scan-missing",
|
||||
includeNdjson: false,
|
||||
verbose: false,
|
||||
cancellationToken: CancellationToken.None);
|
||||
cancellationToken: CancellationToken.None));
|
||||
|
||||
Assert.Equal(1, Environment.ExitCode);
|
||||
Assert.Equal("scan-missing", backend.LastEntryTraceScanId);
|
||||
Assert.Contains("No EntryTrace data", console.Output, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("No EntryTrace data", output.Combined, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var warning = Assert.Single(loggerProvider.Entries.Where(entry => entry.Level == LogLevel.Warning));
|
||||
Assert.Contains("No EntryTrace data", warning.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.ExitCode = originalExit;
|
||||
AnsiConsole.Console = originalConsole;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2543,11 +2542,19 @@ spec:
|
||||
IScannerInstaller? installer = null,
|
||||
StellaOpsCliOptions? options = null,
|
||||
IStellaOpsTokenClient? tokenClient = null,
|
||||
IConcelierObservationsClient? concelierClient = null)
|
||||
IConcelierObservationsClient? concelierClient = null,
|
||||
ILoggerProvider? loggerProvider = null)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton(backend);
|
||||
services.AddSingleton<ILoggerFactory>(_ => LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug)));
|
||||
services.AddSingleton<ILoggerFactory>(_ => LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.SetMinimumLevel(LogLevel.Debug);
|
||||
if (loggerProvider is not null)
|
||||
{
|
||||
builder.AddProvider(loggerProvider);
|
||||
}
|
||||
}));
|
||||
services.AddSingleton(new VerbosityState());
|
||||
services.AddHttpClient();
|
||||
var resolvedOptions = options ?? new StellaOpsCliOptions
|
||||
@@ -2571,6 +2578,70 @@ spec:
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
private static async Task<CapturedConsoleOutput> CaptureTestConsoleAsync(Func<TestConsole, Task> action)
|
||||
{
|
||||
var testConsole = new TestConsole();
|
||||
var originalConsole = AnsiConsole.Console;
|
||||
var originalOut = Console.Out;
|
||||
using var writer = new StringWriter();
|
||||
|
||||
try
|
||||
{
|
||||
AnsiConsole.Console = testConsole;
|
||||
Console.SetOut(writer);
|
||||
|
||||
await action(testConsole).ConfigureAwait(false);
|
||||
return new CapturedConsoleOutput(testConsole.Output.ToString(), writer.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.SetOut(originalOut);
|
||||
AnsiConsole.Console = originalConsole;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record CapturedConsoleOutput(string SpectreBuffer, string PlainBuffer)
|
||||
{
|
||||
public string Combined => string.Concat(SpectreBuffer, PlainBuffer);
|
||||
}
|
||||
|
||||
private sealed class TestLoggerProvider : ILoggerProvider
|
||||
{
|
||||
private readonly List<LogEntry> _entries = new();
|
||||
|
||||
public IReadOnlyList<LogEntry> Entries => _entries;
|
||||
|
||||
public ILogger CreateLogger(string categoryName) => new TestLogger(categoryName, _entries);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
private sealed class TestLogger : ILogger
|
||||
{
|
||||
private readonly string _category;
|
||||
private readonly List<LogEntry> _entries;
|
||||
|
||||
public TestLogger(string category, List<LogEntry> entries)
|
||||
{
|
||||
_category = category;
|
||||
_entries = entries;
|
||||
}
|
||||
|
||||
public IDisposable? BeginScope<TState>(TState state) => null;
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel) => true;
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
var message = formatter(state, exception);
|
||||
_entries.Add(new LogEntry(logLevel, _category, eventId, message, exception));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record LogEntry(LogLevel Level, string Category, EventId EventId, string Message, Exception? Exception);
|
||||
}
|
||||
|
||||
private static IScannerExecutor CreateDefaultExecutor()
|
||||
{
|
||||
var tempResultsFile = Path.GetTempFileName();
|
||||
|
||||
Reference in New Issue
Block a user