using System; using System.Collections.Generic; using MxGateway.Worker.MxAccess; namespace MxGateway.Worker.Tests.MxAccess; /// /// Worker-007 regression tests for . The /// adapter no longer falls back to late-bound Type.InvokeMember /// reflection: a COM object must implement either the typed /// ILMXProxyServer COM interface family (production) or /// directly (test fakes). /// public sealed class MxAccessComServerTests { /// /// A COM object implementing is routed /// through the typed interface — no reflection — preserving arguments /// and return values. /// [Fact] public void Methods_WithTypedServer_RouteThroughTypedInterface() { RecordingMxAccessServer typed = new(registerHandle: 77); MxAccessComServer adapter = new(typed); int serverHandle = adapter.Register("client-a"); adapter.Advise(serverHandle, itemHandle: 9); adapter.Unregister(serverHandle); Assert.Equal(77, serverHandle); Assert.Equal("client-a", typed.RegisteredClientName); Assert.Equal(new[] { "Register:client-a", "Advise:77:9", "Unregister:77" }, typed.Calls); } /// /// A COM object that implements neither the typed COM interface family /// nor fails fast with a clear /// instead of a late-bound /// reflection call. /// [Fact] public void Methods_WithUntypedObject_ThrowInvalidOperation() { MxAccessComServer adapter = new(new object()); InvalidOperationException exception = Assert.Throws(() => adapter.Register("client")); Assert.Contains("does not implement", exception.Message, StringComparison.Ordinal); Assert.Contains(nameof(IMxAccessServer), exception.Message, StringComparison.Ordinal); } /// /// Exceptions thrown by the typed server propagate unchanged — no /// TargetInvocationException wrapping (reflection is gone). /// [Fact] public void Methods_WhenTypedServerThrows_PropagateOriginalException() { RecordingMxAccessServer typed = new(registerHandle: 1) { ThrowOnRegister = new InvalidOperationException("register failed"), }; MxAccessComServer adapter = new(typed); InvalidOperationException exception = Assert.Throws(() => adapter.Register("client")); Assert.Equal("register failed", exception.Message); } private sealed class RecordingMxAccessServer : IMxAccessServer { private readonly int registerHandle; private readonly List calls = new(); public RecordingMxAccessServer(int registerHandle) { this.registerHandle = registerHandle; } public string? RegisteredClientName { get; private set; } public Exception? ThrowOnRegister { get; set; } public IReadOnlyList Calls => calls.ToArray(); public int Register(string clientName) { calls.Add($"Register:{clientName}"); RegisteredClientName = clientName; if (ThrowOnRegister is not null) { throw ThrowOnRegister; } return registerHandle; } public void Unregister(int serverHandle) { calls.Add($"Unregister:{serverHandle}"); } public int AddItem(int serverHandle, string itemDefinition) { calls.Add($"AddItem:{serverHandle}:{itemDefinition}"); return 0; } public int AddItem2(int serverHandle, string itemDefinition, string itemContext) { calls.Add($"AddItem2:{serverHandle}:{itemDefinition}:{itemContext}"); return 0; } public void RemoveItem(int serverHandle, int itemHandle) { calls.Add($"RemoveItem:{serverHandle}:{itemHandle}"); } public void Advise(int serverHandle, int itemHandle) { calls.Add($"Advise:{serverHandle}:{itemHandle}"); } public void UnAdvise(int serverHandle, int itemHandle) { calls.Add($"UnAdvise:{serverHandle}:{itemHandle}"); } public void AdviseSupervisory(int serverHandle, int itemHandle) { calls.Add($"AdviseSupervisory:{serverHandle}:{itemHandle}"); } public void Write(int serverHandle, int itemHandle, object? value, int userId) { calls.Add($"Write:{serverHandle}:{itemHandle}:{value}:{userId}"); } public void Write2(int serverHandle, int itemHandle, object? value, object? timestamp, int userId) { calls.Add($"Write2:{serverHandle}:{itemHandle}:{value}:{timestamp}:{userId}"); } public void WriteSecured(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value) { calls.Add($"WriteSecured:{serverHandle}:{itemHandle}:{currentUserId}:{verifierUserId}:{value}"); } public void WriteSecured2( int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value, object? timestamp) { calls.Add($"WriteSecured2:{serverHandle}:{itemHandle}:{currentUserId}:{verifierUserId}:{value}:{timestamp}"); } } }