rename: prefix gateway projects/namespaces with ZB.MOM.WW + sln→slnx

Apply the ZB.MOM.WW. prefix to all gateway-side projects, folders,
.csproj/.sln contents, C# namespaces, using directives, generated proto
C# (csharp_namespace + checked-in generated files), InternalsVisibleTo
attributes, project-name string literals (LoadProject, .sln lookups,
worker exe paths, staticwebassets manifest), and the install/script/doc
references that point at any of the above. Migrate the solution from
.sln to .slnx via `dotnet sln migrate` and delete the old file.

External-runtime identifiers are intentionally NOT prefixed so external
configuration keeps working:
- GatewayMetrics.cs MeterName ("MxGateway.Server")
- DashboardAuthenticationDefaults Scheme/Policy ("MxGateway.Dashboard")
- GatewayRequestLoggingMiddleware logger category ("MxGateway.Request")
- StaRuntime thread name ("MxGateway.Worker.STA")
- appsettings.json root section "MxGateway" + env-var prefix
  MxGateway__... and secret-name MxGateway:ApiKeyPepper
- C:\ProgramData\MxGateway\ data dir paths

Also fixes two tests that were not rename-related but became visible
while validating the rename:

- WorkerLiveMxAccessSmokeTests.ShutDownAsync: cancellation that the
  gateway service correctly maps to RpcException(Cancelled) per gRPC
  convention was being misclassified as a stream fault. Added a sibling
  catch on RpcException with StatusCode.Cancelled.

- IntegrationTestEnvironment.ResolveRepositoryRoot: extracted IsRepositoryRoot
  and made it accept either a .git marker OR a .sln/.slnx next to src/
  so the worker-exe walker works in non-git working copies.

clients/proto/proto-inputs.json's protoRoot updated to point at
src/ZB.MOM.WW.MxGateway.Contracts/Protos.

Verified by `dotnet build` and a full `dotnet test` of the .slnx with
MXGATEWAY_RUN_LIVE_{MXACCESS,LDAP,GALAXY}_TESTS=1:
  Tests: 472/472 pass
  Worker.Tests: 280/280 pass (4 dev-rig [Fact(Skip=...)] skipped)
  IntegrationTests: 18/18 pass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-23 16:22:23 -04:00
