using Kompas.Mcp.Core.Threading; namespace Kompas.Mcp.Tests; /// Юнит-тесты STA-диспетчера. COM не требуется. [Trait("Category", "Unit")] public sealed class DispatcherTests { [Fact] public async Task Runs_work_on_STA_thread() { using var d = new KompasDispatcher(); var state = await d.InvokeAsync(() => Thread.CurrentThread.GetApartmentState()); Assert.Equal(ApartmentState.STA, state); } [Fact] public async Task Serializes_all_work_on_one_thread() { using var d = new KompasDispatcher(); var id1 = await d.InvokeAsync(() => Environment.CurrentManagedThreadId); var id2 = await d.InvokeAsync(() => Environment.CurrentManagedThreadId); Assert.Equal(id1, id2); Assert.NotEqual(Environment.CurrentManagedThreadId, id1); } [Fact] public async Task Propagates_exceptions_to_caller() { using var d = new KompasDispatcher(); await Assert.ThrowsAsync( () => d.InvokeAsync(() => throw new InvalidOperationException("boom"))); } [Fact] public async Task Throws_ObjectDisposed_after_dispose() { var d = new KompasDispatcher(); d.Dispose(); await Assert.ThrowsAsync(() => d.InvokeAsync(() => { })); } [Fact] public async Task Nested_invoke_runs_inline_without_deadlock() { using var d = new KompasDispatcher(); var result = await d.InvokeAsync(() => { // Вложенный вызов на том же потоке должен выполниться синхронно, без дедлока. return d.InvokeAsync(() => 42).GetAwaiter().GetResult(); }); Assert.Equal(42, result); } }