215 lines
6.8 KiB
C#
215 lines
6.8 KiB
C#
// <copyright file="ITestEvidenceService.cs" company="StellaOps">
|
|
// Copyright (c) StellaOps. Licensed under BUSL-1.1.
|
|
// </copyright>
|
|
// Sprint: SPRINT_20260105_002_002_TEST_trace_replay_evidence
|
|
// Task: TREP-013, TREP-014
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
namespace StellaOps.Testing.Evidence;
|
|
|
|
/// <summary>
|
|
/// Links test executions to EvidenceLocker for audit-grade storage.
|
|
/// </summary>
|
|
public interface ITestEvidenceService
|
|
{
|
|
/// <summary>
|
|
/// Begin a test evidence session.
|
|
/// </summary>
|
|
/// <param name="metadata">Session metadata.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>The created session.</returns>
|
|
Task<TestEvidenceSession> BeginSessionAsync(
|
|
TestSessionMetadata metadata,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Record a test result within a session.
|
|
/// </summary>
|
|
/// <param name="session">The active session.</param>
|
|
/// <param name="result">The test result to record.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
Task RecordTestResultAsync(
|
|
TestEvidenceSession session,
|
|
TestResultRecord result,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Finalize session and store in EvidenceLocker.
|
|
/// </summary>
|
|
/// <param name="session">The session to finalize.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>The evidence bundle.</returns>
|
|
Task<TestEvidenceBundle> FinalizeSessionAsync(
|
|
TestEvidenceSession session,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Retrieve test evidence bundle for audit.
|
|
/// </summary>
|
|
/// <param name="bundleId">The bundle identifier.</param>
|
|
/// <param name="ct">Cancellation token.</param>
|
|
/// <returns>The evidence bundle, or null if not found.</returns>
|
|
Task<TestEvidenceBundle?> GetBundleAsync(
|
|
string bundleId,
|
|
CancellationToken ct = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Metadata about a test session.
|
|
/// </summary>
|
|
/// <param name="SessionId">Unique session identifier.</param>
|
|
/// <param name="TestSuiteId">Identifier for the test suite.</param>
|
|
/// <param name="GitCommit">Git commit hash.</param>
|
|
/// <param name="GitBranch">Git branch name.</param>
|
|
/// <param name="RunnerEnvironment">Description of the runner environment.</param>
|
|
/// <param name="StartedAt">When the session started.</param>
|
|
/// <param name="Labels">Additional labels.</param>
|
|
public sealed record TestSessionMetadata(
|
|
string SessionId,
|
|
string TestSuiteId,
|
|
string GitCommit,
|
|
string GitBranch,
|
|
string RunnerEnvironment,
|
|
DateTimeOffset StartedAt,
|
|
ImmutableDictionary<string, string> Labels);
|
|
|
|
/// <summary>
|
|
/// A recorded test result.
|
|
/// </summary>
|
|
/// <param name="TestId">Unique test identifier.</param>
|
|
/// <param name="TestName">Test method name.</param>
|
|
/// <param name="TestClass">Test class name.</param>
|
|
/// <param name="Outcome">Test outcome.</param>
|
|
/// <param name="Duration">Test duration.</param>
|
|
/// <param name="FailureMessage">Failure message, if failed.</param>
|
|
/// <param name="StackTrace">Stack trace, if failed.</param>
|
|
/// <param name="Categories">Test categories.</param>
|
|
/// <param name="BlastRadiusAnnotations">Blast radius annotations.</param>
|
|
/// <param name="Attachments">Attached file references.</param>
|
|
public sealed record TestResultRecord(
|
|
string TestId,
|
|
string TestName,
|
|
string TestClass,
|
|
TestOutcome Outcome,
|
|
TimeSpan Duration,
|
|
string? FailureMessage,
|
|
string? StackTrace,
|
|
ImmutableArray<string> Categories,
|
|
ImmutableArray<string> BlastRadiusAnnotations,
|
|
ImmutableDictionary<string, string> Attachments);
|
|
|
|
/// <summary>
|
|
/// Test outcome.
|
|
/// </summary>
|
|
public enum TestOutcome
|
|
{
|
|
Passed,
|
|
Failed,
|
|
Skipped,
|
|
Inconclusive
|
|
}
|
|
|
|
/// <summary>
|
|
/// A finalized test evidence bundle.
|
|
/// </summary>
|
|
/// <param name="BundleId">Unique bundle identifier.</param>
|
|
/// <param name="MerkleRoot">Merkle root for integrity verification.</param>
|
|
/// <param name="Metadata">Session metadata.</param>
|
|
/// <param name="Summary">Test summary.</param>
|
|
/// <param name="Results">All test results.</param>
|
|
/// <param name="FinalizedAt">When the bundle was finalized.</param>
|
|
/// <param name="EvidenceLockerRef">Reference to EvidenceLocker storage.</param>
|
|
public sealed record TestEvidenceBundle(
|
|
string BundleId,
|
|
string MerkleRoot,
|
|
TestSessionMetadata Metadata,
|
|
TestSummary Summary,
|
|
ImmutableArray<TestResultRecord> Results,
|
|
DateTimeOffset FinalizedAt,
|
|
string EvidenceLockerRef);
|
|
|
|
/// <summary>
|
|
/// Summary of test results.
|
|
/// </summary>
|
|
/// <param name="TotalTests">Total number of tests.</param>
|
|
/// <param name="Passed">Number of passed tests.</param>
|
|
/// <param name="Failed">Number of failed tests.</param>
|
|
/// <param name="Skipped">Number of skipped tests.</param>
|
|
/// <param name="TotalDuration">Total test duration.</param>
|
|
/// <param name="ResultsByCategory">Results grouped by category.</param>
|
|
/// <param name="ResultsByBlastRadius">Results grouped by blast radius.</param>
|
|
public sealed record TestSummary(
|
|
int TotalTests,
|
|
int Passed,
|
|
int Failed,
|
|
int Skipped,
|
|
TimeSpan TotalDuration,
|
|
ImmutableDictionary<string, int> ResultsByCategory,
|
|
ImmutableDictionary<string, int> ResultsByBlastRadius);
|
|
|
|
/// <summary>
|
|
/// An active test evidence session.
|
|
/// </summary>
|
|
public sealed class TestEvidenceSession
|
|
{
|
|
private readonly List<TestResultRecord> _results = [];
|
|
private readonly object _lock = new();
|
|
|
|
/// <summary>
|
|
/// Gets the session metadata.
|
|
/// </summary>
|
|
public TestSessionMetadata Metadata { get; }
|
|
|
|
/// <summary>
|
|
/// Gets whether the session is finalized.
|
|
/// </summary>
|
|
public bool IsFinalized { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="TestEvidenceSession"/> class.
|
|
/// </summary>
|
|
/// <param name="metadata">Session metadata.</param>
|
|
public TestEvidenceSession(TestSessionMetadata metadata)
|
|
{
|
|
Metadata = metadata;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a test result to the session.
|
|
/// </summary>
|
|
/// <param name="result">The result to add.</param>
|
|
public void AddResult(TestResultRecord result)
|
|
{
|
|
if (IsFinalized)
|
|
{
|
|
throw new InvalidOperationException("Cannot add results to a finalized session.");
|
|
}
|
|
|
|
lock (_lock)
|
|
{
|
|
_results.Add(result);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all results recorded in this session.
|
|
/// </summary>
|
|
/// <returns>Immutable array of results.</returns>
|
|
public ImmutableArray<TestResultRecord> GetResults()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
return [.. _results];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mark the session as finalized.
|
|
/// </summary>
|
|
internal void MarkAsFinalized()
|
|
{
|
|
IsFinalized = true;
|
|
}
|
|
}
|