parent 867bf18116
commit dc9c0c950c
491 changed files with 32854 additions and 8414 deletions
@@ -0,0 +1,141 @@
using System.Buffers.Binary;
using ZB.MOM.WW.MxGateway.Server.Configuration;
using ZB.MOM.WW.MxGateway.Server.Workers;
namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Workers;
/// <summary>
/// Coverage for <see cref="WorkerExecutableValidator"/> PE-header architecture parsing
/// (finding Server-013). The validator reads the DOS <c>MZ</c> stub, follows the PE
/// header offset at <c>0x3c</c>, checks the <c>PE\0\0</c> signature, and compares the
/// machine field against the required <see cref="WorkerArchitecture"/>.
/// </summary>
public sealed class WorkerExecutableValidatorTests : IDisposable
{
private const ushort ImageFileMachineI386 = 0x014c;
private const ushort ImageFileMachineAmd64 = 0x8664;
private readonly List<string> _tempFiles = [];
[Fact]
public void Validate_X86ExecutableMatchingRequiredArchitecture_DoesNotThrow()
{
string path = WritePeFile(ImageFileMachineI386);
WorkerExecutableValidator.Validate(path, WorkerArchitecture.X86);
}
[Fact]
public void Validate_X64ExecutableMatchingRequiredArchitecture_DoesNotThrow()
{
string path = WritePeFile(ImageFileMachineAmd64);
WorkerExecutableValidator.Validate(path, WorkerArchitecture.X64);
}
[Fact]
public void Validate_X64ExecutableWhenX86Required_ThrowsInvalidExecutable()
{
string path = WritePeFile(ImageFileMachineAmd64);
WorkerProcessLaunchException exception = Assert.Throws<WorkerProcessLaunchException>(
() => WorkerExecutableValidator.Validate(path, WorkerArchitecture.X86));
Assert.Equal(WorkerProcessLaunchErrorCode.InvalidExecutable, exception.ErrorCode);
Assert.Contains("architecture", exception.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void Validate_X86ExecutableWhenX64Required_ThrowsInvalidExecutable()
{
string path = WritePeFile(ImageFileMachineI386);
WorkerProcessLaunchException exception = Assert.Throws<WorkerProcessLaunchException>(
() => WorkerExecutableValidator.Validate(path, WorkerArchitecture.X64));
Assert.Equal(WorkerProcessLaunchErrorCode.InvalidExecutable, exception.ErrorCode);
}
[Fact]
public void Validate_FileWithoutMzHeader_ThrowsInvalidExecutable()
{
byte[] bytes = new byte[0x80];
// Leave the first two bytes as zero so the MZ signature check fails.
string path = WriteTempFile(bytes);
WorkerProcessLaunchException exception = Assert.Throws<WorkerProcessLaunchException>(
() => WorkerExecutableValidator.Validate(path, WorkerArchitecture.X86));
Assert.Equal(WorkerProcessLaunchErrorCode.InvalidExecutable, exception.ErrorCode);
Assert.Contains("MZ", exception.Message, StringComparison.Ordinal);
}
[Fact]
public void Validate_FileTooSmallForPeHeader_ThrowsInvalidExecutable()
{
string path = WriteTempFile([(byte)'M', (byte)'Z']);
WorkerProcessLaunchException exception = Assert.Throws<WorkerProcessLaunchException>(
() => WorkerExecutableValidator.Validate(path, WorkerArchitecture.X86));
Assert.Equal(WorkerProcessLaunchErrorCode.InvalidExecutable, exception.ErrorCode);
}
[Fact]
public void Validate_FileWithoutPeSignature_ThrowsInvalidExecutable()
{
// Build a valid MZ header pointing at a PE offset that holds a wrong signature.
byte[] bytes = new byte[0x100];
bytes[0] = (byte)'M';
bytes[1] = (byte)'Z';
BinaryPrimitives.WriteInt32LittleEndian(bytes.AsSpan(0x3c, sizeof(int)), 0x80);
// PE region left as zeros — the "PE\0\0" signature check fails.
string path = WriteTempFile(bytes);
WorkerProcessLaunchException exception = Assert.Throws<WorkerProcessLaunchException>(
() => WorkerExecutableValidator.Validate(path, WorkerArchitecture.X86));
Assert.Equal(WorkerProcessLaunchErrorCode.InvalidExecutable, exception.ErrorCode);
Assert.Contains("PE", exception.Message, StringComparison.Ordinal);
}
private string WritePeFile(ushort machine)
{
const int peHeaderOffset = 0x80;
byte[] bytes = new byte[peHeaderOffset + 6];
bytes[0] = (byte)'M';
bytes[1] = (byte)'Z';
BinaryPrimitives.WriteInt32LittleEndian(bytes.AsSpan(0x3c, sizeof(int)), peHeaderOffset);
bytes[peHeaderOffset] = (byte)'P';
bytes[peHeaderOffset + 1] = (byte)'E';
bytes[peHeaderOffset + 2] = 0;
bytes[peHeaderOffset + 3] = 0;
BinaryPrimitives.WriteUInt16LittleEndian(bytes.AsSpan(peHeaderOffset + 4, sizeof(ushort)), machine);
return WriteTempFile(bytes);
}
private string WriteTempFile(byte[] bytes)
{
string path = Path.Combine(Path.GetTempPath(), $"mxgw-pe-{Guid.NewGuid():N}.bin");
File.WriteAllBytes(path, bytes);
_tempFiles.Add(path);
return path;
}
public void Dispose()
{
foreach (string path in _tempFiles)
{
try
{
File.Delete(path);
}
catch (IOException)
{
// Best-effort cleanup of the temp PE fixtures.
}
}
_tempFiles.Clear();
}
}