feat(lmxproxy): phase 2 — host core (MxAccessClient, SessionManager, SubscriptionManager)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.LmxProxy.Host.Sessions;
|
||||
|
||||
namespace ZB.MOM.WW.LmxProxy.Host.Tests.Sessions
|
||||
{
|
||||
public class SessionManagerTests
|
||||
{
|
||||
[Fact]
|
||||
public void CreateSession_Returns32CharHexId()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
var id = sm.CreateSession("client1", "key1");
|
||||
id.Should().HaveLength(32);
|
||||
id.Should().MatchRegex("^[0-9a-f]{32}$");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateSession_IncrementsCount()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
sm.ActiveSessionCount.Should().Be(0);
|
||||
sm.CreateSession("c1", "k1");
|
||||
sm.ActiveSessionCount.Should().Be(1);
|
||||
sm.CreateSession("c2", "k2");
|
||||
sm.ActiveSessionCount.Should().Be(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateSession_ReturnsTrueForExistingSession()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
var id = sm.CreateSession("c1", "k1");
|
||||
sm.ValidateSession(id).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateSession_ReturnsFalseForUnknownSession()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
sm.ValidateSession("nonexistent").Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidateSession_UpdatesLastActivity()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
var id = sm.CreateSession("c1", "k1");
|
||||
var session = sm.GetSession(id);
|
||||
var initialActivity = session!.LastActivity;
|
||||
|
||||
Thread.Sleep(50); // Small delay to ensure time passes
|
||||
sm.ValidateSession(id);
|
||||
|
||||
session.LastActivity.Should().BeAfter(initialActivity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminateSession_RemovesSession()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
var id = sm.CreateSession("c1", "k1");
|
||||
sm.TerminateSession(id).Should().BeTrue();
|
||||
sm.ActiveSessionCount.Should().Be(0);
|
||||
sm.ValidateSession(id).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminateSession_ReturnsFalseForUnknownSession()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
sm.TerminateSession("nonexistent").Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSession_ReturnsNullForUnknown()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
sm.GetSession("nonexistent").Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSession_ReturnsCorrectInfo()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
var id = sm.CreateSession("client-abc", "key-xyz");
|
||||
var session = sm.GetSession(id);
|
||||
session.Should().NotBeNull();
|
||||
session!.ClientId.Should().Be("client-abc");
|
||||
session.ApiKey.Should().Be("key-xyz");
|
||||
session.SessionId.Should().Be(id);
|
||||
session.ConnectedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAllSessions_ReturnsSnapshot()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
sm.CreateSession("c1", "k1");
|
||||
sm.CreateSession("c2", "k2");
|
||||
var all = sm.GetAllSessions();
|
||||
all.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConcurrentAccess_IsThreadSafe()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
var tasks = new Task[100];
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
int idx = i;
|
||||
tasks[i] = Task.Run(() =>
|
||||
{
|
||||
var id = sm.CreateSession($"client-{idx}", $"key-{idx}");
|
||||
sm.ValidateSession(id);
|
||||
if (idx % 3 == 0) sm.TerminateSession(id);
|
||||
});
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
// Should have ~67 sessions remaining (100 - ~33 terminated)
|
||||
sm.ActiveSessionCount.Should().BeInRange(60, 70);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dispose_ClearsAllSessions()
|
||||
{
|
||||
var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
sm.CreateSession("c1", "k1");
|
||||
sm.CreateSession("c2", "k2");
|
||||
sm.Dispose();
|
||||
sm.ActiveSessionCount.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConnectedSinceUtcTicks_ReturnsCorrectValue()
|
||||
{
|
||||
using var sm = new SessionManager(inactivityTimeoutMinutes: 0);
|
||||
var id = sm.CreateSession("c1", "k1");
|
||||
var session = sm.GetSession(id);
|
||||
session!.ConnectedSinceUtcTicks.Should().Be(session.ConnectedAt.Ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user