using MeetingAssistant; using MeetingAssistant.Transcription; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; namespace MeetingAssistant.Tests; public sealed class FunAsrTranscriptFinalizerTests { [Fact] public async Task FinalizerRunsFunAsrAutoModelAndReturnsSentenceSpeakerSegments() { var audioPath = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"), "meeting.wav"); Directory.CreateDirectory(Path.GetDirectoryName(audioPath)!); await File.WriteAllBytesAsync(audioPath, [1, 2, 3, 4]); var commandRunner = new CapturingCommandRunner( """ setup noise __MEETING_ASSISTANT_DIARIZATION_JSON_START__ [{"key":"input","sentence_info":[{"text":" hello","start":410,"end":4810,"spk":0},{"text":" yes","start":22550,"end":22770,"spk":1}]}] __MEETING_ASSISTANT_DIARIZATION_JSON_END__ """); var finalizer = new FunAsrTranscriptFinalizer( commandRunner, Options.Create(new MeetingAssistantOptions { FunAsr = new FunAsrOptions { Backend = new FunAsrBackendOptions { DockerCommand = "docker", Image = "funasr:test", ModelsFolder = "C:\\Models" }, Diarization = new FunAsrDiarizationOptions { Enabled = true, CommandTimeout = TimeSpan.FromMinutes(1) } } }), NullLogger.Instance); var segments = await finalizer.FinalizeAsync(audioPath, [], CancellationToken.None); Assert.Contains("run", commandRunner.Arguments); Assert.Contains("funasr:test", commandRunner.Arguments); Assert.Contains($"{audioPath}:/workspace/input.wav:ro", commandRunner.Arguments); Assert.Collection( segments, first => { Assert.Equal(TimeSpan.FromMilliseconds(410), first.Start); Assert.Equal(TimeSpan.FromMilliseconds(4810), first.End); Assert.Equal("Speaker 0", first.Speaker); Assert.Equal("hello", first.Text); }, second => { Assert.Equal(TimeSpan.FromMilliseconds(22550), second.Start); Assert.Equal(TimeSpan.FromMilliseconds(22770), second.End); Assert.Equal("Speaker 1", second.Speaker); Assert.Equal("yes", second.Text); }); } [Fact] public async Task FinalizerReturnsNoSegmentsWhenDiarizationIsDisabled() { var commandRunner = new CapturingCommandRunner(""); var finalizer = new FunAsrTranscriptFinalizer( commandRunner, Options.Create(new MeetingAssistantOptions { FunAsr = new FunAsrOptions { Diarization = new FunAsrDiarizationOptions { Enabled = false } } }), NullLogger.Instance); var segments = await finalizer.FinalizeAsync("C:\\Recordings\\meeting.wav", [], CancellationToken.None); Assert.Empty(segments); Assert.Empty(commandRunner.Arguments); } private sealed class CapturingCommandRunner : ICommandRunner { private readonly string output; public CapturingCommandRunner(string output) { this.output = output; } public IReadOnlyList Arguments { get; private set; } = []; public Task RunAsync( string fileName, IReadOnlyList arguments, CancellationToken cancellationToken, IReadOnlyDictionary? environment = null) { Arguments = arguments; return Task.FromResult(new CommandResult(0, output, "")); } } }