using Akka.Actor;
using Akka.TestKit;
using Akka.TestKit.Xunit2;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.Extensions.Logging.Abstractions;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Instance;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Flattening;
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Actors;
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Persistence;
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Scripts;
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Streaming;
using ZB.MOM.WW.ScadaBridge.TestSupport;
using System.Text.Json;
namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests.Actors;
///
/// Audit Log #23 (M5.4 T4 — ParentExecutionId tag-cascade, alarm leg):
/// an alarm on-trigger script run must chain under the execution whose attribute
/// write fired the alarm, and must stay a tree ROOT when the firing value had no
/// audited origin.
///
/// The cascade is three hops, each pinned here:
///
///
/// ScriptRuntimeContext.SetAttribute stamps the running script's
/// ExecutionId onto .
///
///
/// The Instance Actor carries that id onto the published
/// .
///
///
/// The Alarm Actor hands it to the spawned AlarmExecutionActor as the
/// on-trigger run's ParentExecutionId — observable via
/// AlarmActor.LastOnTriggerParentExecutionId.
///
///
///
///
/// A value arriving from the Data Connection Layer (external device data) carries
/// no SourceExecutionId, so the alarm it fires is correctly parentless.
///
///
public class AlarmCascadeParentExecutionTests : TestKit, IDisposable
{
private readonly ScriptCompilationService _compilationService;
private readonly SharedScriptLibrary _sharedLibrary;
private readonly SiteRuntimeOptions _options;
private readonly TestLocalDb _localDb;
private readonly SiteStorageService _storage;
public AlarmCascadeParentExecutionTests()
{
_compilationService = new ScriptCompilationService(
NullLogger.Instance);
_sharedLibrary = new SharedScriptLibrary(
_compilationService, NullLogger.Instance);
_options = new SiteRuntimeOptions();
_localDb = TestLocalDb.CreateTemp("alarm-cascade-test");
_storage = new SiteStorageService(_localDb.Db, NullLogger.Instance);
_storage.InitializeAsync().GetAwaiter().GetResult();
}
void IDisposable.Dispose()
{
// TestKit teardown first, so no in-flight actor can reach a disposed ILocalDb;
// then dispose the database before deleting — the master connection anchors the WAL.
Shutdown();
var path = _localDb.Path;
_localDb.Dispose();
TestLocalDb.DeleteFiles(path);
}
/// A trivially-succeeding on-trigger script, so the alarm actually spawns an execution.
private Script