< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Hosting.KestrunHostScriptValidationExtensions
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Hosting/KestrunHostScriptValidationExtensions.cs
Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb
Line coverage
82%
Covered lines: 37
Uncovered lines: 8
Coverable lines: 45
Total lines: 151
Line coverage: 82.2%
Branch coverage
71%
Covered branches: 20
Total branches: 28
Branch coverage: 71.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ValidateCSharpScript(...)81.25%161692.59%
IsCSharpScriptValid(...)100%11100%
GetCSharpScriptErrors(...)58.33%201262.5%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Hosting/KestrunHostScriptValidationExtensions.cs

#LineLine coverage
 1using System.Collections.Immutable;
 2using System.Reflection;
 3using System.Text;
 4using Kestrun.Languages;
 5using Microsoft.CodeAnalysis;
 6using Microsoft.CodeAnalysis.CSharp;
 7using Microsoft.CodeAnalysis.CSharp.Scripting;
 8using Microsoft.CodeAnalysis.Scripting;
 9using Serilog.Events;
 10using Kestrun.Models;
 11
 12
 13
 14namespace Kestrun.Hosting;
 15
 16/// <summary>
 17/// Provides extension methods for validating C# scripts in the context of a KestrunHost.
 18/// </summary>
 19public static class KestrunHostScriptValidationExtensions
 20{
 21    /// <summary>
 22    /// Validates a C# script and returns compilation diagnostics without throwing exceptions.
 23    /// Useful for testing scripts before adding routes.
 24    /// </summary>
 25    /// <param name="host">The KestrunHost instance used for validation</param>
 26    /// <param name="code">The C# script code to validate</param>
 27    /// <param name="extraImports">Optional additional imports</param>
 28    /// <param name="extraRefs">Optional additional assembly references</param>
 29    /// <param name="languageVersion">C# language version to use</param>
 30    /// <returns>Compilation diagnostics including errors and warnings</returns>
 31    public static ImmutableArray<Diagnostic> ValidateCSharpScript(
 32        this KestrunHost host,
 33        string? code,
 34        string[]? extraImports = null,
 35        Assembly[]? extraRefs = null,
 36        LanguageVersion languageVersion = LanguageVersion.CSharp12)
 37    {
 1838        if (host.HostLogger.IsEnabled(LogEventLevel.Debug))
 39        {
 1840            host.HostLogger.Debug("ValidateCSharpScript() called: {@CodeLength}, imports={ImportsCount}, refs={RefsCount
 1841                code?.Length, extraImports?.Length ?? 0, extraRefs?.Length ?? 0, languageVersion);
 42        }
 43
 44        try
 45        {
 46            // Use the same script options as BuildCsDelegate
 1847            var opts = ScriptOptions.Default
 1848                       .WithImports("System", "System.Linq", "System.Threading.Tasks", "Microsoft.AspNetCore.Http")
 1849                       .WithReferences(typeof(HttpContext).Assembly, typeof(KestrunResponse).Assembly)
 1850                       .WithLanguageVersion(languageVersion);
 51
 1852            if (extraImports is { Length: > 0 })
 53            {
 154                opts = opts.WithImports(opts.Imports.Concat(extraImports));
 55            }
 56
 1857            if (extraRefs is { Length: > 0 })
 58            {
 059                opts = opts.WithReferences(opts.MetadataReferences
 060                                              .Concat(extraRefs.Select(r => MetadataReference.CreateFromFile(r.Location)
 61            }
 62
 1863            var script = CSharpScript.Create(code, opts, typeof(CsGlobals));
 1764            return script.Compile();
 65        }
 166        catch (Exception ex)
 67        {
 68            // If there's an exception during script creation, create a synthetic diagnostic
 169            var diagnostic = Diagnostic.Create(
 170                new DiagnosticDescriptor(
 171                    "KESTRUN001",
 172                    "Script validation error",
 173                    "Script validation failed: {0}",
 174                    "Compilation",
 175                    DiagnosticSeverity.Error,
 176                    true),
 177                Location.None,
 178                ex.Message);
 79
 180            return [diagnostic];
 81        }
 1882    }
 83
 84    /// <summary>
 85    /// Checks if a C# script has compilation errors.
 86    /// </summary>
 87    /// <param name="host">The KestrunHost instance used for validation</param>
 88    /// <param name="code">The C# script code to check</param>
 89    /// <param name="extraImports">Optional additional imports</param>
 90    /// <param name="extraRefs">Optional additional assembly references</param>
 91    /// <param name="languageVersion">C# language version to use</param>
 92    /// <returns>True if the script compiles without errors, false otherwise</returns>
 93    public static bool IsCSharpScriptValid(
 94        this KestrunHost host,
 95        string code,
 96        string[]? extraImports = null,
 97        Assembly[]? extraRefs = null,
 98        LanguageVersion languageVersion = LanguageVersion.CSharp12)
 99    {
 5100        var diagnostics = host.ValidateCSharpScript(code, extraImports, extraRefs, languageVersion);
 7101        return !diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error);
 102    }
 103
 104    /// <summary>
 105    /// Gets formatted error information for a C# script.
 106    /// </summary>
 107    /// <param name="host">The KestrunHost instance used for validation</param>
 108    /// <param name="code">The C# script code to check</param>
 109    /// <param name="extraImports">Optional additional imports</param>
 110    /// <param name="extraRefs">Optional additional assembly references</param>
 111    /// <param name="languageVersion">C# language version to use</param>
 112    /// <returns>Formatted error message, or null if no errors</returns>
 113    public static string? GetCSharpScriptErrors(this KestrunHost host,
 114        string code,
 115        string[]? extraImports = null,
 116        Assembly[]? extraRefs = null,
 117        LanguageVersion languageVersion = LanguageVersion.CSharp12)
 118    {
 3119        if (host.HostLogger.IsEnabled(LogEventLevel.Debug))
 120        {
 3121            host.HostLogger.Debug("GetCSharpScriptErrors() called: {@CodeLength}, imports={ImportsCount}, refs={RefsCoun
 3122                code?.Length, extraImports?.Length ?? 0, extraRefs?.Length ?? 0, languageVersion);
 123        }
 124
 3125        var diagnostics = host.ValidateCSharpScript(code, extraImports, extraRefs, languageVersion);
 5126        var errors = diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).ToArray();
 127
 3128        if (errors.Length == 0)
 129        {
 1130            return null;
 131        }
 132
 133        try
 134        {
 135            // Create a temporary exception to format the errors
 2136            var tempException = new Scripting.CompilationErrorException("Script validation errors:", diagnostics);
 2137            return tempException.GetDetailedErrorMessage();
 138        }
 0139        catch
 140        {
 141            // Fallback formatting if exception creation fails
 0142            var sb = new StringBuilder();
 0143            _ = sb.AppendLine($"Script has {errors.Length} compilation error(s):");
 0144            for (var i = 0; i < errors.Length; i++)
 145            {
 0146                _ = sb.AppendLine($"  {i + 1}. {errors[i].GetMessage()}");
 147            }
 0148            return sb.ToString();
 149        }
 2150    }
 151}