81 lines
3.4 KiB
C#
81 lines
3.4 KiB
C#
using Akka.Actor;
|
|
using Akka.TestKit.Xunit2;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NSubstitute;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.ManagementService.Tests;
|
|
|
|
/// <summary>
|
|
/// Review 2026-10-21 03/R-1: a malformed site gRPC address used to be persisted
|
|
/// unvalidated and then throw inside <c>SitePairChannelProvider.UpdateSite</c>.
|
|
/// Create/Update must reject it at the write boundary with a readable error.
|
|
/// </summary>
|
|
public class SiteGrpcEndpointValidationTests : TestKit
|
|
{
|
|
private readonly ISiteRepository _siteRepo = Substitute.For<ISiteRepository>();
|
|
private readonly ServiceCollection _services;
|
|
|
|
/// <summary>Initializes the harness.</summary>
|
|
public SiteGrpcEndpointValidationTests()
|
|
{
|
|
_services = new ServiceCollection().AddTestConfigurationTransaction();
|
|
_services.AddScoped(_ => _siteRepo);
|
|
_services.AddScoped(_ => Substitute.For<IAuditService>());
|
|
_siteRepo.GetSiteByIdAsync(7, Arg.Any<CancellationToken>())
|
|
.Returns(new Site("S", "S1") { Id = 7 });
|
|
}
|
|
|
|
private IActorRef CreateActor() =>
|
|
Sys.ActorOf(Props.Create(() => new ManagementActor(
|
|
_services.BuildServiceProvider(), NullLogger<ManagementActor>.Instance)));
|
|
|
|
private static ManagementEnvelope Admin(object command) =>
|
|
new(new AuthenticatedUser("admin", "Admin", new[] { "Administrator" }, Array.Empty<string>()),
|
|
command, Guid.NewGuid().ToString("N"));
|
|
|
|
/// <summary>Create with a scheme-less node-A address is rejected and nothing is saved.</summary>
|
|
[Theory]
|
|
[InlineData("scadabridge-site-a-node-a")]
|
|
[InlineData("site-a-node-a:8083")]
|
|
[InlineData("ftp://host:8083")]
|
|
public void CreateSite_MalformedGrpcAddress_ReturnsReadableError(string bad)
|
|
{
|
|
var actor = CreateActor();
|
|
actor.Tell(Admin(new CreateSiteCommand("S", "S1", null, GrpcNodeAAddress: bad)));
|
|
|
|
var error = ExpectMsg<ManagementError>(TimeSpan.FromSeconds(5));
|
|
Assert.Contains("GrpcNodeAAddress", error.Error);
|
|
Assert.Contains("http://", error.Error);
|
|
_siteRepo.DidNotReceive().AddSiteAsync(Arg.Any<Site>(), Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
/// <summary>Update with a malformed node-B address is rejected and nothing is saved.</summary>
|
|
[Fact]
|
|
public void UpdateSite_MalformedGrpcNodeB_ReturnsReadableError()
|
|
{
|
|
var actor = CreateActor();
|
|
actor.Tell(Admin(new UpdateSiteCommand(7, "S", null,
|
|
GrpcNodeAAddress: "http://a:8083", GrpcNodeBAddress: "b:8083")));
|
|
|
|
var error = ExpectMsg<ManagementError>(TimeSpan.FromSeconds(5));
|
|
Assert.Contains("GrpcNodeBAddress", error.Error);
|
|
_siteRepo.DidNotReceive().UpdateSiteAsync(Arg.Any<Site>(), Arg.Any<CancellationToken>());
|
|
}
|
|
|
|
/// <summary>Both gRPC addresses are optional; valid http(s) URLs (and blanks) are accepted.</summary>
|
|
[Fact]
|
|
public void CreateSite_ValidOrEmptyGrpcAddresses_Succeeds()
|
|
{
|
|
var actor = CreateActor();
|
|
actor.Tell(Admin(new CreateSiteCommand("S", "S1", null,
|
|
GrpcNodeAAddress: "http://site-a-node-a:8083", GrpcNodeBAddress: null)));
|
|
|
|
ExpectMsg<ManagementSuccess>(TimeSpan.FromSeconds(5));
|
|
}
|
|
}
|