feat(historian): config-gated SqliteStoreAndForward→Wonderware sink (AddAlarmHistorian)
This commit is contained in:
+94
@@ -0,0 +1,94 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Historian;
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the config-gated <c>AddAlarmHistorian</c> registration: when the
|
||||
/// <c>AlarmHistorian</c> section is absent or disabled the <see cref="NullAlarmHistorianSink"/>
|
||||
/// default survives; when it is enabled a real <see cref="SqliteStoreAndForwardSink"/> wins
|
||||
/// (last-registration-wins over the <c>TryAddSingleton</c> Null default).
|
||||
/// </summary>
|
||||
public sealed class AlarmHistorianRegistrationTests
|
||||
{
|
||||
/// <summary>A no-op writer the factory hands the Sqlite sink; never actually invoked in these tests.</summary>
|
||||
private sealed class FakeWriter : IAlarmHistorianWriter
|
||||
{
|
||||
public Task<IReadOnlyList<HistorianWriteOutcome>> WriteBatchAsync(
|
||||
IReadOnlyList<AlarmHistorianEvent> batch, CancellationToken cancellationToken)
|
||||
=> Task.FromResult<IReadOnlyList<HistorianWriteOutcome>>(
|
||||
batch.Select(_ => HistorianWriteOutcome.Ack).ToList());
|
||||
}
|
||||
|
||||
/// <summary>Seed the Null default exactly the way <c>AddOtOpcUaRuntime</c> does, then add logging.</summary>
|
||||
private static ServiceCollection BaseServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.TryAddSingleton<IAlarmHistorianSink>(NullAlarmHistorianSink.Instance);
|
||||
return services;
|
||||
}
|
||||
|
||||
private static IConfiguration ConfigFrom(Dictionary<string, string?> values)
|
||||
=> new ConfigurationBuilder().AddInMemoryCollection(values).Build();
|
||||
|
||||
[Fact]
|
||||
public void Section_absent_keeps_null_sink()
|
||||
{
|
||||
var services = BaseServices();
|
||||
var config = ConfigFrom(new Dictionary<string, string?>());
|
||||
|
||||
services.AddAlarmHistorian(config, (_, _) => new FakeWriter());
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
provider.GetRequiredService<IAlarmHistorianSink>().ShouldBeOfType<NullAlarmHistorianSink>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Section_disabled_keeps_null_sink()
|
||||
{
|
||||
var services = BaseServices();
|
||||
var config = ConfigFrom(new Dictionary<string, string?>
|
||||
{
|
||||
["AlarmHistorian:Enabled"] = "false",
|
||||
});
|
||||
|
||||
services.AddAlarmHistorian(config, (_, _) => new FakeWriter());
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
provider.GetRequiredService<IAlarmHistorianSink>().ShouldBeOfType<NullAlarmHistorianSink>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Section_enabled_registers_sqlite_sink()
|
||||
{
|
||||
var tempDir = Path.Combine(Path.GetTempPath(), "otopcua-alarmhist-test-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(tempDir);
|
||||
var dbPath = Path.Combine(tempDir, "alarm-historian-test.db");
|
||||
|
||||
try
|
||||
{
|
||||
var services = BaseServices();
|
||||
var config = ConfigFrom(new Dictionary<string, string?>
|
||||
{
|
||||
["AlarmHistorian:Enabled"] = "true",
|
||||
["AlarmHistorian:DatabasePath"] = dbPath,
|
||||
});
|
||||
|
||||
services.AddAlarmHistorian(config, (_, _) => new FakeWriter());
|
||||
|
||||
using (var provider = services.BuildServiceProvider())
|
||||
{
|
||||
provider.GetRequiredService<IAlarmHistorianSink>().ShouldBeOfType<SqliteStoreAndForwardSink>();
|
||||
} // dispose stops the drain loop + releases the SQLite file handle
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { Directory.Delete(tempDir, recursive: true); } catch (IOException) { /* best effort */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user