Files
meeting-assistant/MeetingAssistant.Tests/ConfiguredSpeechRecognitionPipelineFactoryTests.cs
2026-06-03 07:17:00 +02:00

128 lines
4.6 KiB
C#

using MeetingAssistant.LaunchProfiles;
using MeetingAssistant.Recording;
using MeetingAssistant.Transcription;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
namespace MeetingAssistant.Tests;
public sealed class ConfiguredSpeechRecognitionPipelineFactoryTests
{
[Fact]
public async Task FactoryCreatesAzureSpeechPipelineWhenConfigured()
{
var services = new ServiceCollection();
services.AddSingleton(Options.Create(new MeetingAssistantOptions
{
Recording = new RecordingOptions
{
TranscriptionProvider = "azure-speech"
}
}));
services.AddSingleton<ILogger<AzureSpeechStreamingTranscriptionProvider>>(
NullLogger<AzureSpeechStreamingTranscriptionProvider>.Instance);
services.AddTransient<AzureSpeechStreamingTranscriptionProvider>();
var provider = services.BuildServiceProvider();
var factory = new ConfiguredSpeechRecognitionPipelineFactory(
[new AzureSpeechRecognitionPipelineBuilder(provider)],
provider.GetRequiredService<IOptions<MeetingAssistantOptions>>());
await using var pipeline = factory.Create();
Assert.IsType<AzureSpeechRecognitionPipeline>(pipeline);
}
[Fact]
public async Task FactoryPassesResolvedLaunchProfileOptionsToSelectedBuilder()
{
var services = new ServiceCollection().BuildServiceProvider();
var builder = new CapturingPipelineBuilder();
var launchProfiles = new ConfigurationLaunchProfileOptionsProvider(
new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["MeetingAssistant:Recording:TranscriptionProvider"] = "capturing",
["MeetingAssistant:Agent:Model"] = "default-model",
["MeetingAssistant:LaunchProfiles:english:Hotkey:Toggle"] = "Ctrl+Alt+L",
["MeetingAssistant:LaunchProfiles:english:Recording:TranscriptionProvider"] = "capturing",
["MeetingAssistant:LaunchProfiles:english:Agent:Model"] = "english-model"
})
.Build());
var factory = new ConfiguredSpeechRecognitionPipelineFactory(
[builder],
Options.Create(launchProfiles.GetRequiredProfile(null).Options),
launchProfiles);
await using var pipeline = factory.Create("english");
Assert.IsType<CapturingPipeline>(pipeline);
Assert.Equal("english-model", builder.Options?.Agent.Model);
}
private sealed class CapturingPipelineBuilder : ISpeechRecognitionPipelineBuilder
{
public string ProviderName => "capturing";
public MeetingAssistantOptions? Options { get; private set; }
public ISpeechRecognitionPipeline Create(MeetingAssistantOptions options)
{
Options = options;
return new CapturingPipeline();
}
}
private sealed class CapturingPipeline : ISpeechRecognitionPipeline
{
public Task InitializeAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task InitializeAsync(
SpeechRecognitionPipelineOptions options,
CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task WaitUntilReadyAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public ValueTask WriteAsync(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()
{
return ValueTask.CompletedTask;
}
}
}