using System.Runtime.CompilerServices; using System.Text.Json; using WxAgent.Service; using Xunit; namespace WxAgent.Service.Tests; public sealed class EventPumpTests { private sealed class SourceBackend : IAgentBackend, IAgentEventSource { public IReadOnlyList Capabilities => [new("listener-events", true, true, true, true, false, "read", false, 30, null, [])]; public Task StatusAsync(CancellationToken ct) => Task.FromResult(new { ok = true }); public async IAsyncEnumerable ListenAsync([EnumeratorCancellation] CancellationToken ct) { yield return new AgentEvent("evt", "ui-current", "session", "message", "summary", DateTimeOffset.UtcNow); await Task.Delay(Timeout.Infinite, ct); } } private sealed class DisabledSourceBackend : IAgentBackend, IAgentEventSource { public bool ListenCalled { get; private set; } public IReadOnlyList Capabilities => [new("listener-events", true, true, false, true, false, "read", false, 30, "disabled", [])]; public Task StatusAsync(CancellationToken ct) => Task.FromResult(new { ok = true }); public async IAsyncEnumerable ListenAsync([EnumeratorCancellation] CancellationToken ct) { ListenCalled = true; await Task.CompletedTask; yield break; } } [Fact] public async Task DisabledListenerDoesNotStartUiSource() { var source = new DisabledSourceBackend(); using var pump = new EventPump(source, new EventHub(), new ServiceOptions { DataDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")), CredentialFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"), "credentials.json") }); await pump.StartAsync(default); await Task.Delay(100); await pump.StopAsync(default); Assert.False(source.ListenCalled); } [Fact] public async Task PumpForwardsToTheConfiguredPrincipal() { var dir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); Directory.CreateDirectory(dir); var options = new ServiceOptions { DataDirectory = dir, CredentialFile = Path.Combine(dir, "credentials.json") }; File.WriteAllText(options.CredentialFile, JsonSerializer.Serialize(new[] { new ServiceCredential("alice", new string('A', 64), ["read"], []) })); try { var hub = new EventHub(); var subscription = hub.Subscribe("alice", null); using var pump = new EventPump(new SourceBackend(), hub, options); await pump.StartAsync(default); var ct = new CancellationTokenSource(TimeSpan.FromSeconds(5)); while (!hub.TryGet(subscription.SubscriptionId, "alice", out var current) || !current.Channel.Reader.TryRead(out _)) await Task.Delay(10, ct.Token); await pump.StopAsync(default); } finally { Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools(); Directory.Delete(dir, true); } } }