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); } } }