Add Authority Advisory AI and API Lifecycle Configuration
- Introduced AuthorityAdvisoryAiOptions and related classes for managing advisory AI configurations, including remote inference options and tenant-specific settings. - Added AuthorityApiLifecycleOptions to control API lifecycle settings, including legacy OAuth endpoint configurations. - Implemented validation and normalization methods for both advisory AI and API lifecycle options to ensure proper configuration. - Created AuthorityNotificationsOptions and its related classes for managing notification settings, including ack tokens, webhooks, and escalation options. - Developed IssuerDirectoryClient and related models for interacting with the issuer directory service, including caching mechanisms and HTTP client configurations. - Added support for dependency injection through ServiceCollectionExtensions for the Issuer Directory Client. - Updated project file to include necessary package references for the new Issuer Directory Client library.
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Scanner.EntryTrace;
|
||||
using StellaOps.Scanner.EntryTrace.Serialization;
|
||||
using StellaOps.Scanner.Storage;
|
||||
using StellaOps.Scanner.Storage.Mongo;
|
||||
using StellaOps.Scanner.Storage.Repositories;
|
||||
using StellaOps.Scanner.Storage.Services;
|
||||
using Xunit;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace StellaOps.Scanner.Storage.Tests;
|
||||
|
||||
public sealed class EntryTraceResultStoreTests : IClassFixture<ScannerMongoFixture>
|
||||
{
|
||||
private readonly ScannerMongoFixture _fixture;
|
||||
|
||||
public EntryTraceResultStoreTests(ScannerMongoFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StoreAsync_ThrowsWhenResultNull()
|
||||
{
|
||||
var store = CreateStore();
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () =>
|
||||
{
|
||||
EntryTraceResult? result = null;
|
||||
await store.StoreAsync(result!, CancellationToken.None);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAsync_ReturnsNullWhenMissing()
|
||||
{
|
||||
await ClearCollectionAsync();
|
||||
var store = CreateStore();
|
||||
|
||||
var result = await store.GetAsync("scan-missing", CancellationToken.None);
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StoreAsync_RoundTripsResult()
|
||||
{
|
||||
await ClearCollectionAsync();
|
||||
var store = CreateStore();
|
||||
|
||||
var scanId = $"scan-{Guid.NewGuid():n}";
|
||||
var generatedAt = new DateTimeOffset(2025, 11, 2, 10, 30, 0, TimeSpan.Zero);
|
||||
|
||||
var node = new EntryTraceNode(
|
||||
1,
|
||||
EntryTraceNodeKind.Command,
|
||||
"python",
|
||||
ImmutableArray.Create("python", "/app/main.py"),
|
||||
EntryTraceInterpreterKind.None,
|
||||
new EntryTraceEvidence("/usr/bin/python", "sha256:layer-a", "path", ImmutableDictionary<string, string>.Empty),
|
||||
new EntryTraceSpan("/app/start.sh", 1, 0, 1, 14),
|
||||
ImmutableDictionary<string, string>.Empty);
|
||||
|
||||
var plan = new EntryTracePlan(
|
||||
ImmutableArray.Create("/app/main.py"),
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
"/workspace",
|
||||
"scanner",
|
||||
"/app/main.py",
|
||||
EntryTraceTerminalType.Native,
|
||||
"python",
|
||||
0.95,
|
||||
ImmutableDictionary<string, string>.Empty);
|
||||
|
||||
var terminal = new EntryTraceTerminal(
|
||||
"/app/main.py",
|
||||
EntryTraceTerminalType.Native,
|
||||
"python",
|
||||
0.95,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
"scanner",
|
||||
"/workspace",
|
||||
ImmutableArray.Create("/app/main.py"));
|
||||
|
||||
var graph = new EntryTraceGraph(
|
||||
EntryTraceOutcome.Resolved,
|
||||
ImmutableArray.Create(node),
|
||||
ImmutableArray<EntryTraceEdge>.Empty,
|
||||
ImmutableArray<EntryTraceDiagnostic>.Empty,
|
||||
ImmutableArray.Create(plan),
|
||||
ImmutableArray.Create(terminal));
|
||||
|
||||
var ndjson = EntryTraceNdjsonWriter.Serialize(
|
||||
graph,
|
||||
new EntryTraceNdjsonMetadata(scanId, "sha256:image", generatedAt, Source: "storage.tests"));
|
||||
|
||||
var result = new EntryTraceResult(scanId, "sha256:image", generatedAt, graph, ndjson);
|
||||
|
||||
await store.StoreAsync(result, CancellationToken.None);
|
||||
|
||||
var stored = await store.GetAsync(scanId, CancellationToken.None);
|
||||
|
||||
Assert.NotNull(stored);
|
||||
Assert.Equal(result.ScanId, stored!.ScanId);
|
||||
Assert.Equal(result.ImageDigest, stored.ImageDigest);
|
||||
Assert.Equal(result.GeneratedAtUtc, stored.GeneratedAtUtc);
|
||||
Assert.Equal(result.Graph, stored.Graph);
|
||||
Assert.Equal(result.Ndjson, stored.Ndjson);
|
||||
}
|
||||
|
||||
private async Task ClearCollectionAsync()
|
||||
{
|
||||
var provider = CreateProvider();
|
||||
await provider.EntryTrace.DeleteManyAsync(_ => true);
|
||||
}
|
||||
|
||||
private EntryTraceResultStore CreateStore()
|
||||
{
|
||||
var provider = CreateProvider();
|
||||
var repository = new EntryTraceRepository(provider);
|
||||
return new EntryTraceResultStore(repository);
|
||||
}
|
||||
|
||||
private MongoCollectionProvider CreateProvider()
|
||||
{
|
||||
var options = Options.Create(new ScannerStorageOptions
|
||||
{
|
||||
Mongo = new MongoOptions
|
||||
{
|
||||
ConnectionString = _fixture.Runner.ConnectionString,
|
||||
DatabaseName = _fixture.Database.DatabaseNamespace.DatabaseName,
|
||||
UseMajorityReadConcern = false,
|
||||
UseMajorityWriteConcern = false
|
||||
}
|
||||
});
|
||||
|
||||
return new MongoCollectionProvider(_fixture.Database, options);
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,7 @@ public sealed class RustFsArtifactObjectStoreTests
|
||||
}
|
||||
|
||||
// Materialize content to ensure downstream callers can inspect it.
|
||||
_ = await request.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false);
|
||||
_ = await request.Content.ReadAsByteArrayAsync(cancellationToken);
|
||||
}
|
||||
|
||||
CapturedRequests.Add(new CapturedRequest(request.Method, request.RequestUri!, headerSnapshot));
|
||||
|
||||
Reference in New Issue
Block a user