LANCommander/LANCommander.Launcher.IntegrationTests/Tests/LauncherDiCompositionTests.cs
Pat Hartl b183e4b18c Remove old launcher, rename Avalonia launcher
Also adds ARM builds for Linux + Windows launcher
2026-06-06 05:49:24 -05:00

85 lines
3.6 KiB
C#

using LANCommander.Launcher.Data;
using LANCommander.Launcher.Services;
using LANCommander.Launcher.Services.Extensions;
using LANCommander.SDK.Extensions;
using LANCommander.SDK.Providers;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;
namespace LANCommander.Launcher.IntegrationTests.Tests;
/// <summary>
/// Catches DI registration drift in the Avalonia launcher's service graph. If someone
/// removes/renames a registration that <c>AddLANCommanderLauncher()</c> promises, these
/// resolves throw and the test fails — well before a runtime crash would surface it.
///
/// Theory rather than a single Fact: each row builds its own ServiceProvider so a single
/// service that hangs/fails on resolve doesn't take the whole graph down with it. Tests
/// are also lighter to schedule across cores.
/// </summary>
public class LauncherDiCompositionTests
{
[Theory]
// FilterService is intentionally omitted — defined in LANCommander.Launcher.Services
// but not registered by AddLANCommanderLauncher() and not consumed by the Avalonia
// launcher today. Add it here once a future change wires it up.
[InlineData(typeof(GameService))]
[InlineData(typeof(LibraryService))]
[InlineData(typeof(InstallService))]
[InlineData(typeof(ImportService))]
[InlineData(typeof(AuthenticationService))]
[InlineData(typeof(CommandLineService))]
[InlineData(typeof(ProfileService))]
[InlineData(typeof(PlaySessionService))]
[InlineData(typeof(SaveService))]
public void Service_resolves_from_launcher_DI_graph(Type serviceType)
{
var services = BuildLauncherServices();
using var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
var instance = scope.ServiceProvider.GetService(serviceType);
instance.ShouldNotBeNull(
customMessage: $"{serviceType.Name} must resolve from the launcher DI graph. " +
"If this fails, AddLANCommanderLauncher() likely lost a registration.");
}
/// <summary>
/// Replicates the Avalonia launcher's <c>App.axaml.cs.ConfigureServices</c> with two swaps:
/// EF InMemory in place of file-backed SQLite, and a no-op <see cref="IServerConfigurationRefresher"/>
/// so no HTTP calls fire during construction.
/// </summary>
private static IServiceCollection BuildLauncherServices()
{
var services = new ServiceCollection();
services.AddLogging();
services.AddHttpClient();
services.AddOptions<Settings.Settings>().Configure(_ => { });
services.AddSingleton<IServerConfigurationRefresher>(NoopRefresher.Instance);
services.AddLANCommanderClient<Settings.Settings>();
services.AddLANCommanderLauncher();
// The launcher registers DatabaseContext using only EnableSensitiveDataLogging, so
// OnConfiguring would otherwise call UseSqlite via AppPaths. Replace with EF InMemory.
var dbDescriptors = services
.Where(s => s.ServiceType == typeof(DbContextOptions<DatabaseContext>) ||
s.ServiceType == typeof(DbContextOptions))
.ToList();
foreach (var d in dbDescriptors) services.Remove(d);
services.AddDbContext<DatabaseContext>(o => o.UseInMemoryDatabase($"di-{Guid.NewGuid()}"));
return services;
}
private sealed class NoopRefresher : IServerConfigurationRefresher
{
public static readonly NoopRefresher Instance = new();
public Task RefreshAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
}
}