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;
///
/// Catches DI registration drift in the Avalonia launcher's service graph. If someone
/// removes/renames a registration that AddLANCommanderLauncher() 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.
///
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.");
}
///
/// Replicates the Avalonia launcher's App.axaml.cs.ConfigureServices with two swaps:
/// EF InMemory in place of file-backed SQLite, and a no-op
/// so no HTTP calls fire during construction.
///
private static IServiceCollection BuildLauncherServices()
{
var services = new ServiceCollection();
services.AddLogging();
services.AddHttpClient();
services.AddOptions().Configure(_ => { });
services.AddSingleton(NoopRefresher.Instance);
services.AddLANCommanderClient();
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) ||
s.ServiceType == typeof(DbContextOptions))
.ToList();
foreach (var d in dbDescriptors) services.Remove(d);
services.AddDbContext(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;
}
}