generated from frankhaugen/DotnetRepoTemplate
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Major changes to hosted base tests, upgraded nugets and a new in-memo…
…ry logger to make asserting logging possible
- Loading branch information
1 parent
a82c5f8
commit d28976d
Showing
22 changed files
with
447 additions
and
178 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace Frank.Testing.Logging; | ||
|
||
public class InMemoryLogEntry | ||
{ | ||
/// <summary> | ||
/// Gets the log level of the application. | ||
/// </summary> | ||
/// <value>The log level.</value> | ||
public LogLevel LogLevel { get; } | ||
|
||
/// <summary> | ||
/// Gets the unique identifier of an event. | ||
/// </summary> | ||
public EventId EventId { get; } | ||
|
||
/// <summary> | ||
/// Gets the exception associated with the property. | ||
/// </summary> | ||
/// <value> | ||
/// The exception associated with the property, or null if no exception occurred. | ||
/// </value> | ||
public Exception? Exception { get; } | ||
|
||
/// <summary> | ||
/// Gets the name of the category. | ||
/// </summary> | ||
/// <value>The name of the category.</value> | ||
public string CategoryName { get; } | ||
|
||
/// <summary> | ||
/// Gets the message associated with this property. | ||
/// </summary> | ||
public string Message { get; } | ||
|
||
/// <summary> | ||
/// Gets the state of the object. | ||
/// </summary> | ||
/// <remarks> | ||
/// The state is represented as a collection of key-value pairs, where the key is a string and the value is an object. | ||
/// The state is read-only and can be null if there is no state available. | ||
/// </remarks> | ||
/// <returns>A read-only list of key-value pairs representing the state of the object.</returns> | ||
public IReadOnlyList<KeyValuePair<string, object?>>? State { get; } | ||
|
||
/// <summary> | ||
/// Represents a log pulse, which encapsulates information about a log event. | ||
/// </summary> | ||
/// <param name="logLevel">The level of the log event.</param> | ||
/// <param name="eventId">The identifier of the log event.</param> | ||
/// <param name="exception">The exception associated with the log event, if any.</param> | ||
/// <param name="categoryName">The name of the log category.</param> | ||
/// <param name="message">The log message.</param> | ||
/// <param name="state"></param> | ||
public InMemoryLogEntry(LogLevel logLevel, EventId eventId, Exception? exception, string categoryName, string message, IReadOnlyList<KeyValuePair<string, object?>>? state) | ||
{ | ||
LogLevel = logLevel; | ||
EventId = eventId; | ||
Exception = exception; | ||
CategoryName = categoryName; | ||
Message = message; | ||
State = state; | ||
} | ||
|
||
/// <summary> | ||
/// Returns a string representation of the object. | ||
/// </summary> | ||
/// <returns> | ||
/// A string representing the object. The string consists of the log level, | ||
/// event ID, category name, message, and exception, formatted in the following way: | ||
/// [LogLevel] (EventId) CategoryName: 'Message' | ||
/// Exception | ||
/// </returns> | ||
public override string ToString() => $"[{LogLevel}] ({EventId}) {CategoryName}: '{Message}'\n\t{Exception}"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using Frank.Reflection; | ||
|
||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Frank.Testing.Logging; | ||
|
||
public class InMemoryLogger(IOptions<LoggerFilterOptions> options, string category) : ILogger | ||
{ | ||
private readonly List<InMemoryLogEntry> _logEntries = new(); | ||
|
||
/// <inheritdoc /> | ||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) | ||
=> _logEntries.Add(new InMemoryLogEntry(logLevel, eventId, exception, category, formatter(state, exception), state as IReadOnlyList<KeyValuePair<string, object?>>)); | ||
|
||
/// <inheritdoc /> | ||
public bool IsEnabled(LogLevel logLevel) => options.Value.Rules.Any(rule => rule.ProviderName == "InMemoryLogger" && rule.LogLevel <= logLevel); | ||
|
||
/// <inheritdoc /> | ||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => new InMemoryLoggerScope<TState>(state); | ||
|
||
public IReadOnlyList<InMemoryLogEntry> GetLogEntries() => _logEntries; | ||
} | ||
|
||
public class InMemoryLogger<T> : InMemoryLogger, ILogger<T> | ||
{ | ||
public InMemoryLogger(IOptions<LoggerFilterOptions> options) : base(options, typeof(T).GetFullFriendlyName()) { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using System.Collections.Concurrent; | ||
|
||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Frank.Testing.Logging; | ||
|
||
public class InMemoryLoggerProvider(IOptions<LoggerFilterOptions> options) : ILoggerProvider | ||
{ | ||
private readonly ConcurrentDictionary<string, InMemoryLogger> _loggers = new(); | ||
|
||
/// <inheritdoc /> | ||
public ILogger CreateLogger(string categoryName) => _loggers.GetOrAdd(categoryName, new InMemoryLogger(options, categoryName)); | ||
|
||
/// <inheritdoc /> | ||
public void Dispose() => _loggers.Clear(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
namespace Frank.Testing.Logging; | ||
|
||
public class InMemoryLoggerScope<T> : IDisposable | ||
{ | ||
public T? State { get; private set; } | ||
|
||
public InMemoryLoggerScope(object state) => State = state is T t ? t : throw new ArgumentException($"The state must be of type {typeof(T).Name}"); | ||
|
||
/// <inheritdoc /> | ||
public void Dispose() => State = default; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using System.Text.Json; | ||
|
||
namespace Frank.Testing.Logging; | ||
|
||
public class JsonFormatter | ||
{ | ||
public static string Format<TState>(TState state, Exception? exception) | ||
{ | ||
return JsonSerializer.Serialize(state); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using System.Text.Json; | ||
|
||
using Frank.PulseFlow.Logging; | ||
|
||
using Microsoft.Extensions.Logging; | ||
|
||
using Xunit.Abstractions; | ||
|
||
namespace Frank.Testing.Logging; | ||
|
||
public class JsonTestLogger : ILogger | ||
{ | ||
private readonly ITestOutputHelper _outputHelper; | ||
private readonly LogLevel _logLevel; | ||
private readonly string _categoryName; | ||
|
||
public JsonTestLogger(ITestOutputHelper outputHelper, LogLevel logLevel, string categoryName) | ||
{ | ||
_outputHelper = outputHelper; | ||
_logLevel = logLevel; | ||
_categoryName = categoryName; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) | ||
{ | ||
if (!IsEnabled(logLevel)) | ||
return; | ||
|
||
var json = JsonFormatter.Format(state, exception); | ||
|
||
JsonDocument document = JsonDocument.Parse(formatter.Invoke(state, exception)); | ||
|
||
_outputHelper.WriteLine(new LogPulse(logLevel, eventId, exception, _categoryName, formatter.Invoke(state, exception), state as IReadOnlyList<KeyValuePair<string, object?>>).ToString()); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public bool IsEnabled(LogLevel logLevel) | ||
{ | ||
return _logLevel <= logLevel; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull | ||
{ | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using Microsoft.Extensions.Logging; | ||
|
||
using Xunit.Abstractions; | ||
|
||
namespace Frank.Testing.Logging; | ||
|
||
public class JsonTestLoggerProvider : ILoggerProvider | ||
{ | ||
private readonly ITestOutputHelper _outputHelper; | ||
private readonly LogLevel _logLevel; | ||
|
||
public JsonTestLoggerProvider(ITestOutputHelper outputHelper, LogLevel logLevel) | ||
{ | ||
_outputHelper = outputHelper; | ||
_logLevel = logLevel; | ||
} | ||
|
||
public ILogger CreateLogger(string categoryName) => new JsonTestLogger(_outputHelper, _logLevel, categoryName); | ||
|
||
public void Dispose() | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using Microsoft.AspNetCore.Builder; | ||
|
||
namespace Frank.Testing.TestBases; | ||
|
||
internal static class WebApplicationExtensions | ||
{ | ||
public static HttpClient CreateTestClient(this WebApplication application) => | ||
new() | ||
{ | ||
BaseAddress = new Uri(application.Urls.FirstOrDefault() ?? throw new InvalidOperationException("Base address for TestClient has not been initialized yet."), UriKind.Absolute) | ||
}; | ||
} |
Oops, something went wrong.