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:
Joseph Doherty
2026-03-21 23:58:17 -04:00
parent 0d63fb1105
commit 64c92c63e5
15 changed files with 1834 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
using ZB.MOM.WW.LmxProxy.Host.MxAccess;
namespace ZB.MOM.WW.LmxProxy.Host.Tests.MxAccess
{
public class StaDispatchThreadTests
{
[Fact]
public async Task DispatchAsync_ExecutesOnStaThread()
{
using var sta = new StaDispatchThread("Test-STA");
var threadId = await sta.DispatchAsync(() => Thread.CurrentThread.ManagedThreadId);
threadId.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
}
[Fact]
public async Task DispatchAsync_ReturnsResult()
{
using var sta = new StaDispatchThread("Test-STA");
var result = await sta.DispatchAsync(() => 42);
result.Should().Be(42);
}
[Fact]
public async Task DispatchAsync_PropagatesException()
{
using var sta = new StaDispatchThread("Test-STA");
Func<Task> act = () => sta.DispatchAsync<int>(() => throw new InvalidOperationException("test error"));
await act.Should().ThrowAsync<InvalidOperationException>().WithMessage("test error");
}
[Fact]
public async Task DispatchAsync_Action_Completes()
{
using var sta = new StaDispatchThread("Test-STA");
int value = 0;
await sta.DispatchAsync(() => { value = 99; });
value.Should().Be(99);
}
[Fact]
public void Dispose_CompletesGracefully()
{
var sta = new StaDispatchThread("Test-STA");
sta.Dispose(); // Should not throw
}
[Fact]
public void DispatchAfterDispose_ThrowsObjectDisposedException()
{
var sta = new StaDispatchThread("Test-STA");
sta.Dispose();
Func<Task> act = () => sta.DispatchAsync(() => 42);
act.Should().ThrowAsync<ObjectDisposedException>();
}
[Fact]
public async Task MultipleDispatches_ExecuteInOrder()
{
using var sta = new StaDispatchThread("Test-STA");
var results = new System.Collections.Concurrent.ConcurrentBag<int>();
var tasks = new Task[10];
for (int i = 0; i < 10; i++)
{
int idx = i;
tasks[i] = sta.DispatchAsync(() => { results.Add(idx); });
}
await Task.WhenAll(tasks);
results.Count.Should().Be(10);
}
[Fact]
public async Task StaThread_HasStaApartmentState()
{
using var sta = new StaDispatchThread("Test-STA");
var apartmentState = await sta.DispatchAsync(() => Thread.CurrentThread.GetApartmentState());
apartmentState.Should().Be(ApartmentState.STA);
}
}
}

View File

@@ -0,0 +1,78 @@
using FluentAssertions;
using Xunit;
using ZB.MOM.WW.LmxProxy.Host.Domain;
namespace ZB.MOM.WW.LmxProxy.Host.Tests.MxAccess
{
public class TypedValueEqualsTests
{
[Fact]
public void NullEqualsNull() => TypedValueComparer.Equals(null, null).Should().BeTrue();
[Fact]
public void NullNotEqualsValue() => TypedValueComparer.Equals(null, 42).Should().BeFalse();
[Fact]
public void ValueNotEqualsNull() => TypedValueComparer.Equals(42, null).Should().BeFalse();
[Fact]
public void SameTypeAndValue() => TypedValueComparer.Equals(42.5, 42.5).Should().BeTrue();
[Fact]
public void SameTypeDifferentValue() => TypedValueComparer.Equals(42.5, 43.0).Should().BeFalse();
[Fact]
public void DifferentTypes_NeverEqual() => TypedValueComparer.Equals(1, 1.0).Should().BeFalse();
[Fact]
public void BoolTrue() => TypedValueComparer.Equals(true, true).Should().BeTrue();
[Fact]
public void BoolFalse() => TypedValueComparer.Equals(false, true).Should().BeFalse();
[Fact]
public void String_CaseSensitive()
{
TypedValueComparer.Equals("DONE", "DONE").Should().BeTrue();
TypedValueComparer.Equals("done", "DONE").Should().BeFalse();
}
[Fact]
public void Array_SameElements()
{
TypedValueComparer.Equals(new[] { 1, 2, 3 }, new[] { 1, 2, 3 }).Should().BeTrue();
}
[Fact]
public void Array_DifferentElements()
{
TypedValueComparer.Equals(new[] { 1, 2, 3 }, new[] { 1, 2, 4 }).Should().BeFalse();
}
[Fact]
public void Array_DifferentLengths()
{
TypedValueComparer.Equals(new[] { 1, 2 }, new[] { 1, 2, 3 }).Should().BeFalse();
}
[Fact]
public void Int32_NotEqual_ToDouble()
{
TypedValueComparer.Equals(1, 1.0).Should().BeFalse();
}
[Fact]
public void Long_Equality()
{
TypedValueComparer.Equals(long.MaxValue, long.MaxValue).Should().BeTrue();
}
[Fact]
public void DateTime_TickPrecision()
{
var dt1 = new System.DateTime(638789000000000000, System.DateTimeKind.Utc);
var dt2 = new System.DateTime(638789000000000000, System.DateTimeKind.Utc);
TypedValueComparer.Equals(dt1, dt2).Should().BeTrue();
}
}
}