Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Browsing/BrowseSessionReaperTests.cs
Joseph Doherty bd6c0b4d3d docs: complete XML doc comments via fixdocs (2757 to 131 findings)
Add missing <returns>/<param>/<summary>/<typeparam> tags and clean up
misused inheritdoc across 481 files so the documented API surface is
complete. Documentation-only (zero code lines changed). The 131 remaining
findings are inheritdoc-style warnings deliberately left to preserve
hand-written implementation rationale (plan-decision notes, race-condition
explanations).
2026-06-03 12:34:34 -04:00

95 lines
3.9 KiB
C#

using Microsoft.Extensions.Logging.Abstractions;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Browsing;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Browsing;
/// <summary>Unit tests for <see cref="BrowseSessionReaper"/>. We drive the reaper
/// directly through the internal <c>ReapOnceAsync</c> entry point and skew
/// <see cref="FakeBrowseSession.LastUsedUtc"/> to simulate the passage of time —
/// no real wall-clock waits.</summary>
public sealed class BrowseSessionReaperTests
{
private static BrowseSessionReaper NewReaper(BrowseSessionRegistry registry) =>
new(registry, NullLogger<BrowseSessionReaper>.Instance);
/// <summary>Verifies that ReapOnceAsync evicts a session that has been idle beyond the timeout.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReapOnceAsync_evicts_idle_session()
{
var registry = new BrowseSessionRegistry();
var session = new FakeBrowseSession
{
LastUsedUtc = DateTime.UtcNow - TimeSpan.FromMinutes(3),
};
registry.Register(session);
var reaper = NewReaper(registry);
await reaper.ReapOnceAsync(CancellationToken.None);
registry.TryGet(session.Token, out _).ShouldBeFalse();
session.Disposed.ShouldBeTrue();
}
/// <summary>Verifies that ReapOnceAsync preserves a recently used session.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReapOnceAsync_preserves_recent_session()
{
var registry = new BrowseSessionRegistry();
var session = new FakeBrowseSession { LastUsedUtc = DateTime.UtcNow };
registry.Register(session);
var reaper = NewReaper(registry);
await reaper.ReapOnceAsync(CancellationToken.None);
registry.TryGet(session.Token, out _).ShouldBeTrue();
session.Disposed.ShouldBeFalse();
}
/// <summary>Verifies that ReapOnceAsync handles a session already removed from the registry without throwing.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReapOnceAsync_handles_already_removed_session()
{
var registry = new BrowseSessionRegistry();
var session = new FakeBrowseSession
{
LastUsedUtc = DateTime.UtcNow - TimeSpan.FromMinutes(3),
};
registry.Register(session);
// Race the reaper: pull the entry out before the loop calls TryRemove.
registry.TryRemove(session.Token, out _).ShouldBeTrue();
var reaper = NewReaper(registry);
// No exception, no double dispose. Because we removed the session ourselves
// without disposing it, Disposed should still be false.
await reaper.ReapOnceAsync(CancellationToken.None);
session.Disposed.ShouldBeFalse();
}
/// <summary>Verifies that ReapOnceAsync continues processing remaining sessions when one session's dispose throws.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReapOnceAsync_continues_when_one_session_dispose_throws()
{
var registry = new BrowseSessionRegistry();
var staleAt = DateTime.UtcNow - TimeSpan.FromMinutes(3);
var bad = new FakeBrowseSession { LastUsedUtc = staleAt, ThrowOnDispose = true };
var good = new FakeBrowseSession { LastUsedUtc = staleAt };
registry.Register(bad);
registry.Register(good);
var reaper = NewReaper(registry);
// The dispose exception is swallowed; the second session must still be reaped + disposed.
await Should.NotThrowAsync(() => reaper.ReapOnceAsync(CancellationToken.None));
registry.TryGet(bad.Token, out _).ShouldBeFalse();
registry.TryGet(good.Token, out _).ShouldBeFalse();
good.Disposed.ShouldBeTrue();
}
}