using Moq;
using Akka.Actor;
using Akka.TestKit.Xunit2;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
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.Scripts;
namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests.Actors;
///
/// Regression: an alarm on-trigger run's ScriptRuntimeContext was built without a
/// sourceScript, so the context's _logTarget — which names the script on every
/// Log.* line and on the run's trace rows — fell back to the INSTANCE name. The launcher now
/// passes the same AlarmActor:{alarmName} source key it already uses for this alarm's site
/// events, from which the context derives the alarm's own name.
///
public class AlarmScriptLogTargetTests : TestKit, IDisposable
{
private sealed record Entry(LogLevel Level, string Message);
/// A minimal recording logger — the alarm path hands its own ILogger down to the script logger.
private sealed class RecordingLogger : ILogger
{
/// Gets the log entries recorded so far.
public List Entries { get; } = [];
/// Returns no scope.
/// The scope state type.
/// The scope state.
/// Always null.
public IDisposable? BeginScope(TState state) where TState : notnull => null;
/// Reports every log level as enabled.
/// The log level.
/// Always true.
public bool IsEnabled(LogLevel logLevel) => true;
/// Records the log entry for later assertions.
/// The log state type.
/// The log level.
/// The event id.
/// The log state.
/// The associated exception, if any.
/// Formats the state and exception into a message.
public void Log(LogLevel level, EventId eventId, TState state, Exception? exception, Func formatter)
{
lock (Entries) Entries.Add(new Entry(level, formatter(state, exception)));
}
}
private readonly SharedScriptLibrary _sharedLibrary = new(
new ScriptCompilationService(NullLogger.Instance),
NullLogger.Instance);
///
void IDisposable.Dispose() => Shutdown();
/// Verifies that an alarm on-trigger run attributes its log lines to the alarm, not just the instance.
[Fact]
public void AlarmOnTriggerRun_AttributesItsLogLinesToTheALARM_NotJustTheInstance()
{
var options = new SiteRuntimeOptions
{
ScriptExecutionTimeoutSeconds = 10,
MaxScriptCallDepth = 10,
};
var scriptOptions = ScriptOptions.Default
.WithReferences(typeof(object).Assembly, typeof(Enumerable).Assembly, typeof(ScriptGlobals).Assembly)
.WithImports("System", "System.Threading.Tasks");
var compiled = CSharpScript.Create