< Summary

Information
Class: NexusLabs.Needlr.ServiceCollectionVerificationExtensions
Assembly: NexusLabs.Needlr
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr/ServiceCollectionVerificationExtensions.cs
Line coverage
93%
Covered lines: 41
Uncovered lines: 3
Coverable lines: 44
Total lines: 170
Line coverage: 93.1%
Branch coverage
95%
Covered branches: 23
Total branches: 24
Branch coverage: 95.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Verify(...)100%66100%
VerifyWithDiagnostics(...)100%66100%
ProcessVerificationIssues(...)91.66%141276.92%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr/ServiceCollectionVerificationExtensions.cs

#LineLine coverage
 1using Microsoft.Extensions.DependencyInjection;
 2
 3namespace NexusLabs.Needlr;
 4
 5/// <summary>
 6/// Extension methods for verifying service collection configuration.
 7/// </summary>
 8public static class ServiceCollectionVerificationExtensions
 9{
 10    /// <summary>
 11    /// Verifies the service collection configuration and throws if issues are configured to throw.
 12    /// </summary>
 13    /// <param name="services">The service collection to verify.</param>
 14    /// <param name="options">The verification options. Defaults to <see cref="VerificationOptions.Default"/>.</param>
 15    /// <returns>The same service collection for chaining.</returns>
 16    /// <exception cref="ContainerVerificationException">
 17    /// Thrown if verification issues are detected and the configured behavior is <see cref="VerificationBehavior.Throw"
 18    /// </exception>
 19    public static IServiceCollection Verify(
 20        this IServiceCollection services,
 21        VerificationOptions? options = null)
 22    {
 823        ArgumentNullException.ThrowIfNull(services);
 824        options ??= VerificationOptions.Default;
 25
 826        var issues = new List<VerificationIssue>();
 27
 28        // Check for lifetime mismatches
 829        if (options.LifetimeMismatchBehavior != VerificationBehavior.Silent)
 30        {
 731            var mismatches = services.DetectLifetimeMismatches();
 2432            foreach (var mismatch in mismatches)
 33            {
 534                issues.Add(new VerificationIssue(
 535                    Type: VerificationIssueType.LifetimeMismatch,
 536                    Message: $"Lifetime mismatch: {mismatch.ConsumerServiceType.Name} ({mismatch.ConsumerLifetime}) depe
 537                    DetailedMessage: mismatch.ToDetailedString(),
 538                    ConfiguredBehavior: options.LifetimeMismatchBehavior)
 539                {
 540                    InvolvedTypes = [mismatch.ConsumerServiceType, mismatch.DependencyServiceType]
 541                });
 42            }
 43        }
 44
 45        // Process issues based on configured behavior
 846        ProcessVerificationIssues(issues, options);
 47
 748        return services;
 49    }
 50
 51    /// <summary>
 52    /// Verifies the service collection and returns detailed diagnostic results.
 53    /// </summary>
 54    /// <param name="services">The service collection to verify.</param>
 55    /// <param name="options">The verification options. Defaults to <see cref="VerificationOptions.Default"/>.</param>
 56    /// <returns>A <see cref="VerificationResult"/> containing all detected issues.</returns>
 57    public static VerificationResult VerifyWithDiagnostics(
 58        this IServiceCollection services,
 59        VerificationOptions? options = null)
 60    {
 261        ArgumentNullException.ThrowIfNull(services);
 262        options ??= VerificationOptions.Default;
 63
 264        var issues = new List<VerificationIssue>();
 65
 66        // Check for lifetime mismatches
 267        if (options.LifetimeMismatchBehavior != VerificationBehavior.Silent)
 68        {
 269            var mismatches = services.DetectLifetimeMismatches();
 870            foreach (var mismatch in mismatches)
 71            {
 272                issues.Add(new VerificationIssue(
 273                    Type: VerificationIssueType.LifetimeMismatch,
 274                    Message: $"Lifetime mismatch: {mismatch.ConsumerServiceType.Name} ({mismatch.ConsumerLifetime}) depe
 275                    DetailedMessage: mismatch.ToDetailedString(),
 276                    ConfiguredBehavior: options.LifetimeMismatchBehavior)
 277                {
 278                    InvolvedTypes = [mismatch.ConsumerServiceType, mismatch.DependencyServiceType]
 279                });
 80            }
 81        }
 82
 283        return new VerificationResult(issues);
 84    }
 85
 86    private static void ProcessVerificationIssues(List<VerificationIssue> issues, VerificationOptions options)
 87    {
 1388        var issuesByBehavior = issues.GroupBy(i => i.ConfiguredBehavior);
 89
 2390        foreach (var group in issuesByBehavior)
 91        {
 492            switch (group.Key)
 93            {
 94                case VerificationBehavior.Warn:
 1495                    foreach (var issue in group)
 96                    {
 497                        if (options.IssueReporter is not null)
 98                        {
 499                            options.IssueReporter(issue);
 100                        }
 101                        else
 102                        {
 0103                            Console.Error.WriteLine($"[Needlr Warning] {issue.Message}");
 0104                            Console.Error.WriteLine(issue.DetailedMessage);
 0105                            Console.Error.WriteLine();
 106                        }
 107                    }
 108                    break;
 109
 110                case VerificationBehavior.Throw:
 1111                    var throwableIssues = group.ToList();
 1112                    if (throwableIssues.Count > 0)
 113                    {
 1114                        throw new ContainerVerificationException(throwableIssues);
 115                    }
 116                    break;
 117            }
 118        }
 7119    }
 120}
 121
 122/// <summary>
 123/// Result of container verification containing all detected issues.
 124/// </summary>
 125/// <param name="Issues">The list of verification issues detected.</param>
 126public sealed record VerificationResult(IReadOnlyList<VerificationIssue> Issues)
 127{
 128    /// <summary>
 129    /// Gets whether the container configuration is valid (no errors).
 130    /// </summary>
 131    public bool IsValid => !Issues.Any(i => i.ConfiguredBehavior == VerificationBehavior.Throw);
 132
 133    /// <summary>
 134    /// Throws <see cref="ContainerVerificationException"/> if any issues are present.
 135    /// </summary>
 136    public void ThrowIfInvalid()
 137    {
 138        if (Issues.Count > 0)
 139        {
 140            throw new ContainerVerificationException(Issues.ToList());
 141        }
 142    }
 143
 144    /// <summary>
 145    /// Generates a detailed diagnostic report of all issues.
 146    /// </summary>
 147    public string ToDetailedReport()
 148    {
 149        if (Issues.Count == 0)
 150        {
 151            return "✅ Container verification passed. No issues detected.";
 152        }
 153
 154        var sb = new System.Text.StringBuilder();
 155        sb.AppendLine($"❌ Container verification found {Issues.Count} issue(s):");
 156        sb.AppendLine();
 157
 158        foreach (var issue in Issues)
 159        {
 160            sb.AppendLine($"[{issue.Type}] {issue.Message}");
 161            if (!string.IsNullOrWhiteSpace(issue.DetailedMessage))
 162            {
 163                sb.AppendLine(issue.DetailedMessage);
 164            }
 165            sb.AppendLine();
 166        }
 167
 168        return sb.ToString();
 169    }
 170}