| File: Backchannel\BackchannelLoggerProviderTests.cs | Web Access |
| Project: src\tests\Aspire.Hosting.Tests\Aspire.Hosting.Tests.csproj (Aspire.Hosting.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Extensions.Logging; namespace Aspire.Hosting.Backchannel; public class BackchannelLoggerProviderTests { [Fact] public void Subscribe_ReturnsBufferedEntries() { using var provider = new BackchannelLoggerProvider(); var logger = provider.CreateLogger("TestCategory"); logger.LogInformation("Message 1"); logger.LogWarning("Message 2"); logger.LogError("Message 3"); var (snapshot, subscriberId, _) = provider.Subscribe(); provider.Unsubscribe(subscriberId); Assert.Equal(3, snapshot.Count); Assert.Equal("Message 1", snapshot[0].Message); Assert.Equal("Message 2", snapshot[1].Message); Assert.Equal("Message 3", snapshot[2].Message); Assert.Equal("TestCategory", snapshot[0].CategoryName); Assert.Equal(LogLevel.Information, snapshot[0].LogLevel); Assert.Equal(LogLevel.Warning, snapshot[1].LogLevel); Assert.Equal(LogLevel.Error, snapshot[2].LogLevel); } [Fact] public void ReplayBuffer_EvictsOldestWhenFull() { using var provider = new BackchannelLoggerProvider(); var logger = provider.CreateLogger("TestCategory"); // Write 1001 entries — the first should be evicted for (var i = 0; i < 1001; i++) { logger.LogInformation("Message {Index}", i); } var (snapshot, subscriberId, _) = provider.Subscribe(); provider.Unsubscribe(subscriberId); Assert.Equal(1000, snapshot.Count); // First entry should be "Message 1" (index 0 was evicted) Assert.Equal("Message 1", snapshot[0].Message); Assert.Equal("Message 1000", snapshot[999].Message); } [Fact] public void Subscribe_ReturnsIndependentSnapshot() { using var provider = new BackchannelLoggerProvider(); var logger = provider.CreateLogger("TestCategory"); logger.LogInformation("Before snapshot"); var (snapshot1, sub1, _) = provider.Subscribe(); provider.Unsubscribe(sub1); logger.LogInformation("After snapshot"); var (snapshot2, sub2, _) = provider.Subscribe(); provider.Unsubscribe(sub2); // First snapshot should not be affected by subsequent writes Assert.Single(snapshot1); Assert.Equal(2, snapshot2.Count); } [Fact] public async Task ConcurrentSubscribers_ReceiveSameEntries() { using var provider = new BackchannelLoggerProvider(); var logger = provider.CreateLogger("TestCategory"); logger.LogInformation("Historical"); // Two subscribers connect concurrently var (snapshot1, sub1, channel1) = provider.Subscribe(); var (snapshot2, sub2, channel2) = provider.Subscribe(); // Both see the historical entry Assert.Single(snapshot1); Assert.Single(snapshot2); // New entries arrive after both subscribe logger.LogInformation("Live 1"); logger.LogInformation("Live 2"); // Both subscribers receive both live entries Assert.True(channel1.Reader.TryRead(out var entry1a)); Assert.Equal("Live 1", entry1a!.Message); Assert.True(channel1.Reader.TryRead(out var entry1b)); Assert.Equal("Live 2", entry1b!.Message); Assert.True(channel2.Reader.TryRead(out var entry2a)); Assert.Equal("Live 1", entry2a!.Message); Assert.True(channel2.Reader.TryRead(out var entry2b)); Assert.Equal("Live 2", entry2b!.Message); provider.Unsubscribe(sub1); provider.Unsubscribe(sub2); // Channels are completed after unsubscribe await channel1.Reader.Completion; await channel2.Reader.Completion; } }