Keep recording startup independent of dictation words
PR and Push Build/Test / build-and-test (push) Failing after 8m52s

This commit is contained in:
DEEUSEW\DESCMANU
2026-06-02 13:27:10 +02:00
parent 0e9d525b63
commit 8a0b302f1d
8 changed files with 191 additions and 12 deletions
@@ -1001,7 +1001,8 @@ public sealed class RecordingCoordinatorTests
new CapturingMeetingSummaryPipeline(),
Options.Create(new MeetingAssistantOptions()),
NullLogger<MeetingRecordingCoordinator>.Instance,
dictationWordStore: new FixedDictationWordStore(["PBI", "Product Backlog Item"]));
dictationWordProvider: CreateRecordingDictationWordProvider(
new FixedDictationWordStore(["PBI", "Product Backlog Item"])));
await coordinator.StartAsync(CancellationToken.None);
await audioSource.WriteAsync(new AudioChunk([1, 0], 16000, 1), CancellationToken.None);
@@ -1012,6 +1013,55 @@ public sealed class RecordingCoordinatorTests
await coordinator.StopAsync(CancellationToken.None);
}
[Fact]
public async Task StartContinuesWithoutDictationWordsWhenStoreCannotBeRead()
{
await StartContinuesWithoutDictationWordsAsync(new ThrowingDictationWordStore());
}
[Fact]
public async Task StartContinuesWithoutDictationWordsWhenStoreReadTimesOut()
{
await StartContinuesWithoutDictationWordsAsync(
new HangingDictationWordStore(),
TimeSpan.FromMilliseconds(20));
}
private static async Task StartContinuesWithoutDictationWordsAsync(
IDictationWordStore dictationWordStore,
TimeSpan? dictationWordsReadTimeout = null)
{
var audioSource = new ControlledAudioSource();
var provider = new CapturingPipelineOptionsProvider();
var options = new MeetingAssistantOptions();
if (dictationWordsReadTimeout is not null)
{
options.Recording.DictationWordsReadTimeout = dictationWordsReadTimeout.Value;
}
var coordinator = new MeetingRecordingCoordinator(
audioSource,
new TestSpeechRecognitionPipelineFactory(provider),
new InMemoryTranscriptStore(),
new InMemoryMeetingNoteStore(),
new CapturingMeetingNoteOpener(),
new InMemoryMeetingArtifactStore(),
new InMemoryRecordedAudioStore(),
new CapturingMeetingSummaryPipeline(),
Options.Create(options),
NullLogger<MeetingRecordingCoordinator>.Instance,
dictationWordProvider: CreateRecordingDictationWordProvider(dictationWordStore));
var status = await coordinator.StartAsync(CancellationToken.None).WaitAsync(TimeSpan.FromSeconds(5));
await audioSource.WriteAsync(new AudioChunk([1, 0], 16000, 1), CancellationToken.None);
await provider.OptionsObserved.Task.WaitAsync(TimeSpan.FromSeconds(5));
Assert.True(status.IsRecording);
Assert.Empty(provider.Options?.DictationWords ?? []);
await coordinator.StopAsync(CancellationToken.None);
}
[Fact]
public async Task StopRewritesTranscriptWithFinalDiarizedSegmentsWhenAvailable()
{
@@ -3333,6 +3383,41 @@ public sealed class RecordingCoordinatorTests
}
}
private static RecordingDictationWordProvider CreateRecordingDictationWordProvider(
IDictationWordStore dictationWordStore)
{
return new RecordingDictationWordProvider(
dictationWordStore,
NullLogger<RecordingDictationWordProvider>.Instance);
}
private sealed class ThrowingDictationWordStore : IDictationWordStore
{
public Task<IReadOnlyList<string>> ReadWordsAsync(CancellationToken cancellationToken)
{
throw new IOException("dictation words unavailable");
}
public Task<IReadOnlyList<string>> AddWordAsync(string word, CancellationToken cancellationToken)
{
throw new IOException("dictation words unavailable");
}
}
private sealed class HangingDictationWordStore : IDictationWordStore
{
public async Task<IReadOnlyList<string>> ReadWordsAsync(CancellationToken cancellationToken)
{
await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
return [];
}
public Task<IReadOnlyList<string>> AddWordAsync(string word, CancellationToken cancellationToken)
{
return Task.FromResult<IReadOnlyList<string>>([]);
}
}
private sealed class FixedAttendeeCanonicalizer : ISpeakerIdentityAttendeeCanonicalizer
{
private readonly IReadOnlyList<string> attendees;