Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/ActorNameValidationTests.cs
T

91 lines
4.0 KiB
C#

using Moq;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
using ZB.MOM.WW.ScadaBridge.TemplateEngine.Services;
namespace ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests;
/// <summary>
/// Review 2026-10-21 01/SR-C1 (central half): names that become Akka actor names at
/// the site — the instance unique name (the Instance Actor), and the script / alarm /
/// composition-slot names that make up the <c>script-</c>/<c>alarm-</c>/
/// <c>native-alarm-</c> child canonical names — are rejected at save with a readable
/// error, instead of crashing the site's Deployment Manager / failing the deploy when
/// <c>ActorOf</c> throws <c>InvalidActorNameException</c>.
/// </summary>
public class ActorNameValidationTests
{
private readonly Mock<ITemplateEngineRepository> _repoMock = new();
private readonly Mock<IAuditService> _auditMock = new();
private TemplateService TemplateSut => new(_repoMock.Object, _auditMock.Object);
/// <summary>Verifies that an instance unique name with invalid actor-name characters is rejected.</summary>
/// <param name="name">An instance name Akka cannot use as an actor name.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
[Theory]
[InlineData("Furnace #3")]
[InlineData("Line 1")]
[InlineData("Ofen Süd")]
[InlineData("a/b")]
[InlineData("$system")]
public async Task CreateInstance_InvalidActorName_FailsBeforeAnyWrite(string name)
{
var sut = new InstanceService(_repoMock.Object, _auditMock.Object);
var result = await sut.CreateInstanceAsync(name, 1, 1, null, "admin");
Assert.True(result.IsFailure);
Assert.Contains("not allowed", result.Error);
_repoMock.Verify(r => r.AddInstanceAsync(It.IsAny<Instance>(), It.IsAny<CancellationToken>()), Times.Never);
}
/// <summary>Verifies that a script name with invalid actor-name characters is rejected.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task AddScript_InvalidActorName_Fails()
{
var result = await TemplateSut.AddScriptAsync(1, new TemplateScript("Calc #1", "return 1;"), "admin");
Assert.True(result.IsFailure);
Assert.Contains("Calc #1", result.Error);
_repoMock.Verify(r => r.AddTemplateScriptAsync(It.IsAny<TemplateScript>(), It.IsAny<CancellationToken>()), Times.Never);
}
/// <summary>Verifies that an alarm name with invalid actor-name characters is rejected.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task AddAlarm_InvalidActorName_Fails()
{
var result = await TemplateSut.AddAlarmAsync(1, new TemplateAlarm("High Temp"), "admin");
Assert.True(result.IsFailure);
Assert.Contains("High Temp", result.Error);
_repoMock.Verify(r => r.AddTemplateAlarmAsync(It.IsAny<TemplateAlarm>(), It.IsAny<CancellationToken>()), Times.Never);
}
/// <summary>Verifies that a composition slot name with invalid actor-name characters is rejected.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task AddComposition_InvalidActorName_Fails()
{
var result = await TemplateSut.AddCompositionAsync(1, 2, "Left Sensor", "admin");
Assert.True(result.IsFailure);
Assert.Contains("Left Sensor", result.Error);
}
/// <summary>Verifies that renaming a composition slot to an invalid actor name is rejected.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task RenameComposition_InvalidActorName_Fails()
{
var result = await TemplateSut.RenameCompositionAsync(50, "new slot", "admin");
Assert.True(result.IsFailure);
Assert.Contains("new slot", result.Error);
}
}