Resolve Tests-001 and Tests-002 code-review findings

Tests-001: FakeSessionManager.TryGetSession unconditionally synthesized a
session, so Invoke_WhenSessionMissing_ThrowsNotFound did not actually
verify the missing-session path. Added ResolveOnlySeededSessions/SeedSession
to the fake, rewrote the missing-session test, and added seeded-resolution
and alarm-RPC missing-session coverage.

Tests-002: re-triaged. GalaxyRepository issues only constant SQL; filters
are applied in-memory by GalaxyHierarchyProjector/GalaxyGlobMatcher. Kept
as a valid coverage gap and added GalaxyFilterInputSafetyTests exercising
filter/glob input safety directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-18 20:46:02 -04:00
parent 2a635c8522
commit b381bfcaf1
3 changed files with 445 additions and 12 deletions
@@ -47,16 +47,17 @@ public sealed class MxAccessGatewayServiceTests
Assert.Equal("operator-session", sessionManager.LastOpenRequest?.ClientSessionName);
}
/// <summary>Verifies that Invoke throws NotFound when the session does not exist.</summary>
/// <summary>
/// Verifies that Invoke maps a genuinely missing session to NotFound via the
/// service's own <c>ResolveSession</c> lookup. No <c>InvokeException</c> is
/// injected — <see cref="FakeSessionManager.ResolveOnlySeededSessions"/> makes
/// <c>TryGetSession</c> return false, so this test fails if the service drops
/// its missing-session check rather than passing for the wrong reason.
/// </summary>
[Fact]
public async Task Invoke_WhenSessionMissing_ThrowsNotFound()
{
FakeSessionManager sessionManager = new()
{
InvokeException = new SessionManagerException(
SessionManagerErrorCode.SessionNotFound,
"Session session-missing was not found."),
};
FakeSessionManager sessionManager = new() { ResolveOnlySeededSessions = true };
MxAccessGatewayService service = CreateService(sessionManager);
RpcException exception = await Assert.ThrowsAsync<RpcException>(
@@ -66,6 +67,76 @@ public sealed class MxAccessGatewayServiceTests
Assert.Equal(StatusCode.NotFound, exception.StatusCode);
Assert.Contains("session-missing", exception.Status.Detail, StringComparison.Ordinal);
// The service must reject before delegating to the session manager.
Assert.Equal(0, sessionManager.InvokeCount);
}
/// <summary>
/// Verifies that Invoke resolves a session that was seeded into the session
/// manager when <see cref="FakeSessionManager.ResolveOnlySeededSessions"/> is on,
/// confirming the missing-session test above is gated on a real lookup.
/// </summary>
[Fact]
public async Task Invoke_WhenSessionSeeded_ResolvesAndInvokes()
{
FakeSessionManager sessionManager = new() { ResolveOnlySeededSessions = true };
sessionManager.SeedSession(CreateSession("session-1", processId: 1234));
MxAccessGatewayService service = CreateService(sessionManager);
MxCommandReply reply = await service.Invoke(
CreatePingRequest("session-1"),
new TestServerCallContext());
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
Assert.Equal(1, sessionManager.InvokeCount);
}
/// <summary>
/// Verifies that AcknowledgeAlarm maps a genuinely missing session to NotFound via
/// the service's own <c>ResolveSession</c> lookup rather than an injected exception.
/// </summary>
[Fact]
public async Task AcknowledgeAlarm_WhenSessionMissing_ThrowsNotFound()
{
FakeSessionManager sessionManager = new() { ResolveOnlySeededSessions = true };
MxAccessGatewayService service = CreateService(sessionManager);
RpcException exception = await Assert.ThrowsAsync<RpcException>(
async () => await service.AcknowledgeAlarm(
new AcknowledgeAlarmRequest
{
SessionId = "session-missing",
AlarmFullReference = "Tank01.Level.HiHi",
OperatorUser = "alice",
},
new TestServerCallContext()));
Assert.Equal(StatusCode.NotFound, exception.StatusCode);
Assert.Contains("session-missing", exception.Status.Detail, StringComparison.Ordinal);
}
/// <summary>
/// Verifies that QueryActiveAlarms maps a genuinely missing session to NotFound via
/// the service's own <c>ResolveSession</c> lookup rather than an injected exception.
/// </summary>
[Fact]
public async Task QueryActiveAlarms_WhenSessionMissing_ThrowsNotFound()
{
FakeSessionManager sessionManager = new() { ResolveOnlySeededSessions = true };
MxAccessGatewayService service = CreateService(sessionManager);
RpcException exception = await Assert.ThrowsAsync<RpcException>(
async () => await service.QueryActiveAlarms(
new QueryActiveAlarmsRequest
{
SessionId = "session-missing",
AlarmFilterPrefix = "Tank01.",
},
new RecordingStreamWriter<ActiveAlarmSnapshot>(),
new TestServerCallContext()));
Assert.Equal(StatusCode.NotFound, exception.StatusCode);
Assert.Contains("session-missing", exception.Status.Detail, StringComparison.Ordinal);
}
/// <summary>Verifies that Invoke throws InvalidArgument and does not invoke the session manager when payload is mismatched.</summary>
@@ -425,9 +496,26 @@ public sealed class MxAccessGatewayServiceTests
private sealed class FakeSessionManager : ISessionManager
{
private readonly Dictionary<string, GatewaySession> seededSessions = new(StringComparer.Ordinal);
/// <summary>The session to return from OpenSessionAsync.</summary>
public GatewaySession? OpenSessionResult { get; init; }
/// <summary>
/// When true, <see cref="TryGetSession"/> only resolves sessions that have been
/// explicitly seeded via <see cref="SeedSession"/> (or <see cref="OpenSessionResult"/>),
/// and returns false for any other id. This exercises the gateway service's own
/// missing-session handling instead of masking it with a synthesized session.
/// </summary>
public bool ResolveOnlySeededSessions { get; init; }
/// <summary>Registers a session so <see cref="TryGetSession"/> resolves its id.</summary>
/// <param name="session">Session to register by its <see cref="GatewaySession.SessionId"/>.</param>
public void SeedSession(GatewaySession session)
{
seededSessions[session.SessionId] = session;
}
/// <summary>The last OpenSessionAsync request captured.</summary>
public SessionOpenRequest? LastOpenRequest { get; private set; }
@@ -484,6 +572,18 @@ public sealed class MxAccessGatewayServiceTests
string sessionId,
out GatewaySession session)
{
if (seededSessions.TryGetValue(sessionId, out GatewaySession? seeded))
{
session = seeded;
return true;
}
if (ResolveOnlySeededSessions)
{
session = null!;
return false;
}
session = OpenSessionResult ?? CreateSession(sessionId, processId: 1234);
return true;
}