feat(worker): implement 6 MXAccess COM commands in executor

Wire up the previously-unimplemented Suspend, Activate, AuthenticateUser,
ArchestrAUserToId, AddBufferedItem, and SetBufferedUpdateInterval command
kinds in MxAccessCommandExecutor. These are real COM calls and run on the
STA via the executor.

- IMxAccessServer gains the 6 methods; MxAccessComServer routes them to the
  right interface version (Suspend/Activate -> ILMXProxyServer4 out MxStatus,
  AuthenticateUser -> base ILMXProxyServer, ArchestrAUserToId ->
  ILMXProxyServer2, AddBufferedItem/SetBufferedUpdateInterval ->
  ILMXProxyServer5).
- Suspend/Activate surface the native MxStatus, converted to MxStatusProxy
  via the existing MxStatusProxyConverter.
- AuthenticateUser hands the credential straight to MXAccess and never logs
  it; native HResult failures propagate via the dispatcher.
- MxAccessSession gains matching pass-throughs; AddBufferedItem registers
  the item handle in the handle registry.
- Unit tests (fake IMxAccessServer / fake COM object) cover each arm plus a
  password-non-leak assertion; existing IMxAccessServer fakes updated.

No proto changes (all request/reply messages already exist).
This commit is contained in:
Joseph Doherty
2026-06-15 10:41:22 -04:00
parent f94c206489
commit 29399325d5
8 changed files with 948 additions and 8 deletions
@@ -33,6 +33,39 @@ public sealed class MxAccessComServerTests
Assert.Equal(new[] { "Register:client-a", "Advise:77:9", "Unregister:77" }, typed.Calls);
}
/// <summary>
/// The MXAccess command methods added in the worker COM commands bundle
/// (Suspend/Activate/AuthenticateUser/ArchestrAUserToId/AddBufferedItem/
/// SetBufferedUpdateInterval) route through the typed interface with their
/// arguments preserved, and the credential is never echoed back.
/// </summary>
[Fact]
public void CommandMethods_WithTypedServer_RouteThroughTypedInterface()
{
RecordingMxAccessServer typed = new(registerHandle: 5);
MxAccessComServer adapter = new(typed);
adapter.Suspend(serverHandle: 5, itemHandle: 11);
adapter.Activate(serverHandle: 5, itemHandle: 12);
adapter.AuthenticateUser(serverHandle: 5, verifyUser: "Administrator", verifyUserPassword: "s3cret");
adapter.ArchestrAUserToId(serverHandle: 5, userIdGuid: "guid-1");
adapter.AddBufferedItem(serverHandle: 5, itemDefinition: "TestInt", itemContext: "TestChildObject");
adapter.SetBufferedUpdateInterval(serverHandle: 5, updateIntervalMilliseconds: 250);
Assert.Equal(
new[]
{
"Suspend:5:11",
"Activate:5:12",
"AuthenticateUser:5:Administrator",
"ArchestrAUserToId:5:guid-1",
"AddBufferedItem:5:TestInt:TestChildObject",
"SetBufferedUpdateInterval:5:250",
},
typed.Calls);
Assert.DoesNotContain(typed.Calls, call => call.Contains("s3cret", StringComparison.Ordinal));
}
/// <summary>
/// A COM object that implements neither the typed COM interface family
/// nor <see cref="IMxAccessServer"/> fails fast with a clear
@@ -207,5 +240,60 @@ public sealed class MxAccessComServerTests
{
calls.Add($"WriteSecured2:{serverHandle}:{itemHandle}:{currentUserId}:{verifierUserId}:{value}:{timestamp}");
}
/// <summary>Records a Suspend call and returns a canned status.</summary>
/// <param name="serverHandle">The MXAccess server handle.</param>
/// <param name="itemHandle">The MXAccess item handle.</param>
public object Suspend(int serverHandle, int itemHandle)
{
calls.Add($"Suspend:{serverHandle}:{itemHandle}");
return new object();
}
/// <summary>Records an Activate call and returns a canned status.</summary>
/// <param name="serverHandle">The MXAccess server handle.</param>
/// <param name="itemHandle">The MXAccess item handle.</param>
public object Activate(int serverHandle, int itemHandle)
{
calls.Add($"Activate:{serverHandle}:{itemHandle}");
return new object();
}
/// <summary>Records an AuthenticateUser call and returns zero.</summary>
/// <param name="serverHandle">The MXAccess server handle.</param>
/// <param name="verifyUser">The user name to authenticate.</param>
/// <param name="verifyUserPassword">The credential; recorded only as a fixed marker, never echoed.</param>
public int AuthenticateUser(int serverHandle, string verifyUser, string verifyUserPassword)
{
calls.Add($"AuthenticateUser:{serverHandle}:{verifyUser}");
return 0;
}
/// <summary>Records an ArchestrAUserToId call and returns zero.</summary>
/// <param name="serverHandle">The MXAccess server handle.</param>
/// <param name="userIdGuid">The ArchestrA user GUID to resolve.</param>
public int ArchestrAUserToId(int serverHandle, string userIdGuid)
{
calls.Add($"ArchestrAUserToId:{serverHandle}:{userIdGuid}");
return 0;
}
/// <summary>Records an AddBufferedItem call and returns zero.</summary>
/// <param name="serverHandle">The MXAccess server handle.</param>
/// <param name="itemDefinition">The item definition string to record.</param>
/// <param name="itemContext">The item context string to record.</param>
public int AddBufferedItem(int serverHandle, string itemDefinition, string itemContext)
{
calls.Add($"AddBufferedItem:{serverHandle}:{itemDefinition}:{itemContext}");
return 0;
}
/// <summary>Records a SetBufferedUpdateInterval call.</summary>
/// <param name="serverHandle">The MXAccess server handle.</param>
/// <param name="updateIntervalMilliseconds">The buffered update interval in milliseconds.</param>
public void SetBufferedUpdateInterval(int serverHandle, int updateIntervalMilliseconds)
{
calls.Add($"SetBufferedUpdateInterval:{serverHandle}:{updateIntervalMilliseconds}");
}
}
}