70 lines
3.0 KiB
C#
70 lines
3.0 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NSubstitute;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.InboundAPI.Tests;
|
|
|
|
/// <summary>Tests for <see cref="InboundMethodWarmupService"/> (review 2026-10-21 05/U3).</summary>
|
|
public class InboundMethodWarmupServiceTests
|
|
{
|
|
private sealed class Lifetime : IHostApplicationLifetime
|
|
{
|
|
public CancellationTokenSource StartedCts { get; } = new();
|
|
public CancellationToken ApplicationStarted => StartedCts.Token;
|
|
public CancellationToken ApplicationStopping => CancellationToken.None;
|
|
public CancellationToken ApplicationStopped => CancellationToken.None;
|
|
public void StopApplication() { }
|
|
}
|
|
|
|
private sealed class Gate(bool active) : IActiveNodeGate
|
|
{
|
|
public bool IsActiveNode => active;
|
|
}
|
|
|
|
private static (InboundMethodWarmupService svc, InboundScriptExecutor exec, Lifetime life) Build(bool active)
|
|
{
|
|
var repo = Substitute.For<IInboundApiRepository>();
|
|
repo.GetAllApiMethodsAsync(Arg.Any<CancellationToken>())
|
|
.Returns(new List<ApiMethod> { new("warm", "return 1;") { Id = 1, TimeoutSeconds = 10 } });
|
|
var provider = new ServiceCollection().AddSingleton(repo).BuildServiceProvider();
|
|
var exec = new InboundScriptExecutor(NullLogger<InboundScriptExecutor>.Instance, Substitute.For<IServiceProvider>());
|
|
var life = new Lifetime();
|
|
var svc = new InboundMethodWarmupService(
|
|
provider.GetRequiredService<IServiceScopeFactory>(), exec, life,
|
|
NullLogger<InboundMethodWarmupService>.Instance, new Gate(active));
|
|
return (svc, exec, life);
|
|
}
|
|
|
|
/// <summary>Verifies the warm-up waits for ApplicationStarted and then compiles on an active node.</summary>
|
|
/// <returns>A task that represents the asynchronous operation.</returns>
|
|
[Fact]
|
|
public async Task ActiveNode_CompilesAfterApplicationStarted_NotBefore()
|
|
{
|
|
var (svc, exec, life) = Build(active: true);
|
|
await svc.StartAsync(CancellationToken.None);
|
|
await Task.Delay(200);
|
|
Assert.False(exec.HasHandler("warm"));
|
|
|
|
life.StartedCts.Cancel();
|
|
for (var i = 0; i < 100 && !exec.HasHandler("warm"); i++) await Task.Delay(50);
|
|
Assert.True(exec.HasHandler("warm"));
|
|
await svc.StopAsync(CancellationToken.None);
|
|
}
|
|
|
|
/// <summary>Verifies a standby node does not warm up.</summary>
|
|
/// <returns>A task that represents the asynchronous operation.</returns>
|
|
[Fact]
|
|
public async Task StandbyNode_DoesNotCompile()
|
|
{
|
|
var (svc, exec, life) = Build(active: false);
|
|
await svc.StartAsync(CancellationToken.None);
|
|
life.StartedCts.Cancel();
|
|
await Task.Delay(500);
|
|
Assert.False(exec.HasHandler("warm"));
|
|
await svc.StopAsync(CancellationToken.None);
|
|
}
|
|
}
|