using System.Collections.Concurrent; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using ZB.MOM.WW.ScadaBridge.SiteEventLogging; using ZB.MOM.WW.ScadaBridge.SiteRuntime.Scripts; namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests.TestSupport; /// /// M1 Site Event Logging categories: a capturing fake /// used by the actor tests to assert that the right operational events are emitted. /// Thread-safe — the actors fire-and-forget LogEventAsync from background /// tasks, so multiple captures can land concurrently. /// public sealed class FakeSiteEventLogger : ISiteEventLogger { /// One captured invocation. public sealed record Entry( string EventType, string Severity, string? InstanceId, string Source, string Message, string? Details); private readonly ConcurrentQueue _entries = new(); /// All captured events, in arrival order. public IReadOnlyList Entries => _entries.ToArray(); /// Captured events filtered to a single category. public IReadOnlyList OfType(string eventType) => _entries.Where(e => e.EventType == eventType).ToArray(); /// public Task LogEventAsync( string eventType, string severity, string? instanceId, string source, string message, string? details = null) { _entries.Enqueue(new Entry(eventType, severity, instanceId, source, message, details)); return Task.CompletedTask; } /// public long FailedWriteCount => 0; } /// /// Minimal that resolves a single /// — enough for the actors' optional /// _serviceProvider?.GetService<ISiteEventLogger>() resolution /// without pulling a full DI container into the actor tests. /// /// Also serves (returning a scope that just /// re-exposes this provider) so callers that do /// serviceProvider.CreateScope() — e.g. ScriptExecutionActor — /// don't throw before they reach the logging hot path. /// /// /// The event logger to resolve for . /// /// WP3.2: optional for /// — resolves for the hot-toggle tests; null (the default) exercises the launcher's /// constructor-passed-options fallback, matching every pre-WP3.2 caller of this class. /// /// /// WP3.2: optional — resolves so a test can assert on /// the aggregate counters a run produced; null (the default) exercises the launcher's /// null-safe skip path. /// public sealed class SingleServiceProvider( ISiteEventLogger logger, IOptionsMonitor? optionsMonitor = null, ScriptRunSummaryRecorder? summaryRecorder = null) : IServiceProvider, IServiceScopeFactory, IServiceScope { private readonly ISiteEventLogger _logger = logger; private readonly IOptionsMonitor? _optionsMonitor = optionsMonitor; private readonly ScriptRunSummaryRecorder? _summaryRecorder = summaryRecorder; /// public object? GetService(Type serviceType) { if (serviceType == typeof(ISiteEventLogger)) return _logger; if (serviceType == typeof(IServiceScopeFactory)) return this; if (serviceType == typeof(IOptionsMonitor)) return _optionsMonitor; if (serviceType == typeof(ScriptRunSummaryRecorder)) return _summaryRecorder; return null; } /// public IServiceScope CreateScope() => this; /// public IServiceProvider ServiceProvider => this; /// public void Dispose() { } }