< Summary

Information
Class: Pomodoro.Web.Services.WebAssemblyHostEnvironmentWrapper
Assembly: Pomodoro.Web
File(s): /home/runner/work/Pomodoro/Pomodoro/src/Pomodoro.Web/Services/ApplicationStartupService.cs
Line coverage
100%
Covered lines: 5
Uncovered lines: 0
Coverable lines: 5
Total lines: 253
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_BaseAddress()100%11100%

File(s)

/home/runner/work/Pomodoro/Pomodoro/src/Pomodoro.Web/Services/ApplicationStartupService.cs

#LineLine coverage
 1using Microsoft.AspNetCore.Components;
 2using Microsoft.AspNetCore.Components.Web;
 3using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
 4using Microsoft.Extensions.Logging;
 5using Pomodoro.Web;
 6
 7namespace Pomodoro.Web.Services;
 8
 9/// <summary>
 10/// Interface for WebAssemblyHostBuilder to enable testing
 11/// </summary>
 12public interface IHostBuilderWrapper
 13{
 14    IServiceCollection Services { get; }
 15    IHostEnvironmentWrapper HostEnvironment { get; }
 16    void AddRootComponent<TComponent>(string selector) where TComponent : IComponent;
 17}
 18
 19/// <summary>
 20/// Interface for WebAssemblyHostEnvironment to enable testing
 21/// </summary>
 22public interface IHostEnvironmentWrapper
 23{
 24    string BaseAddress { get; }
 25}
 26
 27/// <summary>
 28/// Wrapper implementation for WebAssemblyHostBuilder
 29/// </summary>
 30public class WebAssemblyHostBuilderWrapper : IHostBuilderWrapper
 31{
 32    private readonly WebAssemblyHostBuilder? _builder;
 33    private readonly IServiceCollection? _testServices;
 34    private readonly IWebAssemblyHostEnvironment? _testEnvironment;
 35    private readonly List<(Type ComponentType, string Selector)> _addedRootComponents = new();
 36
 37    public WebAssemblyHostBuilderWrapper(WebAssemblyHostBuilder builder)
 38    {
 39        _builder = builder;
 40    }
 41
 42    internal WebAssemblyHostBuilderWrapper(IServiceCollection services, IWebAssemblyHostEnvironment environment)
 43    {
 44        _testServices = services;
 45        _testEnvironment = environment;
 46    }
 47
 48    public IServiceCollection Services => _builder?.Services ?? _testServices!;
 49    public IHostEnvironmentWrapper HostEnvironment => new WebAssemblyHostEnvironmentWrapper(_builder?.HostEnvironment ??
 50
 51    public void AddRootComponent<TComponent>(string selector) where TComponent : IComponent
 52    {
 53        if (_builder != null)
 54        {
 55            _builder.RootComponents.Add<TComponent>(selector);
 56        }
 57        _addedRootComponents.Add((typeof(TComponent), selector));
 58    }
 59
 60    internal IReadOnlyList<(Type ComponentType, string Selector)> AddedRootComponents => _addedRootComponents;
 61}
 62
 63/// <summary>
 64/// Wrapper implementation for WebAssemblyHostEnvironment
 65/// </summary>
 66public class WebAssemblyHostEnvironmentWrapper : IHostEnvironmentWrapper
 67{
 68    private readonly IWebAssemblyHostEnvironment _environment;
 69
 570    public WebAssemblyHostEnvironmentWrapper(IWebAssemblyHostEnvironment environment)
 571    {
 572        _environment = environment;
 573    }
 74
 475    public string BaseAddress => _environment.BaseAddress;
 76}
 77
 78/// <summary>
 79/// Service responsible for handling application startup and host configuration
 80/// </summary>
 81public class ApplicationStartupService : IApplicationStartupService
 82{
 83    private readonly ILogger<ApplicationStartupService>? _logger;
 84    private readonly ILogger<ServiceRegistrationService>? _serviceRegistrationLogger;
 85
 86    public ApplicationStartupService(ILogger<ApplicationStartupService>? logger = null, ILogger<ServiceRegistrationServi
 87    {
 88        _logger = logger;
 89        _serviceRegistrationLogger = serviceRegistrationLogger;
 90    }
 91
 92    /// <summary>
 93    /// Configures the WebAssemblyHostBuilder with root components and services
 94    /// </summary>
 95    /// <param name="builder">The WebAssemblyHostBuilder to configure</param>
 96    [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
 97    public virtual void ConfigureHostBuilder(WebAssemblyHostBuilder builder)
 98    {
 99        ConfigureHostBuilder(new WebAssemblyHostBuilderWrapper(builder));
 100    }
 101
 102    /// <summary>
 103    /// Configures the WebAssemblyHostBuilder with root components and services (testable overload)
 104    /// </summary>
 105    /// <param name="builder">The wrapped builder to configure</param>
 106    public virtual void ConfigureHostBuilder(IHostBuilderWrapper builder)
 107    {
 108        ConfigureHostBuilderInternal(builder);
 109    }
 110
 111    /// <summary>
 112    /// Internal implementation that uses the wrapper interface for testability
 113    /// </summary>
 114    /// <param name="builder">The wrapped builder to configure</param>
 115    protected virtual void ConfigureHostBuilderInternal(IHostBuilderWrapper builder)
 116    {
 117        // Add root components
 118        builder.AddRootComponent<App>(Constants.Blazor.AppRootSelector);
 119        builder.AddRootComponent<HeadOutlet>(Constants.Blazor.HeadOutletSelector);
 120
 121        // Add HttpClient
 122        ConfigureHttpClient(builder.Services, builder.HostEnvironment.BaseAddress);
 123
 124        // Configure logging
 125        ConfigureLogging(builder.Services);
 126
 127        // Register infrastructure services for testability
 128        RegisterInfrastructureServices(builder.Services);
 129
 130        // Register all application services
 131        RegisterApplicationServices(builder.Services);
 132    }
 133
 134    /// <summary>
 135    /// Configures services for application (extracted for testability)
 136    /// This method contains the core service configuration logic from ConfigureHostBuilder
 137    /// </summary>
 138    /// <param name="services">The service collection to configure</param>
 139    /// <param name="baseAddress">The base address for HttpClient</param>
 140    public virtual void ConfigureServices(IServiceCollection services, string baseAddress)
 141    {
 142        // Add HttpClient
 143        ConfigureHttpClient(services, baseAddress);
 144
 145        // Configure logging
 146        ConfigureLogging(services);
 147
 148        // Register infrastructure services for testability
 149        RegisterInfrastructureServices(services);
 150
 151        // Register all application services
 152        RegisterApplicationServices(services);
 153    }
 154
 155    /// <summary>
 156    /// Configures the HTTP client with the base address
 157    /// </summary>
 158    protected virtual void ConfigureHttpClient(IServiceCollection services, string baseAddress)
 159    {
 160        services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(baseAddress) });
 161    }
 162
 163    /// <summary>
 164    /// Initializes and runs the application host
 165    /// </summary>
 166    /// <param name="builder">The configured WebAssemblyHostBuilder</param>
 167    /// <returns>A task representing the asynchronous operation</returns>
 168    [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
 169    public virtual async Task InitializeAndRunHostAsync(WebAssemblyHostBuilder builder)
 170    {
 171        var host = builder.Build();
 172
 173        // Initialize services with error handling
 174        await InitializeServicesWithErrorHandlingAsync(host.Services);
 175
 176        await host.RunAsync();
 177    }
 178
 179    /// <summary>
 180    /// Configures logging services
 181    /// </summary>
 182    /// <param name="services">The service collection to configure</param>
 183    protected virtual void ConfigureLogging(IServiceCollection services)
 184    {
 185        services.AddLogging(builder =>
 186        {
 187#if DEBUG
 188            builder.SetMinimumLevel(LogLevel.Information);
 189#else
 190            builder.SetMinimumLevel(LogLevel.Warning);
 191#endif
 192            builder.AddFilter(Constants.Logging.MicrosoftCategory, LogLevel.Warning);
 193            builder.AddFilter(Constants.Logging.SystemCategory, LogLevel.Warning);
 194        });
 195    }
 196
 197    /// <summary>
 198    /// Registers infrastructure services for testability
 199    /// </summary>
 200    /// <param name="services">The service collection to configure</param>
 201    protected virtual void RegisterInfrastructureServices(IServiceCollection services)
 202    {
 203        // Register service registration service for testability
 204        services.AddScoped<IServiceRegistrationService, ServiceRegistrationService>();
 205
 206        // Register service initialization service for testability
 207        services.AddScoped<IServiceInitializationService, ServiceInitializationService>();
 208
 209        // Register event wiring service for testability
 210        services.AddScoped<IEventWiringService, EventWiringService>();
 211
 212        // Register layout presenter service for testability
 213        services.AddScoped<LayoutPresenterService, LayoutPresenterService>();
 214
 215        // Register this service for testability
 216        services.AddScoped<IApplicationStartupService, ApplicationStartupService>();
 217    }
 218
 219    /// <summary>
 220    /// Registers all application services
 221    /// </summary>
 222    /// <param name="services">The service collection to configure</param>
 223    protected virtual void RegisterApplicationServices(IServiceCollection services)
 224    {
 225        // Register all application services through ServiceRegistrationService
 226        var serviceRegistration = new ServiceRegistrationService(_serviceRegistrationLogger);
 227        serviceRegistration.RegisterServices(services);
 228    }
 229
 230    /// <summary>
 231    /// Initializes services with error handling
 232    /// </summary>
 233    /// <param name="services">The service provider to retrieve services from</param>
 234    /// <returns>A task representing the asynchronous operation</returns>
 235    protected virtual async Task InitializeServicesWithErrorHandlingAsync(IServiceProvider services)
 236    {
 237        try
 238        {
 239            // Initialize services through ServiceInitializationService
 240            var serviceInitialization = services.GetRequiredService<IServiceInitializationService>();
 241            await serviceInitialization.InitializeServicesAsync(services);
 242
 243            // Wire up event subscribers through EventWiringService
 244            var eventWiring = services.GetRequiredService<IEventWiringService>();
 245            eventWiring.WireEventSubscribers(services);
 246        }
 247        catch (Exception ex)
 248        {
 249            // Log initialization error - app will still run but may have limited functionality
 250            _logger?.LogWarning(ex, Constants.Messages.ServiceInitializationFailed);
 251        }
 252    }
 253}