Files
Anatolii Grynchuk 85b362e8cd chore: add hrynco common library solution
- add the standalone HrynCo.Common solution and projects
- include the shared common library source and tests
- add package metadata and a repo gitignore
2026-05-01 00:17:34 +03:00

95 lines
2.9 KiB
C#

namespace HrynCo.Common.Tests;
using System.Collections.Concurrent;
using HrynCo.Common;
using FluentAssertions;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using Xunit;
public sealed class ProfilerTests
{
[Fact]
public async Task MeasureExecutionAsync_ShouldReturnResultAndWriteStartAndEndEvents()
{
var sink = new CollectingSink();
ILogger logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.Sink(sink)
.CreateLogger();
var profiler = new Profiler(logger);
int result = await profiler.MeasureExecutionAsync(async () =>
{
await Task.Delay(1);
return 42;
}, "LoadItems");
result.Should().Be(42);
sink.Events.Count.Should().BeGreaterThan(1);
sink.Events.Should().ContainSingle(e =>
e.Level == LogEventLevel.Information
&& e.MessageTemplate.Text.Contains("Start", StringComparison.Ordinal)
&& e.Properties["BlockName"].ToString().Contains("LoadItems", StringComparison.Ordinal));
sink.Events.Should().ContainSingle(e =>
e.Level == LogEventLevel.Information
&& e.MessageTemplate.Text.Contains("End", StringComparison.Ordinal)
&& e.Properties["Measurements"].ToString().Contains("True", StringComparison.Ordinal));
}
[Fact]
public async Task MeasureExecutionAsync_ActionOverload_ShouldInvokeAction()
{
var sink = new CollectingSink();
ILogger logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.Sink(sink)
.CreateLogger();
var profiler = new Profiler(logger);
bool invoked = false;
await profiler.MeasureExecutionAsync(async () =>
{
invoked = true;
await Task.CompletedTask;
}, "ActionBlock");
invoked.Should().BeTrue();
}
[Fact]
public async Task MeasureExecutionAsync_ShouldLogErrorAndRethrow()
{
var sink = new CollectingSink();
ILogger logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.Sink(sink)
.CreateLogger();
var profiler = new Profiler(logger);
Func<Task> act = async () =>
{
await profiler.MeasureExecutionAsync<int>(() => throw new InvalidOperationException("boom"), "FailBlock");
};
await act.Should().ThrowAsync<InvalidOperationException>();
sink.Events.Should().Contain(e =>
e.Level == LogEventLevel.Error
&& e.MessageTemplate.Text.Contains("An error occurred", StringComparison.Ordinal));
}
private sealed class CollectingSink : ILogEventSink
{
public ConcurrentQueue<LogEvent> Events { get; } = new();
public void Emit(LogEvent logEvent)
{
Events.Enqueue(logEvent);
}
}
}