using System.Threading; using ClaudeDo.Ui.Services; using Xunit; namespace ClaudeDo.Ui.Tests.Services; public class PtyTerminalSessionTests { [Fact] public async Task WaitForLaunchGateAsync_ThrowsTimeoutException_WhenGateStaysHeld() { using var gate = new SemaphoreSlim(0, 1); // never released — simulates a hung launch await Assert.ThrowsAsync(() => PtyTerminalSession.WaitForLaunchGateAsync(gate, TimeSpan.FromMilliseconds(50), CancellationToken.None)); } [Fact] public async Task WaitForLaunchGateAsync_DoesNotReleaseGate_ItNeverAcquired() { using var gate = new SemaphoreSlim(0, 1); await Assert.ThrowsAsync(() => PtyTerminalSession.WaitForLaunchGateAsync(gate, TimeSpan.FromMilliseconds(50), CancellationToken.None)); Assert.Equal(0, gate.CurrentCount); // still held; a bad fix would Release() what it never acquired } [Fact] public async Task WaitForLaunchGateAsync_AcquiresGate_WhenAvailable() { using var gate = new SemaphoreSlim(1, 1); await PtyTerminalSession.WaitForLaunchGateAsync(gate, TimeSpan.FromSeconds(5), CancellationToken.None); Assert.Equal(0, gate.CurrentCount); // acquired } // Fix: PasteAsync used to be void and swallow every failure. Not-started is the cheap case to // cover without a real TerminalControl/ConPTY spawn. [Fact] public async Task PasteAsync_ReturnsFalse_WhenNotStarted() { using var session = new PtyTerminalSession(); Assert.False(await session.PasteAsync()); } }