using Akka.Actor;
using Microsoft.EntityFrameworkCore;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Admin;
using ZB.MOM.WW.OtOpcUa.Commons.Types;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.ControlPlane.AdminOperations;
using ZB.MOM.WW.OtOpcUa.ControlPlane.Tests.Harness;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Tests;
///
/// R03:4.1 — admin operations echoed ex.Message to the UI, so a SqlException's connection-string
/// fragment reached any ConfigEditor browser session. The reply is now a fixed sentence plus the request's
/// correlation id; the detail stays in the central log.
///
public sealed class AdminOperationsActorServerFaultMessageTests : ControlPlaneActorTestBase
{
private const string Secret = "Server=sql-prod-01;Password=hunter2";
[Fact]
public void StartDeployment_server_fault_reply_carries_the_correlation_id_and_not_the_exception_text()
{
var coordinator = CreateTestProbe("coord");
var actor = Sys.ActorOf(AdminOperationsActor.Props(new ThrowingDbFactory(), coordinator.Ref, Enumerable.Empty()));
var correlationId = CorrelationId.NewId();
actor.Tell(new StartDeployment("joe", correlationId));
var reply = ExpectMsg(PresenceBudget);
reply.Outcome.ShouldBe(StartDeploymentOutcome.Rejected);
reply.Message.ShouldNotBeNull();
reply.Message.ShouldNotContain("hunter2");
reply.Message.ShouldNotContain("sql-prod-01");
reply.Message.ShouldContain(correlationId.ToString()); // the operator can find the log line
}
[Fact]
public void TestDriverConnect_probe_throw_reply_carries_the_correlation_id_and_not_the_exception_text()
{
var coordinator = CreateTestProbe("coord");
var actor = Sys.ActorOf(AdminOperationsActor.Props(NewInMemoryDbFactory(), coordinator.Ref, new IDriverProbe[] { new ThrowingProbe() }));
var correlationId = Guid.NewGuid();
actor.Tell(new TestDriverConnect("Throwing", "{}", 5, correlationId));
var reply = ExpectMsg(PresenceBudget);
reply.Ok.ShouldBeFalse();
reply.Message.ShouldNotBeNull();
reply.Message.ShouldNotContain("hunter2");
reply.Message.ShouldContain(correlationId.ToString());
}
private sealed class ThrowingDbFactory : IDbContextFactory
{
public OtOpcUaConfigDbContext CreateDbContext() =>
throw new InvalidOperationException("Cannot open database: " + Secret);
}
private sealed class ThrowingProbe : IDriverProbe
{
public string DriverType => "Throwing";
public Task ProbeAsync(string configJson, TimeSpan timeout, CancellationToken ct) =>
throw new InvalidOperationException("probe blew up: " + Secret);
}
}