Add integration tests for Proof Chain and Reachability workflows

- Implement ProofChainTestFixture for PostgreSQL-backed integration tests.
- Create StellaOps.Integration.ProofChain project with necessary dependencies.
- Add ReachabilityIntegrationTests to validate call graph extraction and reachability analysis.
- Introduce ReachabilityTestFixture for managing corpus and fixture paths.
- Establish StellaOps.Integration.Reachability project with required references.
- Develop UnknownsWorkflowTests to cover the unknowns lifecycle: detection, ranking, escalation, and resolution.
- Create StellaOps.Integration.Unknowns project with dependencies for unknowns workflow.
This commit is contained in:
StellaOps Bot
2025-12-20 22:19:26 +02:00
parent 3c6e14fca5
commit efe9bd8cfe
86 changed files with 9616 additions and 323 deletions

View File

@@ -148,6 +148,69 @@ public sealed record OfflineKitRiskBundleRequest(
byte[] BundleBytes,
DateTimeOffset CreatedAt);
/// <summary>
/// Manifest entry for a reachability bundle in an offline kit.
/// Sprint: SPRINT_3500_0004_0001_cli_verbs - T5
/// </summary>
public sealed record OfflineKitReachabilityEntry(
[property: JsonPropertyName("kind")] string Kind,
[property: JsonPropertyName("exportId")] string ExportId,
[property: JsonPropertyName("bundleId")] string BundleId,
[property: JsonPropertyName("language")] string Language,
[property: JsonPropertyName("callGraphDigest")] string CallGraphDigest,
[property: JsonPropertyName("rootHash")] string RootHash,
[property: JsonPropertyName("artifact")] string Artifact,
[property: JsonPropertyName("checksum")] string Checksum,
[property: JsonPropertyName("createdAt")] DateTimeOffset CreatedAt)
{
public const string KindValue = "reachability-bundle";
}
/// <summary>
/// Request to add a reachability bundle to an offline kit.
/// Sprint: SPRINT_3500_0004_0001_cli_verbs - T5
/// </summary>
public sealed record OfflineKitReachabilityRequest(
string KitId,
string ExportId,
string BundleId,
string Language,
string CallGraphDigest,
string RootHash,
byte[] BundleBytes,
DateTimeOffset CreatedAt);
/// <summary>
/// Manifest entry for a corpus bundle in an offline kit.
/// Contains ground-truth data for reachability verification.
/// Sprint: SPRINT_3500_0004_0001_cli_verbs - T5
/// </summary>
public sealed record OfflineKitCorpusEntry(
[property: JsonPropertyName("kind")] string Kind,
[property: JsonPropertyName("exportId")] string ExportId,
[property: JsonPropertyName("corpusId")] string CorpusId,
[property: JsonPropertyName("version")] string Version,
[property: JsonPropertyName("rootHash")] string RootHash,
[property: JsonPropertyName("artifact")] string Artifact,
[property: JsonPropertyName("checksum")] string Checksum,
[property: JsonPropertyName("createdAt")] DateTimeOffset CreatedAt)
{
public const string KindValue = "corpus-bundle";
}
/// <summary>
/// Request to add a corpus bundle to an offline kit.
/// Sprint: SPRINT_3500_0004_0001_cli_verbs - T5
/// </summary>
public sealed record OfflineKitCorpusRequest(
string KitId,
string ExportId,
string CorpusId,
string Version,
string RootHash,
byte[] BundleBytes,
DateTimeOffset CreatedAt);
/// <summary>
/// Result of adding an entry to an offline kit.
/// </summary>

View File

