feat: Add UI benchmark driver and scenarios for graph interactions
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled

- Introduced `ui_bench_driver.mjs` to read scenarios and fixture manifest, generating a deterministic run plan.
- Created `ui_bench_plan.md` outlining the purpose, scope, and next steps for the benchmark.
- Added `ui_bench_scenarios.json` containing various scenarios for graph UI interactions.
- Implemented tests for CLI commands, ensuring bundle verification and telemetry defaults.
- Developed schemas for orchestrator components, including replay manifests and event envelopes.
- Added mock API for risk management, including listing and statistics functionalities.
- Implemented models for risk profiles and query options to support the new API.
This commit is contained in:
StellaOps Bot
2025-12-02 01:28:17 +02:00
parent 909d9b6220
commit 44171930ff
94 changed files with 3606 additions and 271 deletions

View File

@@ -8,6 +8,8 @@
<IsPackable>false</IsPackable>
<!-- Stay scoped: disable implicit restore sources beyond local nugets -->
<RestoreSources>$(StellaOpsLocalNuGetSource)</RestoreSources>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
<RestoreNoCache>true</RestoreNoCache>
</PropertyGroup>
<ItemGroup>

View File

@@ -79,8 +79,9 @@ public sealed class CycloneDxComposerTests
Assert.Equal(first.Inventory.ContentHash, first.Inventory.JsonSha256);
Assert.Equal(first.Inventory.ProtobufSha256, second.Inventory.ProtobufSha256);
Assert.Equal(first.Inventory.SerialNumber, second.Inventory.SerialNumber);
Assert.Null(first.Inventory.MerkleRoot);
Assert.False(string.IsNullOrWhiteSpace(first.Inventory.MerkleRoot));
Assert.Null(first.Inventory.CompositionUri);
Assert.Null(first.Inventory.CompositionRecipeUri);
Assert.NotNull(first.Usage);
Assert.NotNull(second.Usage);
@@ -88,8 +89,15 @@ public sealed class CycloneDxComposerTests
Assert.Equal(first.Usage.ContentHash, first.Usage.JsonSha256);
Assert.Equal(first.Usage.ProtobufSha256, second.Usage.ProtobufSha256);
Assert.Equal(first.Usage.SerialNumber, second.Usage.SerialNumber);
Assert.Null(first.Usage.MerkleRoot);
Assert.False(string.IsNullOrWhiteSpace(first.Usage.MerkleRoot));
Assert.Null(first.Usage.CompositionUri);
Assert.Null(first.Usage.CompositionRecipeUri);
Assert.Equal(first.Inventory.MerkleRoot, first.Usage.MerkleRoot);
Assert.Equal(first.Inventory.MerkleRoot, result.CompositionRecipeSha256);
Assert.Equal(first.Inventory.ContentHash.Length, first.Inventory.MerkleRoot!.Length);
Assert.Equal(result.CompositionRecipeSha256.Length, 64);
Assert.NotEmpty(result.CompositionRecipeJson);
}
private static SbomCompositionRequest BuildRequest()

View File

@@ -64,16 +64,16 @@ public sealed class ScannerArtifactPackageBuilderTests
var packageBuilder = new ScannerArtifactPackageBuilder();
var package = packageBuilder.Build(request.Image.ImageDigest, request.GeneratedAt, composition, bomIndex);
Assert.Equal(5, package.Artifacts.Length); // inventory JSON+PB, usage JSON+PB, index
var kinds = package.Manifest.Artifacts.Select(entry => entry.Kind).ToArray();
Assert.Equal(new[] { "bom-index", "sbom-inventory", "sbom-inventory", "sbom-usage", "sbom-usage" }, kinds);
var manifestJson = package.Manifest.ToJsonBytes();
using var document = JsonDocument.Parse(manifestJson);
var root = document.RootElement;
Assert.Equal("sha256:image", root.GetProperty("imageDigest").GetString());
Assert.Equal(5, root.GetProperty("artifacts").GetArrayLength());
Assert.Equal(6, package.Artifacts.Length); // inventory JSON+PB, usage JSON+PB, index, composition recipe
var kinds = package.Manifest.Artifacts.Select(entry => entry.Kind).ToArray();
Assert.Equal(new[] { "bom-index", "composition-recipe", "sbom-inventory", "sbom-inventory", "sbom-usage", "sbom-usage" }, kinds);
var manifestJson = package.Manifest.ToJsonBytes();
using var document = JsonDocument.Parse(manifestJson);
var root = document.RootElement;
Assert.Equal("sha256:image", root.GetProperty("imageDigest").GetString());
Assert.Equal(6, root.GetProperty("artifacts").GetArrayLength());
var usageEntry = root.GetProperty("artifacts").EnumerateArray().First(element => element.GetProperty("kind").GetString() == "sbom-usage");
Assert.Equal("application/vnd.cyclonedx+json; version=1.6; view=usage", usageEntry.GetProperty("mediaType").GetString());

View File

@@ -102,10 +102,12 @@ public sealed class SurfaceManifestStageExecutorTests
Assert.Equal(publisher.LastManifestDigest, result!.ManifestDigest);
Assert.Equal(result.DeterminismMerkleRoot, publisher.LastRequest!.DeterminismMerkleRoot);
Assert.Equal(4, cache.Entries.Count);
Assert.Equal(6, cache.Entries.Count);
Assert.Contains(cache.Entries.Keys, key => key.Namespace == "surface.artifacts.entrytrace.graph" && key.Tenant == "tenant-a");
Assert.Contains(cache.Entries.Keys, key => key.Namespace == "surface.artifacts.entrytrace.ndjson" && key.Tenant == "tenant-a");
Assert.Contains(cache.Entries.Keys, key => key.Namespace == "surface.artifacts.layer.fragments" && key.Tenant == "tenant-a");
Assert.Contains(cache.Entries.Keys, key => key.Namespace == "surface.artifacts.determinism.json" && key.Tenant == "tenant-a");
Assert.Contains(cache.Entries.Keys, key => key.Namespace == "surface.artifacts.composition.recipe" && key.Tenant == "tenant-a");
Assert.Contains(cache.Entries.Keys, key => key.Namespace == "surface.manifests" && key.Tenant == "tenant-a");
var publishedMetrics = listener.Measurements
@@ -114,7 +116,7 @@ public sealed class SurfaceManifestStageExecutorTests
Assert.Single(publishedMetrics);
Assert.Equal(1, publishedMetrics[0].Value);
Assert.Equal("published", publishedMetrics[0]["surface.result"]);
Assert.Equal(3, Convert.ToInt32(publishedMetrics[0]["surface.payload_count"]));
Assert.Equal(5, Convert.ToInt32(publishedMetrics[0]["surface.payload_count"]));
var payloadMetrics = listener.Measurements
.Where(m => m.InstrumentName == "scanner_worker_surface_payload_persisted_total")
@@ -608,7 +610,8 @@ public sealed class SurfaceManifestStageExecutorTests
WorkerInstance = request.WorkerInstance,
Attempt = request.Attempt
},
Artifacts = artifacts
Artifacts = artifacts,
DeterminismMerkleRoot = request.DeterminismMerkleRoot
};
var manifestBytes = JsonSerializer.SerializeToUtf8Bytes(document, _options);