< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Forms.KrDiskPartSink
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Forms/KrPartSinks.cs
Tag: Kestrun/Kestrun@d9261bd752e45afa789d10bc0c82b7d5724d9589
Line coverage
100%
Covered lines: 28
Uncovered lines: 0
Coverable lines: 28
Total lines: 83
Line coverage: 100%
Branch coverage
100%
Covered branches: 14
Total branches: 14
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 02/05/2026 - 00:28:18 Line coverage: 100% (28/28) Branch coverage: 100% (14/14) Total lines: 83 Tag: Kestrun/Kestrun@d9261bd752e45afa789d10bc0c82b7d5724d9589 02/05/2026 - 00:28:18 Line coverage: 100% (28/28) Branch coverage: 100% (14/14) Total lines: 83 Tag: Kestrun/Kestrun@d9261bd752e45afa789d10bc0c82b7d5724d9589

Metrics

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

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Forms/KrPartSinks.cs

#LineLine coverage
 1using System.Security.Cryptography;
 2
 3namespace Kestrun.Forms;
 4
 5/// <summary>
 6/// Represents the result of writing a part to storage.
 7/// </summary>
 8public sealed record KrPartWriteResult
 9{
 10    /// <summary>
 11    /// Gets the path to the stored part.
 12    /// </summary>
 13    public required string TempPath { get; init; }
 14
 15    /// <summary>
 16    /// Gets the length of the stored part in bytes.
 17    /// </summary>
 18    public long Length { get; init; }
 19
 20    /// <summary>
 21    /// Gets the SHA-256 hash of the stored part, if computed.
 22    /// </summary>
 23    public string? Sha256 { get; init; }
 24}
 25
 26/// <summary>
 27/// Defines a streaming sink for multipart parts.
 28/// </summary>
 29public interface IKrPartSink
 30{
 31    /// <summary>
 32    /// Writes the part content to the sink.
 33    /// </summary>
 34    /// <param name="source">The source stream for the part.</param>
 35    /// <param name="cancellationToken">The cancellation token.</param>
 36    /// <returns>The write result.</returns>
 37    Task<KrPartWriteResult> WriteAsync(Stream source, CancellationToken cancellationToken);
 38}
 39
 40/// <summary>
 41/// Stores part contents on disk.
 42/// </summary>
 343public sealed class KrDiskPartSink(string destinationPath, bool computeSha256, string? fileName = null) : IKrPartSink
 44{
 345    private readonly string _destinationPath = destinationPath;
 346    private readonly bool _computeSha256 = computeSha256;
 347    private readonly string? _fileName = fileName;
 48
 49    /// <inheritdoc />
 50    public async Task<KrPartWriteResult> WriteAsync(Stream source, CancellationToken cancellationToken)
 51    {
 352        _ = Directory.CreateDirectory(_destinationPath);
 353        var fileName = string.IsNullOrWhiteSpace(_fileName)
 354            ? Path.GetRandomFileName()
 355            : _fileName;
 356        var uniqueName = string.IsNullOrWhiteSpace(_fileName)
 357            ? fileName
 358            : $"{Path.GetFileNameWithoutExtension(fileName)}-{Guid.NewGuid():N}{Path.GetExtension(fileName)}";
 359        var tempPath = Path.Combine(_destinationPath, uniqueName);
 60
 361        await using var fileStream = new FileStream(tempPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 8192
 62
 363        using var hasher = _computeSha256 ? IncrementalHash.CreateHash(HashAlgorithmName.SHA256) : null;
 364        var buffer = new byte[81920];
 365        long total = 0;
 66        int read;
 667        while ((read = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0)
 68        {
 369            await fileStream.WriteAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false);
 370            total += read;
 371            hasher?.AppendData(buffer, 0, read);
 72        }
 73
 374        var sha = hasher is null ? null : Convert.ToHexString(hasher.GetHashAndReset()).ToLowerInvariant();
 75
 376        return new KrPartWriteResult
 377        {
 378            TempPath = tempPath,
 379            Length = total,
 380            Sha256 = sha
 381        };
 382    }
 83}