@@ -16,6 +16,8 @@ public sealed class OfflineKitPackager
private const string BootstrapDir = "bootstrap";
private const string EvidenceDir = "evidence";
private const string RiskBundlesDir = "risk-bundles";
private const string ReachabilityDir = "reachability";
private const string CorpusDir = "corpus";
private const string ChecksumsDir = "checksums";
private const string ManifestFileName = "manifest.json";
@@ -24,6 +26,8 @@ public sealed class OfflineKitPackager
private const string BootstrapBundleFileName = "export-bootstrap-pack-v1.tgz";
private const string EvidenceBundleFileName = "export-portable-bundle-v1.tgz";
private const string RiskBundleFileName = "export-risk-bundle-v1.tgz";
private const string ReachabilityBundleFileName = "export-reachability-bundle-v1.tgz";
private const string CorpusBundleFileName = "export-corpus-bundle-v1.tgz";
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
{
@@ -153,6 +157,66 @@ public sealed class OfflineKitPackager
RiskBundleFileName);
}
/// <summary>
/// Adds a reachability bundle to the offline kit.
/// Sprint: SPRINT_3500_0004_0001_cli_verbs - T5
/// </summary>
public OfflineKitAddResult AddReachabilityBundle(
string outputDirectory,
OfflineKitReachabilityRequest request,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
if (string.IsNullOrWhiteSpace(outputDirectory))
{
throw new ArgumentException("Output directory must be provided.", nameof(outputDirectory));
}
cancellationToken.ThrowIfCancellationRequested();
// Include language in filename for multiple language support
var fileName = $"export-reachability-{request.Language}-v1.tgz";
var artifactRelativePath = Path.Combine(ReachabilityDir, fileName);
var checksumRelativePath = Path.Combine(ChecksumsDir, ReachabilityDir, $"{fileName}.sha256");
return WriteBundle(
outputDirectory,
request.BundleBytes,
artifactRelativePath,
checksumRelativePath,
fileName);
}
/// <summary>
/// Adds a corpus bundle to the offline kit.
/// Sprint: SPRINT_3500_0004_0001_cli_verbs - T5
/// </summary>
public OfflineKitAddResult AddCorpusBundle(
string outputDirectory,
OfflineKitCorpusRequest request,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
if (string.IsNullOrWhiteSpace(outputDirectory))
{
throw new ArgumentException("Output directory must be provided.", nameof(outputDirectory));
}
cancellationToken.ThrowIfCancellationRequested();
var artifactRelativePath = Path.Combine(CorpusDir, CorpusBundleFileName);
var checksumRelativePath = Path.Combine(ChecksumsDir, CorpusDir, $"{CorpusBundleFileName}.sha256");
return WriteBundle(
outputDirectory,
request.BundleBytes,
artifactRelativePath,
checksumRelativePath,
CorpusBundleFileName);
}
/// <summary>
/// Creates a manifest entry for an attestation bundle.
/// </summary>
@@ -216,6 +280,42 @@ public sealed class OfflineKitPackager
CreatedAt: request.CreatedAt);
}
/// <summary>
/// Creates a manifest entry for a reachability bundle.
/// Sprint: SPRINT_3500_0004_0001_cli_verbs - T5
/// </summary>
public OfflineKitReachabilityEntry CreateReachabilityEntry(OfflineKitReachabilityRequest request, string sha256Hash)
{
var fileName = $"export-reachability-{request.Language}-v1.tgz";
return new OfflineKitReachabilityEntry(
Kind: OfflineKitReachabilityEntry.KindValue,
ExportId: request.ExportId,
BundleId: request.BundleId,
Language: request.Language,
CallGraphDigest: request.CallGraphDigest,
RootHash: $"sha256:{request.RootHash}",
Artifact: Path.Combine(ReachabilityDir, fileName).Replace('\\', '/'),
Checksum: Path.Combine(ChecksumsDir, ReachabilityDir, $"{fileName}.sha256").Replace('\\', '/'),
CreatedAt: request.CreatedAt);
}
/// <summary>
/// Creates a manifest entry for a corpus bundle.
/// Sprint: SPRINT_3500_0004_0001_cli_verbs - T5
/// </summary>
public OfflineKitCorpusEntry CreateCorpusEntry(OfflineKitCorpusRequest request, string sha256Hash)
{
return new OfflineKitCorpusEntry(
Kind: OfflineKitCorpusEntry.KindValue,
ExportId: request.ExportId,
CorpusId: request.CorpusId,
Version: request.Version,
RootHash: $"sha256:{request.RootHash}",
Artifact: Path.Combine(CorpusDir, CorpusBundleFileName).Replace('\\', '/'),
Checksum: Path.Combine(ChecksumsDir, CorpusDir, $"{CorpusBundleFileName}.sha256").Replace('\\', '/'),
CreatedAt: request.CreatedAt);
}
/// <summary>
/// Writes or updates the offline kit manifest.
/// </summary>