< Summary - Kestrun — Combined Coverage

Information
Class: DelegateBuilder
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Languages/DelegateBuilder.cs
Tag: Kestrun/Kestrun@2d87023b37eb91155071c91dd3d6a2eeb3004705
Line coverage
99%
Covered lines: 115
Uncovered lines: 1
Coverable lines: 116
Total lines: 234
Line coverage: 99.1%
Branch coverage
97%
Covered branches: 39
Total branches: 40
Branch coverage: 97.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 09/04/2025 - 17:02:01 Line coverage: 93% (107/115) Branch coverage: 85% (34/40) Total lines: 233 Tag: Kestrun/Kestrun@f3880b25ea131298aa2f8b1e0d0a8d55eb160bc009/06/2025 - 18:30:33 Line coverage: 97.3% (112/115) Branch coverage: 97.5% (39/40) Total lines: 233 Tag: Kestrun/Kestrun@aeddbedb8a96e9137aac94c2d5edd011b57ac87109/12/2025 - 03:43:11 Line coverage: 97.3% (112/115) Branch coverage: 97.5% (39/40) Total lines: 232 Tag: Kestrun/Kestrun@d160286e3020330b1eb862d66a37db2e26fc904210/13/2025 - 16:52:37 Line coverage: 99.1% (115/116) Branch coverage: 97.5% (39/40) Total lines: 234 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e 09/04/2025 - 17:02:01 Line coverage: 93% (107/115) Branch coverage: 85% (34/40) Total lines: 233 Tag: Kestrun/Kestrun@f3880b25ea131298aa2f8b1e0d0a8d55eb160bc009/06/2025 - 18:30:33 Line coverage: 97.3% (112/115) Branch coverage: 97.5% (39/40) Total lines: 233 Tag: Kestrun/Kestrun@aeddbedb8a96e9137aac94c2d5edd011b57ac87109/12/2025 - 03:43:11 Line coverage: 97.3% (112/115) Branch coverage: 97.5% (39/40) Total lines: 232 Tag: Kestrun/Kestrun@d160286e3020330b1eb862d66a37db2e26fc904210/13/2025 - 16:52:37 Line coverage: 99.1% (115/116) Branch coverage: 97.5% (39/40) Total lines: 234 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
PrepareExecutionAsync()92.85%141494.73%
ApplyResponseAsync()100%66100%
BuildBaselineReferences()100%1010100%
Add()100%22100%
NamespaceExistsInAssembly(...)100%88100%
.cctor()100%11100%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Languages/DelegateBuilder.cs

