Public Access
Implement meeting assistant v1
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
using MeetingAssistant.Transcription;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace MeetingAssistant.Tests;
|
||||
|
||||
public sealed class FunAsrBackendLifecycleTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task LifecycleInstallsStartsAndStopsManagedDockerContainer()
|
||||
{
|
||||
var runner = new CapturingCommandRunner();
|
||||
var readinessProbe = new CapturingReadinessProbe();
|
||||
var modelsFolder = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
||||
var lifecycle = new FunAsrDockerBackendLifecycle(
|
||||
runner,
|
||||
readinessProbe,
|
||||
Options.Create(new MeetingAssistantOptions
|
||||
{
|
||||
FunAsr = new FunAsrOptions
|
||||
{
|
||||
Endpoint = "ws://127.0.0.1:10095",
|
||||
Backend = new FunAsrBackendOptions
|
||||
{
|
||||
Enabled = true,
|
||||
Image = "funasr:test",
|
||||
ContainerName = "meeting-assistant-funasr-test",
|
||||
ModelsFolder = modelsFolder,
|
||||
StartupTimeout = TimeSpan.Zero
|
||||
}
|
||||
}
|
||||
}),
|
||||
NullLogger<FunAsrDockerBackendLifecycle>.Instance);
|
||||
|
||||
await lifecycle.EnsureStartedAsync(CancellationToken.None);
|
||||
await lifecycle.EnsureStartedAsync(CancellationToken.None);
|
||||
await lifecycle.DisposeAsync();
|
||||
|
||||
Assert.Equal(new Uri("ws://127.0.0.1:10095"), readinessProbe.Endpoint);
|
||||
Assert.Equal(TimeSpan.Zero, readinessProbe.Timeout);
|
||||
Assert.Collection(
|
||||
runner.Commands,
|
||||
command =>
|
||||
{
|
||||
Assert.Equal("docker", command.FileName);
|
||||
Assert.Equal(["pull", "funasr:test"], command.Arguments);
|
||||
},
|
||||
command =>
|
||||
{
|
||||
Assert.Equal(["rm", "-f", "meeting-assistant-funasr-test"], command.Arguments);
|
||||
},
|
||||
command =>
|
||||
{
|
||||
Assert.Equal("docker", command.FileName);
|
||||
Assert.Contains("run", command.Arguments);
|
||||
Assert.Contains("-d", command.Arguments);
|
||||
Assert.Contains("--privileged=true", command.Arguments);
|
||||
Assert.Contains("--name", command.Arguments);
|
||||
Assert.Contains("meeting-assistant-funasr-test", command.Arguments);
|
||||
Assert.Contains("-p", command.Arguments);
|
||||
Assert.Contains("10095:10095", command.Arguments);
|
||||
Assert.Contains($"{modelsFolder}:/workspace/models", command.Arguments);
|
||||
Assert.Contains("funasr:test", command.Arguments);
|
||||
},
|
||||
command =>
|
||||
{
|
||||
Assert.Equal(["stop", "meeting-assistant-funasr-test"], command.Arguments);
|
||||
});
|
||||
Assert.True(File.Exists(Path.Combine(modelsFolder, "hotwords.txt")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DisabledLifecycleDoesNotRunDockerCommands()
|
||||
{
|
||||
var runner = new CapturingCommandRunner();
|
||||
var lifecycle = new FunAsrDockerBackendLifecycle(
|
||||
runner,
|
||||
new CapturingReadinessProbe(),
|
||||
Options.Create(new MeetingAssistantOptions
|
||||
{
|
||||
FunAsr = new FunAsrOptions
|
||||
{
|
||||
Backend = new FunAsrBackendOptions { Enabled = false }
|
||||
}
|
||||
}),
|
||||
NullLogger<FunAsrDockerBackendLifecycle>.Instance);
|
||||
|
||||
await lifecycle.EnsureStartedAsync(CancellationToken.None);
|
||||
await lifecycle.DisposeAsync();
|
||||
|
||||
Assert.Empty(runner.Commands);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LifecycleFailsWhenDockerCommandTimesOut()
|
||||
{
|
||||
var runner = new HangingCommandRunner();
|
||||
var lifecycle = new FunAsrDockerBackendLifecycle(
|
||||
runner,
|
||||
new CapturingReadinessProbe(),
|
||||
Options.Create(new MeetingAssistantOptions
|
||||
{
|
||||
FunAsr = new FunAsrOptions
|
||||
{
|
||||
Backend = new FunAsrBackendOptions
|
||||
{
|
||||
Enabled = true,
|
||||
CommandTimeout = TimeSpan.FromMilliseconds(1)
|
||||
}
|
||||
}
|
||||
}),
|
||||
NullLogger<FunAsrDockerBackendLifecycle>.Instance);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<TimeoutException>(
|
||||
() => lifecycle.EnsureStartedAsync(CancellationToken.None));
|
||||
|
||||
Assert.Contains("timed out", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LifecycleWaitsForWebSocketReadinessBeforeMarkingStarted()
|
||||
{
|
||||
var runner = new CapturingCommandRunner();
|
||||
var readinessProbe = new CapturingReadinessProbe();
|
||||
var lifecycle = new FunAsrDockerBackendLifecycle(
|
||||
runner,
|
||||
readinessProbe,
|
||||
Options.Create(new MeetingAssistantOptions
|
||||
{
|
||||
FunAsr = new FunAsrOptions
|
||||
{
|
||||
Endpoint = "ws://127.0.0.1:10095",
|
||||
Backend = new FunAsrBackendOptions
|
||||
{
|
||||
Enabled = true,
|
||||
Image = "funasr:test",
|
||||
ContainerName = "meeting-assistant-funasr-test",
|
||||
ModelsFolder = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N")),
|
||||
StartupTimeout = TimeSpan.FromMinutes(3)
|
||||
}
|
||||
}
|
||||
}),
|
||||
NullLogger<FunAsrDockerBackendLifecycle>.Instance);
|
||||
|
||||
await lifecycle.EnsureStartedAsync(CancellationToken.None);
|
||||
|
||||
Assert.Equal(1, readinessProbe.CallCount);
|
||||
Assert.Equal(new Uri("ws://127.0.0.1:10095"), readinessProbe.Endpoint);
|
||||
Assert.Equal(TimeSpan.FromMinutes(3), readinessProbe.Timeout);
|
||||
Assert.Equal(3, runner.Commands.Count);
|
||||
await lifecycle.DisposeAsync();
|
||||
}
|
||||
|
||||
private sealed class CapturingCommandRunner : ICommandRunner
|
||||
{
|
||||
public List<CapturedCommand> Commands { get; } = [];
|
||||
|
||||
public Task<CommandResult> RunAsync(
|
||||
string fileName,
|
||||
IReadOnlyList<string> arguments,
|
||||
CancellationToken cancellationToken,
|
||||
IReadOnlyDictionary<string, string>? environment = null)
|
||||
{
|
||||
Commands.Add(new CapturedCommand(fileName, arguments.ToArray()));
|
||||
return Task.FromResult(new CommandResult(0, "", ""));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record CapturedCommand(string FileName, IReadOnlyList<string> Arguments);
|
||||
|
||||
private sealed class HangingCommandRunner : ICommandRunner
|
||||
{
|
||||
public async Task<CommandResult> RunAsync(
|
||||
string fileName,
|
||||
IReadOnlyList<string> arguments,
|
||||
CancellationToken cancellationToken,
|
||||
IReadOnlyDictionary<string, string>? environment = null)
|
||||
{
|
||||
await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
|
||||
return new CommandResult(0, "", "");
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CapturingReadinessProbe : IFunAsrBackendReadinessProbe
|
||||
{
|
||||
public int CallCount { get; private set; }
|
||||
|
||||
public Uri? Endpoint { get; private set; }
|
||||
|
||||
public TimeSpan Timeout { get; private set; }
|
||||
|
||||
public Task WaitUntilReadyAsync(
|
||||
Uri endpoint,
|
||||
TimeSpan timeout,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
CallCount++;
|
||||
Endpoint = endpoint;
|
||||
Timeout = timeout;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user