fix(management-service): resolve ManagementService-005,008,010,011 — supervision strategy, configured command timeout, remove stale ResolveRoles path; ManagementService-012 deferred

This commit is contained in:
Joseph Doherty
2026-05-16 22:24:03 -04:00
parent 858fe24add
commit dab0056d1b
6 changed files with 200 additions and 32 deletions

View File

@@ -0,0 +1,39 @@
using Akka.Actor;
using ScadaLink.ManagementService;
namespace ScadaLink.ManagementService.Tests;
/// <summary>
/// Tests for the <see cref="ManagementActor"/> supervision strategy
/// (finding ManagementService-005). The project convention is that long-lived
/// coordinator-style actors declare an explicit Resume-based strategy.
/// </summary>
public class ManagementActorSupervisionTests
{
[Fact]
public void CreateSupervisorStrategy_ReturnsOneForOneStrategy()
{
var strategy = ManagementActor.CreateSupervisorStrategy();
Assert.IsType<OneForOneStrategy>(strategy);
}
[Fact]
public void CreateSupervisorStrategy_ResumesOnArbitraryException()
{
var strategy = (OneForOneStrategy)ManagementActor.CreateSupervisorStrategy();
var directive = strategy.Decider.Decide(new InvalidOperationException("boom"));
Assert.Equal(Directive.Resume, directive);
}
[Fact]
public void CreateSupervisorStrategy_ResumesIndefinitely()
{
var strategy = (OneForOneStrategy)ManagementActor.CreateSupervisorStrategy();
// Coordinator actors should not give up: unbounded retries.
Assert.Equal(-1, strategy.MaxNumberOfRetries);
}
}

View File

@@ -715,6 +715,33 @@ public class ManagementActorTests : TestKit, IDisposable
// continuation rather than escaping or being silently dropped.
// ========================================================================
// ========================================================================
// ResolveRolesCommand dead-code removal (finding ManagementService-011 / -008)
//
// The two-step ResolveRoles + command flow is retired: the HTTP endpoint does
// LDAP auth and role resolution itself. The actor must no longer dispatch
// ResolveRolesCommand — a stray ClusterClient sender hitting it gets a uniform
// ManagementError rather than an unauthenticated role-mapping enumeration.
// ========================================================================
[Fact]
public void ResolveRolesCommand_IsNoLongerDispatched_ReturnsManagementError()
{
var secRepo = Substitute.For<ISecurityRepository>();
_services.AddScoped(_ => secRepo);
var actor = CreateActor();
var envelope = Envelope(new ResolveRolesCommand(new[] { "cn=admins" }));
actor.Tell(envelope);
var response = ExpectMsg<ManagementError>(TimeSpan.FromSeconds(5));
Assert.Equal(envelope.CorrelationId, response.CorrelationId);
Assert.Equal("COMMAND_FAILED", response.ErrorCode);
// No role-mapping data is enumerated/leaked back to the caller.
secRepo.DidNotReceiveWithAnyArgs().GetAllMappingsAsync(default);
}
[Fact]
public void UnknownCommandType_FaultMappedToManagementError()
{

View File

@@ -62,4 +62,41 @@ public class ManagementEndpointsTests
Assert.False(result.Success);
Assert.Equal("BAD_REQUEST", result.ErrorCode);
}
// ========================================================================
// Command-timeout configuration (finding ManagementService-010)
//
// ManagementServiceOptions.CommandTimeout must actually drive the Ask
// timeout instead of a hard-coded 30s constant.
// ========================================================================
[Fact]
public void ResolveAskTimeout_UsesConfiguredCommandTimeout()
{
var options = new ManagementServiceOptions { CommandTimeout = TimeSpan.FromSeconds(75) };
var timeout = ManagementEndpoints.ResolveAskTimeout(options);
Assert.Equal(TimeSpan.FromSeconds(75), timeout);
}
[Fact]
public void ResolveAskTimeout_WithNullOptions_FallsBackToDefault()
{
var timeout = ManagementEndpoints.ResolveAskTimeout(null);
Assert.Equal(TimeSpan.FromSeconds(30), timeout);
}
[Fact]
public void ResolveAskTimeout_WithNonPositiveTimeout_FallsBackToDefault()
{
// A misconfigured zero/negative timeout would make every Ask fail
// immediately; fall back to the safe default instead.
var options = new ManagementServiceOptions { CommandTimeout = TimeSpan.Zero };
var timeout = ManagementEndpoints.ResolveAskTimeout(options);
Assert.Equal(TimeSpan.FromSeconds(30), timeout);
}
}