< Summary - Kestrun — Combined Coverage

Information
Class: OpenApiExtensionAttribute
Assembly: Kestrun.Annotations
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun.Annotations/OpenApi/Paths/OpenApiExtensionAttribute.cs
Tag: Kestrun/Kestrun@ca54e35c77799b76774b3805b6f075cdbc0c5fbe
Line coverage
46%
Covered lines: 20
Uncovered lines: 23
Coverable lines: 43
Total lines: 120
Line coverage: 46.5%
Branch coverage
60%
Covered branches: 6
Total branches: 10
Branch coverage: 60%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 01/18/2026 - 06:40:41 Line coverage: 51.2% (20/39) Branch coverage: 60% (6/10) Total lines: 110 Tag: Kestrun/Kestrun@99e92690d0fd95f6f4896f3410d2c024350a979401/18/2026 - 21:37:07 Line coverage: 46.5% (20/43) Branch coverage: 60% (6/10) Total lines: 120 Tag: Kestrun/Kestrun@99c4ae445e8e5afc8b7080e01d5d9cdf39f972b8 01/18/2026 - 06:40:41 Line coverage: 51.2% (20/39) Branch coverage: 60% (6/10) Total lines: 110 Tag: Kestrun/Kestrun@99e92690d0fd95f6f4896f3410d2c024350a979401/18/2026 - 21:37:07 Line coverage: 46.5% (20/43) Branch coverage: 60% (6/10) Total lines: 120 Tag: Kestrun/Kestrun@99c4ae445e8e5afc8b7080e01d5d9cdf39f972b8

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Name()100%11100%
get_Json()100%11100%
.ctor()100%210%
.ctor(...)100%44100%
.ctor(...)0%620%
.ctor(...)100%210%
.ctor(...)100%210%
ValidateName(...)50%7442.85%
IsValidJson(...)100%11100%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun.Annotations/OpenApi/Paths/OpenApiExtensionAttribute.cs

#LineLine coverage
 1using System.Globalization;
 2using System.Text.Json;
 3
 4/// <summary>
 5/// Attribute to specify OpenAPI vendor extensions (x-*) on an API operation.
 6/// </summary>
 7[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
 8public sealed class OpenApiExtensionAttribute : KestrunAnnotation
 9{
 10    /// <summary>
 11    /// The extension name. Must start with "x-".
 12    /// </summary>
 913    public string Name { get; set; }
 14
 15    /// <summary>
 16    /// Raw JSON value for the extension.
 17    /// </summary>
 618    public string Json { get; set; }
 19
 20    /// <summary>
 21    /// Creates an OpenAPI extension with no value.
 22    /// </summary>
 023    public OpenApiExtensionAttribute()
 24    {
 25        // Default constructor
 026        Name = string.Empty;
 027        Json = "null";
 028    }
 29
 30    /// <summary>
 31    /// Creates an OpenAPI extension.
 32    /// If <paramref name="value"/> is valid JSON, it is used as-is.
 33    /// Otherwise, it is treated as a string literal and JSON-encoded.
 34    /// </summary>
 335    public OpenApiExtensionAttribute(string name, string value)
 36    {
 337        ValidateName(name);
 338        Name = name;
 39
 340        if (value is null)
 41        {
 142            Json = "null";
 143            return;
 44        }
 45
 46        // Treat as string literal when not valid JSON
 247        Json = IsValidJson(value)
 248            ? value
 249            : JsonSerializer.Serialize(value);
 250    }
 51
 52    /// <summary>
 53    /// Creates an OpenAPI extension with a boolean value.
 54    /// </summary>
 055    public OpenApiExtensionAttribute(string name, bool value)
 56    {
 057        ValidateName(name);
 058        Name = name;
 059        Json = value ? "true" : "false";
 060    }
 61
 62    /// <summary>
 63    /// Creates an OpenAPI extension with an integer value.
 64    /// </summary>
 065    public OpenApiExtensionAttribute(string name, int value)
 66    {
 067        ValidateName(name);
 068        Name = name;
 069        Json = value.ToString(CultureInfo.InvariantCulture);
 070    }
 71
 72    /// <summary>
 73    /// Creates an OpenAPI extension with a numeric value.
 74    /// </summary>
 075    public OpenApiExtensionAttribute(string name, double value)
 76    {
 077        ValidateName(name);
 078        Name = name;
 079        Json = value.ToString(CultureInfo.InvariantCulture);
 080    }
 81
 82    /// <summary>
 83    /// Validates the extension name.
 84    /// </summary>
 85    /// <param name="name">The extension name to validate.</param>
 86    /// <exception cref="ArgumentException">Thrown when the extension name is null, empty, or does not start with "x-".<
 87    private static void ValidateName(string name)
 88    {
 389        if (string.IsNullOrWhiteSpace(name))
 90        {
 091            throw new ArgumentException("Extension name cannot be null or empty.", nameof(name));
 92        }
 93
 394        if (!name.StartsWith("x-", StringComparison.Ordinal))
 95        {
 096            throw new ArgumentException(
 097                $"OpenAPI extension '{name}' is invalid. Extension names must start with 'x-'.",
 098                nameof(name));
 99        }
 3100    }
 101
 102    /// <summary>
 103    /// Validates whether a string is valid JSON.
 104    /// </summary>
 105    /// <param name="value"> The string to validate as JSON. </param>
 106    /// <returns> True if the string is valid JSON; otherwise, false. </returns>
 107    private static bool IsValidJson(string value)
 108    {
 109        try
 110        {
 2111            using var _ = JsonDocument.Parse(value);
 1112            return true;
 113        }
 1114        catch (JsonException)
 115        {
 1116            return false;
 117        }
 2118    }
 119}
 120