feat: wip...migrating to proper app
This commit is contained in:
+5
-1
@@ -23,6 +23,10 @@ insert_final_newline = false
|
|||||||
#### .NET Coding Conventions ####
|
#### .NET Coding Conventions ####
|
||||||
[*.{cs,vb}]
|
[*.{cs,vb}]
|
||||||
|
|
||||||
|
# Diagnostic severity preferences
|
||||||
|
dotnet_diagnostic.IDE0058.severity = none
|
||||||
|
dotnet_diagnostic.IDE0100.severity = none
|
||||||
|
|
||||||
# Organize usings
|
# Organize usings
|
||||||
dotnet_separate_import_directive_groups = true
|
dotnet_separate_import_directive_groups = true
|
||||||
dotnet_sort_system_directives_first = true
|
dotnet_sort_system_directives_first = true
|
||||||
@@ -59,7 +63,7 @@ dotnet_style_prefer_auto_properties = true:suggestion
|
|||||||
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
|
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
|
||||||
dotnet_style_prefer_compound_assignment = true:suggestion
|
dotnet_style_prefer_compound_assignment = true:suggestion
|
||||||
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
|
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
|
||||||
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
|
dotnet_style_prefer_conditional_expression_over_return = false:silent
|
||||||
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed:suggestion
|
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed:suggestion
|
||||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||||
|
|||||||
@@ -6,7 +6,15 @@
|
|||||||
<PackageVersion Include="FFMpegCore" Version="5.2.0" />
|
<PackageVersion Include="FFMpegCore" Version="5.2.0" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.7" />
|
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.7" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.7" />
|
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.7" />
|
||||||
|
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
|
||||||
<PackageVersion Include="NAudio" Version="2.2.1" />
|
<PackageVersion Include="NAudio" Version="2.2.1" />
|
||||||
|
<PackageVersion Include="Serilog" Version="4.3.0" />
|
||||||
|
<PackageVersion Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||||
|
<PackageVersion Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||||
|
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
|
<PackageVersion Include="Spectre.Console" Version="0.50.0" />
|
||||||
|
<PackageVersion Include="Spectre.Console.Cli" Version="0.50.0" />
|
||||||
|
<PackageVersion Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.0.15" />
|
||||||
<PackageVersion Include="Whisper.net.AllRuntimes" Version="1.8.1" />
|
<PackageVersion Include="Whisper.net.AllRuntimes" Version="1.8.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
[*.cs]
|
[*.cs]
|
||||||
dotnet_diagnostic.CA2007.severity = none
|
dotnet_diagnostic.CA2007.severity = none
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
namespace StreamShorts.Console.Commands;
|
||||||
|
|
||||||
|
internal class DefaultCommand(
|
||||||
|
IFileSystem fileSystem,
|
||||||
|
IAnsiConsole console
|
||||||
|
) : AsyncCommand<DefaultCommand.Settings>
|
||||||
|
{
|
||||||
|
private readonly IFileSystem _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
|
||||||
|
private readonly IAnsiConsole _console = console ?? throw new ArgumentNullException(nameof(console));
|
||||||
|
|
||||||
|
internal class Settings : CommandSettings
|
||||||
|
{
|
||||||
|
[CommandArgument(0, "[Stream]")]
|
||||||
|
[Description("The path to the stream")]
|
||||||
|
public string Stream { get; init; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResult Validate(CommandContext context, Settings settings)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(settings.Stream))
|
||||||
|
{
|
||||||
|
return ValidationResult.Error("Stream path must be provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_fileSystem.File.Exists(settings.Stream) is false)
|
||||||
|
{
|
||||||
|
return ValidationResult.Error($"The specified stream file '{settings.Stream}' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileExtension = _fileSystem.Path.GetExtension(settings.Stream).ToUpperInvariant();
|
||||||
|
|
||||||
|
if (fileExtension != ".MP4")
|
||||||
|
{
|
||||||
|
return ValidationResult.Error("The specified stream file must be an .mp4 file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.Validate(context, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<int> ExecuteAsync(CommandContext context, Settings settings)
|
||||||
|
{
|
||||||
|
_console.Write($"[green]Processing stream:[/] {settings.Stream}");
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
namespace StreamShorts.Console.Hosting;
|
||||||
|
|
||||||
|
internal static class HostBuilderExtensions
|
||||||
|
{
|
||||||
|
public static CommandApp<DefaultCommand> BuildApp(this IHostBuilder builder)
|
||||||
|
{
|
||||||
|
var registrar = new TypeRegistrar(builder);
|
||||||
|
var app = new CommandApp<DefaultCommand>(registrar);
|
||||||
|
|
||||||
|
app.Configure(static c =>
|
||||||
|
c.SetExceptionHandler(static (ex, resolver) =>
|
||||||
|
{
|
||||||
|
var console = resolver?.Resolve(typeof(IAnsiConsole)) as IAnsiConsole;
|
||||||
|
console?.WriteLine($"[red]An error occurred while executing the command:[/]");
|
||||||
|
console?.WriteException(ex, ExceptionFormats.ShortenEverything);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
namespace StreamShorts.Console.Hosting;
|
||||||
|
|
||||||
|
internal class TypeRegistrar(IHostBuilder builder) : ITypeRegistrar
|
||||||
|
{
|
||||||
|
private readonly IHostBuilder _builder = builder;
|
||||||
|
|
||||||
|
public ITypeResolver Build()
|
||||||
|
{
|
||||||
|
return new TypeResolver(_builder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Register(Type service, Type implementation)
|
||||||
|
{
|
||||||
|
_builder.ConfigureServices((_, services) => services.AddSingleton(service, implementation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterInstance(Type service, object implementation)
|
||||||
|
{
|
||||||
|
_builder.ConfigureServices((_, services) => services.AddSingleton(service, implementation));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterLazy(Type service, Func<object> func)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(func);
|
||||||
|
|
||||||
|
_builder.ConfigureServices((_, services) => services.AddSingleton(service, _ => func()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace StreamShorts.Console.Hosting;
|
||||||
|
|
||||||
|
internal class TypeResolver(IHost provider) : ITypeResolver, IDisposable
|
||||||
|
{
|
||||||
|
private readonly IHost _host = provider ?? throw new ArgumentNullException(nameof(provider));
|
||||||
|
|
||||||
|
public object? Resolve(Type? type)
|
||||||
|
{
|
||||||
|
return type is not null ? _host.Services.GetService(type) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_host.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Diagnostics;
|
using System.Globalization;
|
||||||
using System.Globalization;
|
|
||||||
using System.Resources;
|
using System.Resources;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@@ -9,28 +8,47 @@ using FFMpegCore;
|
|||||||
using FFMpegCore.Enums;
|
using FFMpegCore.Enums;
|
||||||
using FFMpegCore.Pipes;
|
using FFMpegCore.Pipes;
|
||||||
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
|
|
||||||
using NAudio.Wave;
|
using NAudio.Wave;
|
||||||
|
|
||||||
using Whisper.net;
|
using Whisper.net;
|
||||||
using Whisper.net.Ggml;
|
using Whisper.net.Ggml;
|
||||||
|
|
||||||
var config = new ConfigurationBuilder()
|
Log.Logger = new LoggerConfiguration()
|
||||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
.WriteTo.File(
|
||||||
.Build();
|
formatter: new CompactJsonFormatter(),
|
||||||
|
path: Path.Combine(AppContext.BaseDirectory, "logs", "log.jsonl"),
|
||||||
|
rollingInterval: RollingInterval.Day
|
||||||
|
)
|
||||||
|
.Enrich.FromLogContext()
|
||||||
|
.MinimumLevel.Verbose()
|
||||||
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Fatal)
|
||||||
|
.CreateLogger();
|
||||||
|
|
||||||
var stopwatch = new Stopwatch();
|
try
|
||||||
stopwatch.Start();
|
|
||||||
var resourceManager = new ResourceManager("StreamShorts.Console.Resources.Resources", typeof(Program).Assembly);
|
|
||||||
|
|
||||||
Console.WriteLine(resourceManager.GetString("WelcomeMessage", CultureInfo.CurrentCulture));
|
|
||||||
|
|
||||||
// Step 1: Retrieve stream
|
|
||||||
if (args.Length is 0)
|
|
||||||
{
|
{
|
||||||
Console.WriteLine(resourceManager.GetString("StreamNotProvided", CultureInfo.CurrentCulture));
|
var appName = Assembly.GetExecutingAssembly().GetName().Name;
|
||||||
return;
|
Log.Information("Starting {AppName}", appName);
|
||||||
|
|
||||||
|
await Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureLogging(static l => l.ClearProviders())
|
||||||
|
.ConfigureServices(static (_, services) =>
|
||||||
|
{
|
||||||
|
services.AddSingleton(AnsiConsole.Console);
|
||||||
|
services.AddSingleton<IFileSystem, FileSystem>();
|
||||||
|
})
|
||||||
|
.BuildApp()
|
||||||
|
.RunAsync(args);
|
||||||
|
|
||||||
|
Log.Information("{AppName} has completed successfully.", appName);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Fatal(ex, "An unhandled exception occurred during execution.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await Log.CloseAndFlushAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
using var mp3Stream = new MemoryStream();
|
using var mp3Stream = new MemoryStream();
|
||||||
@@ -44,12 +62,6 @@ var wasExtracted = await FFMpegArguments
|
|||||||
)
|
)
|
||||||
.ProcessAsynchronously();
|
.ProcessAsynchronously();
|
||||||
|
|
||||||
if (wasExtracted is false)
|
|
||||||
{
|
|
||||||
Console.WriteLine(resourceManager.GetString("Mp3ExtractionFailed", CultureInfo.CurrentCulture));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: Convert MP3 stream to 16khz wave format
|
// Step 2: Convert MP3 stream to 16khz wave format
|
||||||
mp3Stream.Position = 0;
|
mp3Stream.Position = 0;
|
||||||
using var reader = new Mp3FileReader(mp3Stream);
|
using var reader = new Mp3FileReader(mp3Stream);
|
||||||
@@ -141,8 +153,6 @@ Here is the transcript of my YouTube live stream:
|
|||||||
{{completeTranscription}}
|
{{completeTranscription}}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
var apiKey = config["GeminiApiKey"];
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(apiKey))
|
if (string.IsNullOrWhiteSpace(apiKey))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("GeminiApiKey is not configured in appsettings.json.");
|
throw new InvalidOperationException("GeminiApiKey is not configured in appsettings.json.");
|
||||||
@@ -210,10 +220,6 @@ foreach (var result in analysis)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
stopwatch.Stop();
|
|
||||||
Console.WriteLine(stopwatch.Elapsed.Minutes);
|
|
||||||
|
|
||||||
// Step 6: Use analysis to generate a short video
|
// Step 6: Use analysis to generate a short video
|
||||||
|
|
||||||
record LLMAnalysis(
|
record LLMAnalysis(
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<root>
|
|
||||||
<data name="WelcomeMessage" xml:space="preserve">
|
|
||||||
<value>Welcome to StreamShorts!</value>
|
|
||||||
</data>
|
|
||||||
<data name="StreamNotProvided" xml:space="preserve">
|
|
||||||
<value>No stream provided. Please specify a stream to process.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Mp3ExtractionFailed" xml:space="preserve">
|
|
||||||
<value>Failed to extract MP3 from the provided stream.</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
|
||||||
@@ -8,7 +8,15 @@
|
|||||||
<PackageReference Include="FFMpegCore" />
|
<PackageReference Include="FFMpegCore" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||||
<PackageReference Include="NAudio" />
|
<PackageReference Include="NAudio" />
|
||||||
|
<PackageReference Include="Serilog" />
|
||||||
|
<PackageReference Include="Serilog.Extensions.Hosting" />
|
||||||
|
<PackageReference Include="Serilog.Formatting.Compact" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" />
|
||||||
|
<PackageReference Include="Spectre.Console" />
|
||||||
|
<PackageReference Include="Spectre.Console.Cli" />
|
||||||
|
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" />
|
||||||
<PackageReference Include="Whisper.net.AllRuntimes" />
|
<PackageReference Include="Whisper.net.AllRuntimes" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
global using System.ComponentModel;
|
||||||
|
global using System.IO.Abstractions;
|
||||||
|
global using System.Reflection;
|
||||||
|
|
||||||
|
global using Microsoft.Extensions.Configuration;
|
||||||
|
global using Microsoft.Extensions.DependencyInjection;
|
||||||
|
global using Microsoft.Extensions.Hosting;
|
||||||
|
global using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
global using Serilog;
|
||||||
|
global using Serilog.Events;
|
||||||
|
global using Serilog.Formatting.Compact;
|
||||||
|
|
||||||
|
global using Spectre.Console;
|
||||||
|
global using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
global using StreamShorts.Console.Commands;
|
||||||
|
global using StreamShorts.Console.Hosting;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[*.cs]
|
||||||
|
dotnet_diagnostic.CA1707.severity = none
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
<PropertyGroup>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
</PropertyGroup>
|
||||||
</PropertyGroup>
|
<ItemGroup>
|
||||||
|
<PackageVersion Include="AwesomeAssertions" Version="9.1.0" />
|
||||||
<ItemGroup>
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||||
<PackageVersion Include="xunit.v3" Version="3.0.0" />
|
<PackageVersion Include="Spectre.Console.Testing" Version="0.50.0" />
|
||||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.2" />
|
<PackageVersion Include="xunit.v3" Version="3.0.0" />
|
||||||
</ItemGroup>
|
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.2" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AwesomeAssertions" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||||
|
<PackageReference Include="Moq" />
|
||||||
|
<PackageReference Include="Spectre.Console.Testing" />
|
||||||
<PackageReference Include="xunit.v3" />
|
<PackageReference Include="xunit.v3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" />
|
<PackageReference Include="xunit.runner.visualstudio" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
namespace StreamShorts.Console.Tests.Unit.Hosting;
|
||||||
|
|
||||||
|
public class TypeRegistrarTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldNotThrowShould()
|
||||||
|
{
|
||||||
|
var mockBuilder = new Mock<IHostBuilder>();
|
||||||
|
|
||||||
|
var action = () => new TypeRegistrar(mockBuilder.Object);
|
||||||
|
|
||||||
|
action.Should().NotThrow<Exception>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Build_WhenCalled_ItShouldReturnResolverAndBuildHost()
|
||||||
|
{
|
||||||
|
var mockHost = new Mock<IHost>();
|
||||||
|
var mockBuilder = new Mock<IHostBuilder>();
|
||||||
|
|
||||||
|
mockBuilder
|
||||||
|
.Setup(static b => b.Build())
|
||||||
|
.Returns(mockHost.Object);
|
||||||
|
|
||||||
|
var registrar = new TypeRegistrar(mockBuilder.Object);
|
||||||
|
|
||||||
|
var resolver = registrar.Build();
|
||||||
|
|
||||||
|
resolver.Should().BeOfType<TypeResolver>();
|
||||||
|
mockBuilder.Verify(static b => b.Build(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Register_WhenCalledWithType_ItShouldAddToContainer()
|
||||||
|
{
|
||||||
|
var builder = Host.CreateDefaultBuilder();
|
||||||
|
var registrar = new TypeRegistrar(builder);
|
||||||
|
registrar.Register(typeof(IService), typeof(ServiceImplementation));
|
||||||
|
|
||||||
|
using var host = builder.Build();
|
||||||
|
var service = host.Services.GetService<IService>();
|
||||||
|
|
||||||
|
service.Should().NotBeNull();
|
||||||
|
service.Should().BeOfType<ServiceImplementation>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RegisterInstance_WhenCalledWithInstance_ItShouldAddToContainer()
|
||||||
|
{
|
||||||
|
var builder = Host.CreateDefaultBuilder();
|
||||||
|
var registrar = new TypeRegistrar(builder);
|
||||||
|
var instance = new ServiceImplementation();
|
||||||
|
registrar.RegisterInstance(typeof(IService), instance);
|
||||||
|
|
||||||
|
using var host = builder.Build();
|
||||||
|
var service = host.Services.GetService<IService>();
|
||||||
|
|
||||||
|
service.Should().BeSameAs(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RegisterLazy_WhenCalledWithFunc_ItShouldAddToContainer()
|
||||||
|
{
|
||||||
|
var builder = Host.CreateDefaultBuilder();
|
||||||
|
var registrar = new TypeRegistrar(builder);
|
||||||
|
registrar.RegisterLazy(typeof(IService), static () => new ServiceImplementation());
|
||||||
|
|
||||||
|
using var host = builder.Build();
|
||||||
|
var service = host.Services.GetService<IService>();
|
||||||
|
|
||||||
|
service.Should().NotBeNull();
|
||||||
|
service.Should().BeOfType<ServiceImplementation>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RegisterLazy_WhenFuncIsNull_ItShouldThrow()
|
||||||
|
{
|
||||||
|
var builder = Host.CreateDefaultBuilder();
|
||||||
|
var registrar = new TypeRegistrar(builder);
|
||||||
|
|
||||||
|
var action = () => registrar.RegisterLazy(typeof(IService), null!);
|
||||||
|
|
||||||
|
action.Should().Throw<Exception>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface IService { }
|
||||||
|
private sealed class ServiceImplementation : IService { }
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
namespace StreamShorts.Console.Tests.Unit.Hosting;
|
||||||
|
|
||||||
|
public class TypeResolverTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalledWithNullHost_ItShouldThrowArgumentNullException()
|
||||||
|
{
|
||||||
|
var action = static () => new TypeResolver(null!);
|
||||||
|
|
||||||
|
action.Should().Throw<ArgumentNullException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Resolve_WhenTypeIsNull_ItShouldReturnNull()
|
||||||
|
{
|
||||||
|
var mockHost = new Mock<IHost>();
|
||||||
|
using var resolver = new TypeResolver(mockHost.Object);
|
||||||
|
|
||||||
|
var result = resolver.Resolve(null);
|
||||||
|
|
||||||
|
result.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Resolve_WhenCalledWithRegisteredType_ItShouldReturnAnInstance()
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddSingleton(new TestService());
|
||||||
|
|
||||||
|
var mockHost = new Mock<IHost>();
|
||||||
|
|
||||||
|
mockHost
|
||||||
|
.Setup(static h => h.Services)
|
||||||
|
.Returns(services.BuildServiceProvider());
|
||||||
|
|
||||||
|
using var resolver = new TypeResolver(mockHost.Object);
|
||||||
|
|
||||||
|
var result = resolver.Resolve(typeof(TestService));
|
||||||
|
|
||||||
|
result.Should().NotBeNull();
|
||||||
|
result.Should().BeOfType<TestService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Resolve_WhenCalledWithUnregisteredType_ItShouldReturnNull()
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
var mockHost = new Mock<IHost>();
|
||||||
|
|
||||||
|
mockHost
|
||||||
|
.Setup(static h => h.Services)
|
||||||
|
.Returns(services.BuildServiceProvider());
|
||||||
|
|
||||||
|
using var resolver = new TypeResolver(mockHost.Object);
|
||||||
|
|
||||||
|
var result = resolver.Resolve(typeof(TestService));
|
||||||
|
|
||||||
|
result.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Dispose_WhenCalled_ItShouldAlsoDisposeHost()
|
||||||
|
{
|
||||||
|
var mockHost = new Mock<IHost>();
|
||||||
|
var resolver = new TypeResolver(mockHost.Object);
|
||||||
|
|
||||||
|
resolver.Dispose();
|
||||||
|
|
||||||
|
mockHost.Verify(static h => h.Dispose(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TestService { }
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace StreamShorts.Console.Tests;
|
|
||||||
|
|
||||||
public class UnitTest1
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void Test1()
|
|
||||||
{
|
|
||||||
Assert.True(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
global using AwesomeAssertions;
|
||||||
|
|
||||||
|
global using Microsoft.Extensions.DependencyInjection;
|
||||||
|
global using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
global using Moq;
|
||||||
|
|
||||||
|
global using StreamShorts.Console.Hosting;
|
||||||
Reference in New Issue
Block a user