feat(sessions): record OwnerKeyId on session creation
Add a nullable string? OwnerKeyId property to GatewaySession that captures the API key identifier (KeyId) of the authenticated caller that opened the session. Wire it through ISessionManager.OpenSessionAsync → SessionManager → GatewaySession constructor. The gRPC service passes identityAccessor .Current?.KeyId; internal callers (GatewayAlarmMonitor, DashboardLiveDataService) pass null. Covers the positive and null cases with two new TDD-first tests.
This commit is contained in:
@@ -203,6 +203,7 @@ public sealed class GatewayAlarmMonitor : BackgroundService, IGatewayAlarmServic
|
|||||||
GatewaySession session = await _sessionManager.OpenSessionAsync(
|
GatewaySession session = await _sessionManager.OpenSessionAsync(
|
||||||
new SessionOpenRequest(BackendName, MonitorClientName, Guid.NewGuid().ToString("N"), CommandTimeout: null),
|
new SessionOpenRequest(BackendName, MonitorClientName, Guid.NewGuid().ToString("N"), CommandTimeout: null),
|
||||||
MonitorClientName,
|
MonitorClientName,
|
||||||
|
ownerKeyId: null,
|
||||||
stoppingToken)
|
stoppingToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
lock (_sync) { _session = session; }
|
lock (_sync) { _session = session; }
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ public sealed class DashboardLiveDataService : IDashboardLiveDataService, IAsync
|
|||||||
GatewaySession session = await _sessionManager.OpenSessionAsync(
|
GatewaySession session = await _sessionManager.OpenSessionAsync(
|
||||||
new SessionOpenRequest(BackendName, ClientName, Guid.NewGuid().ToString("N"), CommandTimeout: null),
|
new SessionOpenRequest(BackendName, ClientName, Guid.NewGuid().ToString("N"), CommandTimeout: null),
|
||||||
ClientName,
|
ClientName,
|
||||||
|
ownerKeyId: null,
|
||||||
cancellationToken)
|
cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public sealed class MxAccessGatewayService(
|
|||||||
.OpenSessionAsync(
|
.OpenSessionAsync(
|
||||||
SessionOpenRequest.FromContract(request),
|
SessionOpenRequest.FromContract(request),
|
||||||
ResolveClientIdentity(),
|
ResolveClientIdentity(),
|
||||||
|
identityAccessor.Current?.KeyId,
|
||||||
context.CancellationToken)
|
context.CancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ public sealed class GatewaySession
|
|||||||
pipeName,
|
pipeName,
|
||||||
nonce,
|
nonce,
|
||||||
clientIdentity,
|
clientIdentity,
|
||||||
|
ownerKeyId: null,
|
||||||
clientSessionName,
|
clientSessionName,
|
||||||
clientCorrelationId,
|
clientCorrelationId,
|
||||||
commandTimeout,
|
commandTimeout,
|
||||||
@@ -66,6 +67,7 @@ public sealed class GatewaySession
|
|||||||
/// <param name="pipeName">Name of the named pipe for gateway-worker IPC.</param>
|
/// <param name="pipeName">Name of the named pipe for gateway-worker IPC.</param>
|
||||||
/// <param name="nonce">Security nonce for worker validation.</param>
|
/// <param name="nonce">Security nonce for worker validation.</param>
|
||||||
/// <param name="clientIdentity">Client identity from the authentication context.</param>
|
/// <param name="clientIdentity">Client identity from the authentication context.</param>
|
||||||
|
/// <param name="ownerKeyId">API key identifier of the caller that created this session.</param>
|
||||||
/// <param name="clientSessionName">Client-supplied session name.</param>
|
/// <param name="clientSessionName">Client-supplied session name.</param>
|
||||||
/// <param name="clientCorrelationId">Client-supplied correlation identifier.</param>
|
/// <param name="clientCorrelationId">Client-supplied correlation identifier.</param>
|
||||||
/// <param name="commandTimeout">Timeout for command invocation.</param>
|
/// <param name="commandTimeout">Timeout for command invocation.</param>
|
||||||
@@ -79,6 +81,7 @@ public sealed class GatewaySession
|
|||||||
string pipeName,
|
string pipeName,
|
||||||
string nonce,
|
string nonce,
|
||||||
string? clientIdentity,
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId,
|
||||||
string? clientSessionName,
|
string? clientSessionName,
|
||||||
string? clientCorrelationId,
|
string? clientCorrelationId,
|
||||||
TimeSpan commandTimeout,
|
TimeSpan commandTimeout,
|
||||||
@@ -112,6 +115,7 @@ public sealed class GatewaySession
|
|||||||
PipeName = pipeName;
|
PipeName = pipeName;
|
||||||
Nonce = nonce;
|
Nonce = nonce;
|
||||||
ClientIdentity = clientIdentity;
|
ClientIdentity = clientIdentity;
|
||||||
|
OwnerKeyId = ownerKeyId;
|
||||||
ClientSessionName = clientSessionName;
|
ClientSessionName = clientSessionName;
|
||||||
ClientCorrelationId = clientCorrelationId;
|
ClientCorrelationId = clientCorrelationId;
|
||||||
CommandTimeout = commandTimeout;
|
CommandTimeout = commandTimeout;
|
||||||
@@ -148,6 +152,11 @@ public sealed class GatewaySession
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string? ClientIdentity { get; }
|
public string? ClientIdentity { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the API key identifier of the caller that created this session.
|
||||||
|
/// </summary>
|
||||||
|
public string? OwnerKeyId { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the client-supplied session name.
|
/// Gets the client-supplied session name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ public interface ISessionManager
|
|||||||
/// <summary>Opens a new gateway session and launches a worker process.</summary>
|
/// <summary>Opens a new gateway session and launches a worker process.</summary>
|
||||||
/// <param name="request">Request payload.</param>
|
/// <param name="request">Request payload.</param>
|
||||||
/// <param name="clientIdentity">Client identity string.</param>
|
/// <param name="clientIdentity">Client identity string.</param>
|
||||||
|
/// <param name="ownerKeyId">API key identifier of the caller creating the session.</param>
|
||||||
/// <param name="cancellationToken">Token to cancel the asynchronous operation.</param>
|
/// <param name="cancellationToken">Token to cancel the asynchronous operation.</param>
|
||||||
/// <returns>The newly opened session.</returns>
|
/// <returns>The newly opened session.</returns>
|
||||||
Task<GatewaySession> OpenSessionAsync(
|
Task<GatewaySession> OpenSessionAsync(
|
||||||
SessionOpenRequest request,
|
SessionOpenRequest request,
|
||||||
string? clientIdentity,
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId,
|
||||||
CancellationToken cancellationToken);
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>Attempts to retrieve a session by ID.</summary>
|
/// <summary>Attempts to retrieve a session by ID.</summary>
|
||||||
|
|||||||
@@ -58,11 +58,13 @@ public sealed class SessionManager : ISessionManager
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">Session open request.</param>
|
/// <param name="request">Session open request.</param>
|
||||||
/// <param name="clientIdentity">Client authentication identity.</param>
|
/// <param name="clientIdentity">Client authentication identity.</param>
|
||||||
|
/// <param name="ownerKeyId">API key identifier of the caller creating the session.</param>
|
||||||
/// <param name="cancellationToken">Cancellation token.</param>
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
/// <returns>Opened gateway session.</returns>
|
/// <returns>Opened gateway session.</returns>
|
||||||
public async Task<GatewaySession> OpenSessionAsync(
|
public async Task<GatewaySession> OpenSessionAsync(
|
||||||
SessionOpenRequest request,
|
SessionOpenRequest request,
|
||||||
string? clientIdentity,
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(request);
|
ArgumentNullException.ThrowIfNull(request);
|
||||||
@@ -72,7 +74,7 @@ public sealed class SessionManager : ISessionManager
|
|||||||
bool sessionOpenedRecorded = false;
|
bool sessionOpenedRecorded = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
session = CreateSession(request, clientIdentity);
|
session = CreateSession(request, clientIdentity, ownerKeyId);
|
||||||
if (!_registry.TryAdd(session))
|
if (!_registry.TryAdd(session))
|
||||||
{
|
{
|
||||||
throw new SessionManagerException(
|
throw new SessionManagerException(
|
||||||
@@ -420,7 +422,8 @@ public sealed class SessionManager : ISessionManager
|
|||||||
|
|
||||||
private GatewaySession CreateSession(
|
private GatewaySession CreateSession(
|
||||||
SessionOpenRequest request,
|
SessionOpenRequest request,
|
||||||
string? clientIdentity)
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId)
|
||||||
{
|
{
|
||||||
string sessionId = CreateSessionId();
|
string sessionId = CreateSessionId();
|
||||||
string backendName = string.IsNullOrWhiteSpace(request.RequestedBackend)
|
string backendName = string.IsNullOrWhiteSpace(request.RequestedBackend)
|
||||||
@@ -441,6 +444,7 @@ public sealed class SessionManager : ISessionManager
|
|||||||
pipeName,
|
pipeName,
|
||||||
nonce,
|
nonce,
|
||||||
clientIdentity,
|
clientIdentity,
|
||||||
|
ownerKeyId,
|
||||||
request.ClientSessionName,
|
request.ClientSessionName,
|
||||||
clientCorrelationId,
|
clientCorrelationId,
|
||||||
commandTimeout,
|
commandTimeout,
|
||||||
|
|||||||
@@ -410,6 +410,7 @@ public sealed class AlarmFailoverEndToEndTests
|
|||||||
public Task<GatewaySession> OpenSessionAsync(
|
public Task<GatewaySession> OpenSessionAsync(
|
||||||
SessionOpenRequest request,
|
SessionOpenRequest request,
|
||||||
string? clientIdentity,
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
GatewaySession session = new(
|
GatewaySession session = new(
|
||||||
|
|||||||
@@ -711,6 +711,7 @@ public sealed class GatewayAlarmMonitorProviderModeTests
|
|||||||
public Task<GatewaySession> OpenSessionAsync(
|
public Task<GatewaySession> OpenSessionAsync(
|
||||||
SessionOpenRequest request,
|
SessionOpenRequest request,
|
||||||
string? clientIdentity,
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
GatewaySession session = new(
|
GatewaySession session = new(
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ public sealed class DashboardSessionAdminServiceTests
|
|||||||
public Task<GatewaySession> OpenSessionAsync(
|
public Task<GatewaySession> OpenSessionAsync(
|
||||||
SessionOpenRequest request,
|
SessionOpenRequest request,
|
||||||
string? clientIdentity,
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
|
|||||||
@@ -471,6 +471,7 @@ public sealed class EventStreamServiceTests
|
|||||||
public Task<GatewaySession> OpenSessionAsync(
|
public Task<GatewaySession> OpenSessionAsync(
|
||||||
SessionOpenRequest request,
|
SessionOpenRequest request,
|
||||||
string? clientIdentity,
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.FromResult(_sessions.Values.First());
|
return Task.FromResult(_sessions.Values.First());
|
||||||
|
|||||||
@@ -884,10 +884,12 @@ public sealed class MxAccessGatewayServiceConstraintTests
|
|||||||
/// <summary>Opens a test session asynchronously.</summary>
|
/// <summary>Opens a test session asynchronously.</summary>
|
||||||
/// <param name="request">The session open request.</param>
|
/// <param name="request">The session open request.</param>
|
||||||
/// <param name="clientIdentity">The client identity, if any.</param>
|
/// <param name="clientIdentity">The client identity, if any.</param>
|
||||||
|
/// <param name="ownerKeyId">The API key identifier of the caller, if any.</param>
|
||||||
/// <param name="cancellationToken">Token to observe for cancellation.</param>
|
/// <param name="cancellationToken">Token to observe for cancellation.</param>
|
||||||
public Task<GatewaySession> OpenSessionAsync(
|
public Task<GatewaySession> OpenSessionAsync(
|
||||||
SessionOpenRequest request,
|
SessionOpenRequest request,
|
||||||
string? clientIdentity,
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId,
|
||||||
CancellationToken cancellationToken) =>
|
CancellationToken cancellationToken) =>
|
||||||
Task.FromResult(seededSessions.Values.First());
|
Task.FromResult(seededSessions.Values.First());
|
||||||
|
|
||||||
|
|||||||
@@ -545,6 +545,7 @@ public sealed class MxAccessGatewayServiceTests
|
|||||||
public Task<GatewaySession> OpenSessionAsync(
|
public Task<GatewaySession> OpenSessionAsync(
|
||||||
SessionOpenRequest request,
|
SessionOpenRequest request,
|
||||||
string? clientIdentity,
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
LastOpenRequest = request;
|
LastOpenRequest = request;
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ public sealed class GatewaySessionTests
|
|||||||
pipeName: "mxaccess-gateway-1-session-test",
|
pipeName: "mxaccess-gateway-1-session-test",
|
||||||
nonce: "nonce",
|
nonce: "nonce",
|
||||||
clientIdentity: "client-1",
|
clientIdentity: "client-1",
|
||||||
|
ownerKeyId: null,
|
||||||
clientSessionName: "test-session",
|
clientSessionName: "test-session",
|
||||||
clientCorrelationId: "client-correlation-1",
|
clientCorrelationId: "client-correlation-1",
|
||||||
commandTimeout: TimeSpan.FromSeconds(5),
|
commandTimeout: TimeSpan.FromSeconds(5),
|
||||||
|
|||||||
@@ -663,7 +663,7 @@ public sealed class SessionManagerBulkTests
|
|||||||
private static async Task<GatewaySession> OpenSessionAsync(IWorkerClient workerClient)
|
private static async Task<GatewaySession> OpenSessionAsync(IWorkerClient workerClient)
|
||||||
{
|
{
|
||||||
SessionManager manager = CreateManager(workerClient);
|
SessionManager manager = CreateManager(workerClient);
|
||||||
return await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
return await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SessionManager CreateManager(IWorkerClient workerClient)
|
private static SessionManager CreateManager(IWorkerClient workerClient)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public sealed class SessionManagerTests
|
|||||||
using GatewayMetrics metrics = new();
|
using GatewayMetrics metrics = new();
|
||||||
SessionManager manager = CreateManager(factory, metrics: metrics);
|
SessionManager manager = CreateManager(factory, metrics: metrics);
|
||||||
|
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
Assert.True(manager.TryGetSession(session.SessionId, out GatewaySession? registered));
|
Assert.True(manager.TryGetSession(session.SessionId, out GatewaySession? registered));
|
||||||
Assert.Same(session, registered);
|
Assert.Same(session, registered);
|
||||||
@@ -34,6 +34,36 @@ public sealed class SessionManagerTests
|
|||||||
Assert.Equal(1, metrics.GetSnapshot().SessionsOpened);
|
Assert.Equal(1, metrics.GetSnapshot().SessionsOpened);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that a session opened by an authenticated caller records that caller's API key id in OwnerKeyId.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task OpenSessionAsync_WithOwnerKeyId_RecordsOwnerKeyIdOnSession()
|
||||||
|
{
|
||||||
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(new FakeWorkerClient()));
|
||||||
|
|
||||||
|
GatewaySession session = await manager.OpenSessionAsync(
|
||||||
|
CreateOpenRequest(),
|
||||||
|
clientIdentity: "MyKey Display",
|
||||||
|
ownerKeyId: "key-abc123",
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal("key-abc123", session.OwnerKeyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that a session opened without an owner key id records null in OwnerKeyId.</summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task OpenSessionAsync_WithNullOwnerKeyId_RecordsNullOwnerKeyIdOnSession()
|
||||||
|
{
|
||||||
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(new FakeWorkerClient()));
|
||||||
|
|
||||||
|
GatewaySession session = await manager.OpenSessionAsync(
|
||||||
|
CreateOpenRequest(),
|
||||||
|
clientIdentity: null,
|
||||||
|
ownerKeyId: null,
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Null(session.OwnerKeyId);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Verifies that opening a session sets the initial lease expiry from the configured default lease.</summary>
|
/// <summary>Verifies that opening a session sets the initial lease expiry from the configured default lease.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task OpenSessionAsync_SetsInitialDefaultLease()
|
public async Task OpenSessionAsync_SetsInitialDefaultLease()
|
||||||
@@ -45,7 +75,7 @@ public sealed class SessionManagerTests
|
|||||||
options: options,
|
options: options,
|
||||||
timeProvider: clock);
|
timeProvider: clock);
|
||||||
|
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(clock.GetUtcNow() + TimeSpan.FromMinutes(30), session.LeaseExpiresAt);
|
Assert.Equal(clock.GetUtcNow() + TimeSpan.FromMinutes(30), session.LeaseExpiresAt);
|
||||||
}
|
}
|
||||||
@@ -61,7 +91,7 @@ public sealed class SessionManagerTests
|
|||||||
};
|
};
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(new FakeWorkerClient()));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(new FakeWorkerClient()));
|
||||||
|
|
||||||
GatewaySession session = await manager.OpenSessionAsync(request, "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(request, "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal($"rust-load-client-{session.SessionId}", session.ClientCorrelationId);
|
Assert.Equal($"rust-load-client-{session.SessionId}", session.ClientCorrelationId);
|
||||||
}
|
}
|
||||||
@@ -76,7 +106,7 @@ public sealed class SessionManagerTests
|
|||||||
};
|
};
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(new FakeWorkerClient()));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(new FakeWorkerClient()));
|
||||||
|
|
||||||
GatewaySession session = await manager.OpenSessionAsync(request, "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(request, "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal($"client-{session.SessionId}", session.ClientCorrelationId);
|
Assert.Equal($"client-{session.SessionId}", session.ClientCorrelationId);
|
||||||
}
|
}
|
||||||
@@ -87,7 +117,7 @@ public sealed class SessionManagerTests
|
|||||||
{
|
{
|
||||||
FakeWorkerClient workerClient = new();
|
FakeWorkerClient workerClient = new();
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
WorkerCommandReply reply = await manager.InvokeAsync(
|
WorkerCommandReply reply = await manager.InvokeAsync(
|
||||||
session.SessionId,
|
session.SessionId,
|
||||||
@@ -108,6 +138,7 @@ public sealed class SessionManagerTests
|
|||||||
"mxaccess-gateway-1-session-lease-refresh",
|
"mxaccess-gateway-1-session-lease-refresh",
|
||||||
"nonce",
|
"nonce",
|
||||||
"client-1",
|
"client-1",
|
||||||
|
null,
|
||||||
"test-session",
|
"test-session",
|
||||||
"client-correlation-1",
|
"client-correlation-1",
|
||||||
TimeSpan.FromSeconds(30),
|
TimeSpan.FromSeconds(30),
|
||||||
@@ -156,7 +187,7 @@ public sealed class SessionManagerTests
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
IReadOnlyList<SubscribeResult> results = await session.SubscribeBulkAsync(
|
IReadOnlyList<SubscribeResult> results = await session.SubscribeBulkAsync(
|
||||||
12,
|
12,
|
||||||
@@ -207,7 +238,7 @@ public sealed class SessionManagerTests
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
IReadOnlyList<BulkWriteResult> results = await session.WriteBulkAsync(
|
IReadOnlyList<BulkWriteResult> results = await session.WriteBulkAsync(
|
||||||
12,
|
12,
|
||||||
@@ -268,7 +299,7 @@ public sealed class SessionManagerTests
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
IReadOnlyList<BulkReadResult> results = await session.ReadBulkAsync(
|
IReadOnlyList<BulkReadResult> results = await session.ReadBulkAsync(
|
||||||
12,
|
12,
|
||||||
@@ -291,7 +322,7 @@ public sealed class SessionManagerTests
|
|||||||
{
|
{
|
||||||
FakeWorkerClient workerClient = new();
|
FakeWorkerClient workerClient = new();
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
session.MarkFaulted("test fault");
|
session.MarkFaulted("test fault");
|
||||||
|
|
||||||
SessionManagerException exception = await Assert.ThrowsAsync<SessionManagerException>(
|
SessionManagerException exception = await Assert.ThrowsAsync<SessionManagerException>(
|
||||||
@@ -316,7 +347,7 @@ public sealed class SessionManagerTests
|
|||||||
{
|
{
|
||||||
FakeWorkerClient workerClient = new();
|
FakeWorkerClient workerClient = new();
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
// Force a state mismatch: session stays Ready, worker transitions out.
|
// Force a state mismatch: session stays Ready, worker transitions out.
|
||||||
workerClient.State = WorkerClientState.Handshaking;
|
workerClient.State = WorkerClientState.Handshaking;
|
||||||
@@ -341,7 +372,7 @@ public sealed class SessionManagerTests
|
|||||||
FakeWorkerClient workerClient = new();
|
FakeWorkerClient workerClient = new();
|
||||||
using GatewayMetrics metrics = new();
|
using GatewayMetrics metrics = new();
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient), metrics: metrics);
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient), metrics: metrics);
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
SessionCloseResult firstClose = await manager.CloseSessionAsync(session.SessionId, CancellationToken.None);
|
SessionCloseResult firstClose = await manager.CloseSessionAsync(session.SessionId, CancellationToken.None);
|
||||||
SessionManagerException secondClose = await Assert.ThrowsAsync<SessionManagerException>(
|
SessionManagerException secondClose = await Assert.ThrowsAsync<SessionManagerException>(
|
||||||
@@ -366,7 +397,7 @@ public sealed class SessionManagerTests
|
|||||||
"Worker shutdown timed out."),
|
"Worker shutdown timed out."),
|
||||||
};
|
};
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
SessionManagerException exception = await Assert.ThrowsAsync<SessionManagerException>(
|
SessionManagerException exception = await Assert.ThrowsAsync<SessionManagerException>(
|
||||||
async () => await manager.CloseSessionAsync(session.SessionId, CancellationToken.None));
|
async () => await manager.CloseSessionAsync(session.SessionId, CancellationToken.None));
|
||||||
@@ -397,6 +428,7 @@ public sealed class SessionManagerTests
|
|||||||
GatewaySession firstSession = await manager.OpenSessionAsync(
|
GatewaySession firstSession = await manager.OpenSessionAsync(
|
||||||
CreateOpenRequest(),
|
CreateOpenRequest(),
|
||||||
"client-1",
|
"client-1",
|
||||||
|
ownerKeyId: null,
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
metrics.EventReceived(firstSession.SessionId, MxEventFamily.OnDataChange.ToString());
|
metrics.EventReceived(firstSession.SessionId, MxEventFamily.OnDataChange.ToString());
|
||||||
|
|
||||||
@@ -405,6 +437,7 @@ public sealed class SessionManagerTests
|
|||||||
GatewaySession secondSession = await manager.OpenSessionAsync(
|
GatewaySession secondSession = await manager.OpenSessionAsync(
|
||||||
CreateOpenRequest(),
|
CreateOpenRequest(),
|
||||||
"client-2",
|
"client-2",
|
||||||
|
ownerKeyId: null,
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(SessionManagerErrorCode.CloseFailed, exception.ErrorCode);
|
Assert.Equal(SessionManagerErrorCode.CloseFailed, exception.ErrorCode);
|
||||||
@@ -440,6 +473,7 @@ public sealed class SessionManagerTests
|
|||||||
GatewaySession session = await manager.OpenSessionAsync(
|
GatewaySession session = await manager.OpenSessionAsync(
|
||||||
CreateOpenRequest(),
|
CreateOpenRequest(),
|
||||||
"client-1",
|
"client-1",
|
||||||
|
ownerKeyId: null,
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
Task<SessionCloseResult> firstClose = manager.CloseSessionAsync(session.SessionId, CancellationToken.None);
|
Task<SessionCloseResult> firstClose = manager.CloseSessionAsync(session.SessionId, CancellationToken.None);
|
||||||
@@ -482,7 +516,7 @@ public sealed class SessionManagerTests
|
|||||||
FakeWorkerClient workerClient = new();
|
FakeWorkerClient workerClient = new();
|
||||||
using GatewayMetrics metrics = new();
|
using GatewayMetrics metrics = new();
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient), metrics: metrics);
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient), metrics: metrics);
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
SessionCloseResult result = await manager.KillWorkerAsync(session.SessionId, "test-kill", CancellationToken.None);
|
SessionCloseResult result = await manager.KillWorkerAsync(session.SessionId, "test-kill", CancellationToken.None);
|
||||||
|
|
||||||
@@ -510,7 +544,7 @@ public sealed class SessionManagerTests
|
|||||||
{
|
{
|
||||||
FakeWorkerClient workerClient = new();
|
FakeWorkerClient workerClient = new();
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
await Assert.ThrowsAsync<ArgumentException>(
|
await Assert.ThrowsAsync<ArgumentException>(
|
||||||
async () => await manager.KillWorkerAsync(session.SessionId, blankReason, CancellationToken.None));
|
async () => await manager.KillWorkerAsync(session.SessionId, blankReason, CancellationToken.None));
|
||||||
@@ -529,7 +563,7 @@ public sealed class SessionManagerTests
|
|||||||
{
|
{
|
||||||
FakeWorkerClient workerClient = new();
|
FakeWorkerClient workerClient = new();
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>(
|
await Assert.ThrowsAsync<ArgumentNullException>(
|
||||||
async () => await manager.KillWorkerAsync(session.SessionId, null!, CancellationToken.None));
|
async () => await manager.KillWorkerAsync(session.SessionId, null!, CancellationToken.None));
|
||||||
@@ -569,6 +603,7 @@ public sealed class SessionManagerTests
|
|||||||
GatewaySession session = await manager.OpenSessionAsync(
|
GatewaySession session = await manager.OpenSessionAsync(
|
||||||
CreateOpenRequest(),
|
CreateOpenRequest(),
|
||||||
"client-1",
|
"client-1",
|
||||||
|
ownerKeyId: null,
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(1, metrics.GetSnapshot().OpenSessions);
|
Assert.Equal(1, metrics.GetSnapshot().OpenSessions);
|
||||||
@@ -598,6 +633,7 @@ public sealed class SessionManagerTests
|
|||||||
GatewaySession session = await manager.OpenSessionAsync(
|
GatewaySession session = await manager.OpenSessionAsync(
|
||||||
CreateOpenRequest(),
|
CreateOpenRequest(),
|
||||||
"client-1",
|
"client-1",
|
||||||
|
ownerKeyId: null,
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
Task<SessionCloseResult> first = manager.KillWorkerAsync(session.SessionId, "kill-a", CancellationToken.None);
|
Task<SessionCloseResult> first = manager.KillWorkerAsync(session.SessionId, "kill-a", CancellationToken.None);
|
||||||
@@ -641,6 +677,7 @@ public sealed class SessionManagerTests
|
|||||||
GatewaySession session = await manager.OpenSessionAsync(
|
GatewaySession session = await manager.OpenSessionAsync(
|
||||||
CreateOpenRequest(),
|
CreateOpenRequest(),
|
||||||
"client-1",
|
"client-1",
|
||||||
|
ownerKeyId: null,
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(1, metrics.GetSnapshot().OpenSessions);
|
Assert.Equal(1, metrics.GetSnapshot().OpenSessions);
|
||||||
@@ -666,7 +703,7 @@ public sealed class SessionManagerTests
|
|||||||
metrics);
|
metrics);
|
||||||
|
|
||||||
SessionManagerException exception = await Assert.ThrowsAsync<SessionManagerException>(
|
SessionManagerException exception = await Assert.ThrowsAsync<SessionManagerException>(
|
||||||
async () => await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None));
|
async () => await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None));
|
||||||
|
|
||||||
Assert.Equal(SessionManagerErrorCode.OpenFailed, exception.ErrorCode);
|
Assert.Equal(SessionManagerErrorCode.OpenFailed, exception.ErrorCode);
|
||||||
Assert.Equal(0, registry.Count);
|
Assert.Equal(0, registry.Count);
|
||||||
@@ -682,8 +719,8 @@ public sealed class SessionManagerTests
|
|||||||
FakeWorkerClient activeClient = new();
|
FakeWorkerClient activeClient = new();
|
||||||
QueueingSessionWorkerClientFactory factory = new(expiredClient, activeClient);
|
QueueingSessionWorkerClientFactory factory = new(expiredClient, activeClient);
|
||||||
SessionManager manager = CreateManager(factory);
|
SessionManager manager = CreateManager(factory);
|
||||||
GatewaySession expiredSession = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession expiredSession = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
GatewaySession activeSession = await manager.OpenSessionAsync(CreateOpenRequest(), "client-2", CancellationToken.None);
|
GatewaySession activeSession = await manager.OpenSessionAsync(CreateOpenRequest(), "client-2", ownerKeyId: null, CancellationToken.None);
|
||||||
DateTimeOffset now = DateTimeOffset.UtcNow;
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
expiredSession.ExtendLease(now.AddSeconds(-1));
|
expiredSession.ExtendLease(now.AddSeconds(-1));
|
||||||
activeSession.ExtendLease(now.AddMinutes(5));
|
activeSession.ExtendLease(now.AddMinutes(5));
|
||||||
@@ -703,7 +740,7 @@ public sealed class SessionManagerTests
|
|||||||
{
|
{
|
||||||
FakeWorkerClient workerClient = new();
|
FakeWorkerClient workerClient = new();
|
||||||
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
SessionManager manager = CreateManager(new FakeSessionWorkerClientFactory(workerClient));
|
||||||
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession session = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
DateTimeOffset now = DateTimeOffset.UtcNow;
|
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||||
session.ExtendLease(now.AddSeconds(-1));
|
session.ExtendLease(now.AddSeconds(-1));
|
||||||
using IDisposable eventSubscriber = session.AttachEventSubscriber(allowMultipleSubscribers: false);
|
using IDisposable eventSubscriber = session.AttachEventSubscriber(allowMultipleSubscribers: false);
|
||||||
@@ -724,8 +761,8 @@ public sealed class SessionManagerTests
|
|||||||
QueueingSessionWorkerClientFactory factory = new(firstClient, secondClient);
|
QueueingSessionWorkerClientFactory factory = new(firstClient, secondClient);
|
||||||
using GatewayMetrics metrics = new();
|
using GatewayMetrics metrics = new();
|
||||||
SessionManager manager = CreateManager(factory, metrics: metrics);
|
SessionManager manager = CreateManager(factory, metrics: metrics);
|
||||||
GatewaySession firstSession = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", CancellationToken.None);
|
GatewaySession firstSession = await manager.OpenSessionAsync(CreateOpenRequest(), "client-1", ownerKeyId: null, CancellationToken.None);
|
||||||
GatewaySession secondSession = await manager.OpenSessionAsync(CreateOpenRequest(), "client-2", CancellationToken.None);
|
GatewaySession secondSession = await manager.OpenSessionAsync(CreateOpenRequest(), "client-2", ownerKeyId: null, CancellationToken.None);
|
||||||
|
|
||||||
await manager.ShutdownAsync(CancellationToken.None);
|
await manager.ShutdownAsync(CancellationToken.None);
|
||||||
|
|
||||||
|
|||||||
+1
@@ -416,6 +416,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests
|
|||||||
public Task<GatewaySession> OpenSessionAsync(
|
public Task<GatewaySession> OpenSessionAsync(
|
||||||
SessionOpenRequest request,
|
SessionOpenRequest request,
|
||||||
string? clientIdentity,
|
string? clientIdentity,
|
||||||
|
string? ownerKeyId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
OpenSessionCount++;
|
OpenSessionCount++;
|
||||||
|
|||||||
Reference in New Issue
Block a user