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

149 lines
5.2 KiB
C#

using MeetingAssistant;
using MeetingAssistant.Speakers;
using MeetingAssistant.Transcription;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using NAudio.Wave;
namespace MeetingAssistant.Tests;
public sealed class AzureSpeechSpeakerIdentityMatcherTests
{
[Fact]
public void IdentityDiarizationLanguageUsesFirstAutoDetectLanguageWhenConfiguredLanguageIsAuto()
{
Assert.Equal(
"de-DE",
AzureSpeechSpeakerIdentityDiarizationClient.ResolveRecognitionLanguage(new AzureSpeechOptions
{
Language = "auto",
AutoDetectLanguages = ["de-DE", "en-US"]
}));
}
[Fact]
public void IdentityDiarizationLanguageUsesConfiguredConcreteLanguage()
{
Assert.Equal(
"en-US",
AzureSpeechSpeakerIdentityDiarizationClient.ResolveRecognitionLanguage(new AzureSpeechOptions
{
Language = "en-US",
AutoDetectLanguages = ["de-DE"]
}));
}
[Fact]
public async Task MatcherReturnsIdentityWhenAzureDiarizationAssignsUnknownSnippetToKnownSpeaker()
{
var diarizationClient = new FakeSpeakerIdentityDiarizationClient(
[
new TranscriptionSegment(TimeSpan.Zero, TimeSpan.FromSeconds(1), "Guest-1", "known"),
new TranscriptionSegment(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3), "Guest-1", "unknown")
]);
var matcher = CreateMatcher(diarizationClient);
var snippet = CreateWavSnippet();
var match = await matcher.MatchAsync(
new SpeakerIdentityMatchRequest(
"Guest03",
snippet,
[new SpeakerIdentityMatchCandidate(42, "Chris", 5, [snippet])]),
CancellationToken.None);
Assert.NotNull(match);
Assert.Equal(42, match.IdentityId);
Assert.NotNull(diarizationClient.WavPath);
Assert.False(File.Exists(diarizationClient.WavPath));
}
[Fact]
public async Task MatcherRequiresTwoMatchingKnownSnippetsWhenIdentityHasMultipleSnippets()
{
var diarizationClient = new FakeSpeakerIdentityDiarizationClient(
[
new TranscriptionSegment(TimeSpan.Zero, TimeSpan.FromSeconds(1), "Guest-1", "known one"),
new TranscriptionSegment(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3), "Guest-2", "known two"),
new TranscriptionSegment(TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(5), "Guest-1", "unknown")
]);
var matcher = CreateMatcher(diarizationClient);
var snippet = CreateWavSnippet();
var match = await matcher.MatchAsync(
new SpeakerIdentityMatchRequest(
"Guest03",
snippet,
[new SpeakerIdentityMatchCandidate(42, "Chris", 5, [snippet, snippet])]),
CancellationToken.None);
Assert.Null(match);
}
[Fact]
public async Task MatcherReturnsNullWhenAzureDiarizationAssignsDifferentSpeakers()
{
var diarizationClient = new FakeSpeakerIdentityDiarizationClient(
[
new TranscriptionSegment(TimeSpan.Zero, TimeSpan.FromSeconds(1), "Guest-1", "known"),
new TranscriptionSegment(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3), "Guest-2", "unknown")
]);
var matcher = CreateMatcher(diarizationClient);
var snippet = CreateWavSnippet();
var match = await matcher.MatchAsync(
new SpeakerIdentityMatchRequest(
"Guest03",
snippet,
[new SpeakerIdentityMatchCandidate(42, "Chris", 5, [snippet])]),
CancellationToken.None);
Assert.Null(match);
}
private static AzureSpeechSpeakerIdentityMatcher CreateMatcher(ISpeakerIdentityDiarizationClient diarizationClient)
{
return new AzureSpeechSpeakerIdentityMatcher(
diarizationClient,
Options.Create(new MeetingAssistantOptions
{
SpeakerIdentification = new SpeakerIdentificationOptions
{
SilenceBetweenSnippetsSeconds = 1
}
}),
NullLogger<AzureSpeechSpeakerIdentityMatcher>.Instance);
}
private static byte[] CreateWavSnippet()
{
using var stream = new MemoryStream();
using (var writer = new WaveFileWriter(stream, new WaveFormat(16000, 16, 1)))
{
writer.Write(new byte[16000 * 2], 0, 16000 * 2);
}
return stream.ToArray();
}
private sealed class FakeSpeakerIdentityDiarizationClient : ISpeakerIdentityDiarizationClient
{
private readonly IReadOnlyList<TranscriptionSegment> segments;
public FakeSpeakerIdentityDiarizationClient(IReadOnlyList<TranscriptionSegment> segments)
{
this.segments = segments;
}
public string? WavPath { get; private set; }
public Task<IReadOnlyList<TranscriptionSegment>> DiarizeAsync(
string wavPath,
CancellationToken cancellationToken)
{
WavPath = wavPath;
Assert.True(File.Exists(wavPath));
return Task.FromResult(segments);
}
}
}