Files
meeting-assistant/MeetingAssistant.Tests/SpeechRecognitionPipelineHostedServiceTests.cs
T

136 lines
4.4 KiB
C#

using MeetingAssistant.Transcription;
using Microsoft.Extensions.Logging.Abstractions;
namespace MeetingAssistant.Tests;
public sealed class SpeechRecognitionPipelineHostedServiceTests
{
[Fact]
public async Task HostedServiceInitializesAndWarmsConfiguredPipelineWithoutBlockingStartup()
{
var pipeline = new CapturingSpeechRecognitionPipeline { BlockReadinessUntilCancelled = true };
var service = new SpeechRecognitionPipelineHostedService(
new CapturingSpeechRecognitionPipelineFactory(pipeline),
NullLogger<SpeechRecognitionPipelineHostedService>.Instance);
var startTask = service.StartAsync(CancellationToken.None);
await startTask.WaitAsync(TimeSpan.FromSeconds(1));
await pipeline.WaitForInitializeAsync();
await pipeline.WaitForReadinessAsync();
await service.StopAsync(CancellationToken.None);
Assert.Equal(1, pipeline.InitializeCount);
Assert.Equal(1, pipeline.ReadinessCount);
Assert.True(pipeline.ReadinessCancellationWasObserved);
Assert.True(pipeline.Disposed);
}
private sealed class CapturingSpeechRecognitionPipelineFactory : ISpeechRecognitionPipelineFactory
{
private readonly ISpeechRecognitionPipeline pipeline;
public CapturingSpeechRecognitionPipelineFactory(ISpeechRecognitionPipeline pipeline)
{
this.pipeline = pipeline;
}
public ISpeechRecognitionPipeline Create()
{
return pipeline;
}
}
private sealed class CapturingSpeechRecognitionPipeline : ISpeechRecognitionPipeline
{
private readonly TaskCompletionSource initialized = new(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly TaskCompletionSource readinessChecked = new(TaskCreationOptions.RunContinuationsAsynchronously);
public int InitializeCount { get; private set; }
public int ReadinessCount { get; private set; }
public bool BlockReadinessUntilCancelled { get; init; }
public bool ReadinessCancellationWasObserved { get; private set; }
public bool Disposed { get; private set; }
public Task WaitForInitializeAsync()
{
return initialized.Task.WaitAsync(TimeSpan.FromSeconds(5));
}
public Task WaitForReadinessAsync()
{
return readinessChecked.Task.WaitAsync(TimeSpan.FromSeconds(5));
}
public Task InitializeAsync(CancellationToken cancellationToken)
{
return InitializeAsync(SpeechRecognitionPipelineOptions.Default, cancellationToken);
}
public Task InitializeAsync(
SpeechRecognitionPipelineOptions options,
CancellationToken cancellationToken)
{
InitializeCount++;
initialized.TrySetResult();
return Task.CompletedTask;
}
public async Task WaitUntilReadyAsync(CancellationToken cancellationToken)
{
ReadinessCount++;
readinessChecked.TrySetResult();
if (!BlockReadinessUntilCancelled)
{
return;
}
try
{
await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
}
catch (OperationCanceledException)
{
ReadinessCancellationWasObserved = true;
throw;
}
}
public ValueTask WriteAsync(MeetingAssistant.Recording.AudioChunk chunk, CancellationToken cancellationToken)
{
return ValueTask.CompletedTask;
}
public Task CompleteAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public async IAsyncEnumerable<TranscriptionSegment> ReadLiveTranscriptAsync(
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken)
{
await Task.CompletedTask;
yield break;
}
public Task<IReadOnlyList<TranscriptionSegment>> ReadFinishedTranscriptAsync(
string audioPath,
SpeechRecognitionPipelineOptions options,
CancellationToken cancellationToken)
{
return Task.FromResult<IReadOnlyList<TranscriptionSegment>>([]);
}
public ValueTask DisposeAsync()
{
Disposed = true;
return ValueTask.CompletedTask;
}
}
}