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:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user