#LineLine coverage
 1using System.Security.Claims;
 2using System.Text;
 3using Kestrun.Languages;
 4using Kestrun.Logging;
 5using Kestrun.Models;
 6using Kestrun.SharedState;
 7using Microsoft.CodeAnalysis;
 8using Serilog.Events;
 9using System.Reflection;
 10using Kestrun.Hosting;
 11
 12internal static class DelegateBuilder
 13{
 14    /// <summary>
 15    /// Prepares the Kestrun context, response, and script globals for execution.
 16    /// Encapsulates request parsing, shared state snapshot, arg injection, and logging.
 17    /// </summary>
 18    /// <param name="host">The Kestrun host instance.</param>
 19    /// <param name="ctx">The current HTTP context.</param>
 20    /// <param name="args">Optional variables to inject into the globals.</param>
 21    /// <returns>Tuple containing the prepared CsGlobals, KestrunResponse, and KestrunContext.</returns>
 22    internal static async Task<(CsGlobals Globals, KestrunResponse Response, KestrunContext Context)> PrepareExecutionAs
 23        KestrunHost host,
 24        HttpContext ctx,
 25        Dictionary<string, object?>? args)
 26    {
 1927        var log = host.Logger;
 1928        var krRequest = await KestrunRequest.NewRequest(ctx).ConfigureAwait(false);
 1929        var krResponse = new KestrunResponse(krRequest);
 1930        var Context = new KestrunContext(host, krRequest, krResponse, ctx);
 1931        if (log.IsEnabled(LogEventLevel.Debug))
 32        {
 533            log.DebugSanitized("Kestrun context created for {Path}", ctx.Request.Path);
 34        }
 35
 36        // Create a shared state dictionary that will be used to store global variables
 37        // This will be shared across all requests and can be used to store state
 38        // that needs to persist across multiple requests
 1939        if (log.IsEnabled(LogEventLevel.Debug))
 40        {
 541            log.Debug("Creating shared state store for Kestrun context");
 42        }
 43
 1944        var glob = new Dictionary<string, object?>(SharedStateStore.Snapshot());
 1945        if (log.IsEnabled(LogEventLevel.Debug))
 46        {
 547            log.Debug("Shared state store created with {Count} items", glob.Count);
 48        }
 49
 50        // Inject the provided arguments into the globals so the script can access them
 1951        if (args != null && args.Count > 0)
 52        {
 153            if (log.IsEnabled(LogEventLevel.Debug))
 54            {
 055                log.Debug("Setting variables from arguments: {Count}", args.Count);
 56            }
 57
 458            foreach (var kv in args)
 59            {
 160                glob[kv.Key] = kv.Value; // add args to globals
 61            }
 62        }
 63
 64        // Create a new CsGlobals instance with the current context and shared state
 1965        var globals = new CsGlobals(glob, Context);
 1966        return (globals, krResponse, Context);
 1967    }
 68
 69
 70    /// <summary>
 71    /// Decides the VB return type string that matches TResult.
 72    /// </summary>
 73    /// <param name="ctx">The current HTTP context.</param>
 74    /// <param name="response">The Kestrun response to apply.</param>
 75    /// <param name="log">The logger to use for logging.</param>
 76    /// <returns>A task representing the asynchronous operation.</returns>
 77    internal static async Task ApplyResponseAsync(HttpContext ctx, KestrunResponse response, Serilog.ILogger log)
 78    {
 1479        if (log.IsEnabled(LogEventLevel.Debug))
 80        {
 581            log.DebugSanitized("Applying response to Kestrun context for {Path}", ctx.Request.Path);
 82        }
 83
 1484        if (!string.IsNullOrEmpty(response.RedirectUrl))
 85        {
 286            ctx.Response.Redirect(response.RedirectUrl);
 287            return;
 88        }
 89
 1290        await response.ApplyTo(ctx.Response).ConfigureAwait(false);
 91
 1292        if (log.IsEnabled(LogEventLevel.Debug))
 93        {
 494            log.DebugSanitized("Response applied to Kestrun context for {Path}", ctx.Request.Path);
 95        }
 1496    }
 97
 98    /// <summary>
 99    /// Common baseline references shared across script compilations.
 100    /// </summary>
 101    /// <returns> MetadataReference[] </returns>
 102    internal static MetadataReference[] BuildBaselineReferences()
 103    {
 104        // Collect unique assembly locations
 72105        var locations = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
 106
 107        void Add(Type t)
 108        {
 1080109            var loc = t.Assembly.Location;
 1080110            if (!string.IsNullOrWhiteSpace(loc))
 111            {
 1080112                _ = locations.Add(loc); // capture return to satisfy analyzer
 113            }
 1080114        }
 115
 116        // Seed core & always-required assemblies
 72117        Add(typeof(object));                                                              // System.Private.CoreLib
 72118        Add(typeof(Enumerable));                                                          // System.Linq
 72119        Add(typeof(HttpContext));                                                         // Microsoft.AspNetCore.Http
 72120        Add(typeof(Console));                                                             // System.Console
 72121        Add(typeof(StringBuilder));                                                       // System.Text
 72122        Add(typeof(Serilog.Log));                                                         // Serilog
 72123        Add(typeof(ClaimsPrincipal));                                                     // System.Security.Claims
 72124        Add(typeof(ClaimsIdentity));                                                      // System.Security.Claims
 72125        Add(typeof(System.Security.Cryptography.X509Certificates.X509Certificate2));      // X509 Certificates
 126
 127        // Snapshot loaded assemblies once
 72128        var loadedAssemblies = AppDomain.CurrentDomain
 72129            .GetAssemblies()
 21583130            .Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location))
 72131            .ToArray();
 132
 133        // Attempt to bind each namespace in PlatformImports to a loaded assembly
 6336134        foreach (var ns in PlatformImports)
 135        {
 1599342136            foreach (var asm in loadedAssemblies)
 137            {
 796575138                if (locations.Contains(asm.Location))
 139                {
 140                    continue; // already referenced
 141                }
 467888142                if (NamespaceExistsInAssembly(asm, ns))
 143                {
 12805144                    _ = locations.Add(asm.Location); // capture return to satisfy analyzer
 145                }
 146            }
 147        }
 148
 149        // Explicitly ensure critical assemblies needed for dynamic auth / razor / encodings even if not yet scanned
 72150        Add(typeof(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults));
 72151        Add(typeof(Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults));
 72152        Add(typeof(Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectDefaults));
 72153        Add(typeof(Microsoft.AspNetCore.Authentication.Negotiate.NegotiateDefaults));
 72154        Add(typeof(Microsoft.AspNetCore.Mvc.Razor.RazorPageBase));
 72155        Add(typeof(System.Text.Encodings.Web.HtmlEncoder));
 156
 72157        return [.. locations
 72158            .Distinct(StringComparer.OrdinalIgnoreCase)
 13381159            .Select(loc => MetadataReference.CreateFromFile(loc))];
 160    }
 161
 162    private static bool NamespaceExistsInAssembly(Assembly asm, string @namespace)
 163    {
 164        try
 165        {
 166            // Using DefinedTypes to avoid loading reflection-only contexts; guard against type load issues.
 106638317167            foreach (var t in asm.DefinedTypes)
 168            {
 52859178169                var ns = t.Namespace;
 52859178170                if (ns == null)
 171                {
 172                    continue;
 173                }
 50759356174                if (ns.Equals(@namespace, StringComparison.Ordinal) || ns.StartsWith(@namespace + ".", StringComparison.
 175                {
 12805176                    return true;
 177                }
 178            }
 452073179        }
 3010180        catch (ReflectionTypeLoadException)
 181        {
 182            // Ignore partially loadable assemblies; namespace absence treated as false.
 3010183        }
 455083184        return false;
 12805185    }
 186
 187    // Ordered & de-duplicated platform / framework imports used for dynamic script compilation.
 1188    internal static string[] PlatformImports = [
 1189        "Kestrun.Languages",
 1190        "Microsoft.AspNetCore.Authentication",
 1191        "Microsoft.AspNetCore.Authentication.Cookies",
 1192        "Microsoft.AspNetCore.Authentication.JwtBearer",
 1193        "Microsoft.AspNetCore.Authentication.Negotiate",
 1194        "Microsoft.AspNetCore.Authentication.OpenIdConnect",
 1195        "Microsoft.AspNetCore.Authorization",
 1196        "Microsoft.AspNetCore.Http",
 1197        "Microsoft.AspNetCore.Mvc",
 1198        "Microsoft.AspNetCore.Mvc.RazorPages",
 1199        // "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation".
 1200        "Microsoft.AspNetCore.Server.Kestrel.Core",
 1201        "Microsoft.AspNetCore.SignalR",
 1202        "Microsoft.CodeAnalysis",
 1203        "Microsoft.CodeAnalysis.CSharp",
 1204        "Microsoft.CodeAnalysis.CSharp.Scripting",
 1205        "Microsoft.CodeAnalysis.Scripting",
 1206        "Microsoft.Extensions.FileProviders",
 1207        "Microsoft.Extensions.Logging",
 1208        "Microsoft.Extensions.Options",
 1209        "Microsoft.Extensions.Primitives",
 1210        "Microsoft.IdentityModel.Tokens",
 1211        "Microsoft.PowerShell",
 1212        "Microsoft.VisualBasic",
 1213        "Serilog",
 1214        "Serilog.Events",
 1215        "System",
 1216        "System.Collections",
 1217        "System.Collections.Generic",
 1218        "System.Collections.Immutable",
 1219        "System.IO",
 1220        "System.Linq",
 1221        "System.Management.Automation",
 1222        "System.Management.Automation.Runspaces",
 1223        "System.Net",
 1224        "System.Net.Http.Headers",
 1225        "System.Reflection",
 1226        "System.Runtime.InteropServices",
 1227        "System.Security.Claims",
 1228        "System.Security.Cryptography.X509Certificates",
 1229        "System.Text",
 1230        "System.Text.Encodings.Web",
 1231        "System.Text.RegularExpressions",
 1232        "System.Threading.Tasks"
 1233    ];
 234}