Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.
This commit is contained in:
@@ -68,13 +68,38 @@ public sealed class AirgapAndOrchestratorServiceTests
|
||||
|
||||
private sealed class InMemoryAirgapImportRepository : IAirgapImportRepository
|
||||
{
|
||||
private readonly List<AirgapImportRecord> _records = new();
|
||||
public AirgapImportRecord? LastRecord { get; private set; }
|
||||
|
||||
public Task InsertAsync(AirgapImportRecord record, CancellationToken cancellationToken)
|
||||
{
|
||||
LastRecord = record;
|
||||
_records.Add(record);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<AirgapImportRecord?> GetLatestByDomainAsync(string tenantId, string domainId, CancellationToken cancellationToken)
|
||||
{
|
||||
var record = _records
|
||||
.Where(r => r.TenantId == tenantId)
|
||||
.OrderByDescending(r => r.ImportedAt)
|
||||
.FirstOrDefault();
|
||||
return Task.FromResult(record);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<AirgapImportRecord>> GetAllLatestByDomainAsync(string tenantId, CancellationToken cancellationToken)
|
||||
{
|
||||
var records = _records
|
||||
.Where(r => r.TenantId == tenantId)
|
||||
.ToList();
|
||||
return Task.FromResult<IReadOnlyList<AirgapImportRecord>>(records);
|
||||
}
|
||||
|
||||
public Task<int> GetBundleCountByDomainAsync(string tenantId, string domainId, CancellationToken cancellationToken)
|
||||
{
|
||||
var count = _records.Count(r => r.TenantId == tenantId);
|
||||
return Task.FromResult(count);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class InMemoryOrchestratorExportRepository : IOrchestratorExportRepository
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);**/tools/**/*</DefaultItemExcludes>
|
||||
<DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
|
||||
<MSBuildProjectExtensionsPath>$(MSBuildThisFileDirectory)obj/$(MSBuildProjectName)/</MSBuildProjectExtensionsPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="**/tools/**/*.cs" />
|
||||
<None Remove="**/tools/**/*" />
|
||||
<None Include="**/tools/**/*" Pack="false" CopyToOutputDirectory="Never" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,3 +1,6 @@
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Findings.Ledger.Infrastructure.Postgres;
|
||||
using StellaOps.Findings.Ledger.Options;
|
||||
using StellaOps.Findings.Ledger.WebService.Contracts;
|
||||
using StellaOps.Findings.Ledger.WebService.Services;
|
||||
using Xunit;
|
||||
@@ -6,7 +9,17 @@ namespace StellaOps.Findings.Ledger.Tests.Exports;
|
||||
|
||||
public class ExportFiltersHashTests
|
||||
{
|
||||
private readonly ExportQueryService _service = new(new TestDataSource(), new Microsoft.Extensions.Logging.Abstractions.NullLogger<ExportQueryService>());
|
||||
private readonly ExportQueryService _service;
|
||||
|
||||
public ExportFiltersHashTests()
|
||||
{
|
||||
var options = Microsoft.Extensions.Options.Options.Create(new LedgerServiceOptions
|
||||
{
|
||||
Database = { ConnectionString = "Host=localhost;Username=test;Password=test;Database=test" }
|
||||
});
|
||||
var dataSource = new LedgerDataSource(options, NullLogger<LedgerDataSource>.Instance);
|
||||
_service = new ExportQueryService(dataSource, NullLogger<ExportQueryService>.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VexFiltersHash_IsDeterministic()
|
||||
@@ -33,16 +46,4 @@ public class ExportFiltersHashTests
|
||||
|
||||
Assert.Equal(left, right);
|
||||
}
|
||||
|
||||
private sealed class TestDataSource : StellaOps.Findings.Ledger.Infrastructure.Postgres.LedgerDataSource
|
||||
{
|
||||
public TestDataSource() : base(
|
||||
Microsoft.Extensions.Options.Options.Create(new StellaOps.Findings.Ledger.Options.LedgerServiceOptions
|
||||
{
|
||||
Database = { ConnectionString = "Host=localhost;Username=test;Password=test;Database=test" }
|
||||
}),
|
||||
new Microsoft.Extensions.Logging.Abstractions.NullLogger<StellaOps.Findings.Ledger.Infrastructure.Postgres.LedgerDataSource>())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +1,66 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
// FindingsLedgerIntegrationTests.cs
|
||||
// Sprint: SPRINT_5100_0010_0001_evidencelocker_tests
|
||||
// Task: FINDINGS-5100-005
|
||||
// Description: Integration test: event stream → ledger state → replay → verify identical state
|
||||
// Description: Integration test: event stream → ledger state → replay → verify identical state
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Findings.Ledger.Core.Domain;
|
||||
using StellaOps.Findings.Ledger.Core.Events;
|
||||
using StellaOps.Findings.Ledger.Core.Projection;
|
||||
using StellaOps.Findings.Ledger.Core.Repositories;
|
||||
|
||||
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.Findings.Ledger.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Integration Tests for Findings Ledger
|
||||
/// Task FINDINGS-5100-005: event stream → ledger state → replay → verify identical state
|
||||
/// Task FINDINGS-5100-005: event stream → ledger state → replay → verify identical state
|
||||
/// </summary>
|
||||
public sealed class FindingsLedgerIntegrationTests
|
||||
{
|
||||
#region FINDINGS-5100-005: Event Stream → Ledger State → Replay → Verify Identical
|
||||
#region FINDINGS-5100-005: Event Stream → Ledger State → Replay → Verify Identical
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task EventStream_ToLedgerState_Replay_ProducesIdenticalState()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryLedgerEventRepository();
|
||||
var reducer = new LedgerProjectionReducer();
|
||||
var repository = new TestInMemoryLedgerEventRepository();
|
||||
var reducer = new TestLedgerProjectionReducer();
|
||||
|
||||
var tenantId = Guid.NewGuid().ToString("D");
|
||||
var findingId = Guid.NewGuid();
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
// Create a sequence of events
|
||||
var events = new List<LedgerEvent>
|
||||
var events = new List<TestLedgerEvent>
|
||||
{
|
||||
new LedgerEvent(
|
||||
new TestLedgerEvent(
|
||||
EventId: Guid.NewGuid(),
|
||||
TenantId: tenantId,
|
||||
FindingId: findingId,
|
||||
EventType: LedgerEventType.FindingCreated,
|
||||
EventType: TestLedgerEventType.FindingCreated,
|
||||
Timestamp: now,
|
||||
Sequence: 1,
|
||||
Payload: JsonSerializer.Serialize(new { cveId = "CVE-2024-1234", severity = "critical" }),
|
||||
Hash: ComputeEventHash(1, "FindingCreated", now)
|
||||
),
|
||||
new LedgerEvent(
|
||||
new TestLedgerEvent(
|
||||
EventId: Guid.NewGuid(),
|
||||
TenantId: tenantId,
|
||||
FindingId: findingId,
|
||||
EventType: LedgerEventType.StatusChanged,
|
||||
EventType: TestLedgerEventType.StatusChanged,
|
||||
Timestamp: now.AddMinutes(5),
|
||||
Sequence: 2,
|
||||
Payload: JsonSerializer.Serialize(new { previousStatus = "open", newStatus = "investigating" }),
|
||||
Hash: ComputeEventHash(2, "StatusChanged", now.AddMinutes(5))
|
||||
),
|
||||
new LedgerEvent(
|
||||
new TestLedgerEvent(
|
||||
EventId: Guid.NewGuid(),
|
||||
TenantId: tenantId,
|
||||
FindingId: findingId,
|
||||
EventType: LedgerEventType.VexApplied,
|
||||
EventType: TestLedgerEventType.VexApplied,
|
||||
Timestamp: now.AddMinutes(10),
|
||||
Sequence: 3,
|
||||
Payload: JsonSerializer.Serialize(new { vexStatus = "not_affected", justification = "vulnerable_code_not_present" }),
|
||||
@@ -98,9 +93,9 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
public async Task EventStream_WithSameEvents_ProducesSameStateHash()
|
||||
{
|
||||
// Arrange
|
||||
var repository1 = new InMemoryLedgerEventRepository();
|
||||
var repository2 = new InMemoryLedgerEventRepository();
|
||||
var reducer = new LedgerProjectionReducer();
|
||||
var repository1 = new TestInMemoryLedgerEventRepository();
|
||||
var repository2 = new TestInMemoryLedgerEventRepository();
|
||||
var reducer = new TestLedgerProjectionReducer();
|
||||
|
||||
var tenantId = Guid.NewGuid().ToString("D");
|
||||
var findingId = Guid.NewGuid();
|
||||
@@ -128,9 +123,9 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
public async Task EventStream_DifferentEvents_ProducesDifferentStateHash()
|
||||
{
|
||||
// Arrange
|
||||
var repository1 = new InMemoryLedgerEventRepository();
|
||||
var repository2 = new InMemoryLedgerEventRepository();
|
||||
var reducer = new LedgerProjectionReducer();
|
||||
var repository1 = new TestInMemoryLedgerEventRepository();
|
||||
var repository2 = new TestInMemoryLedgerEventRepository();
|
||||
var reducer = new TestLedgerProjectionReducer();
|
||||
|
||||
var tenantId = Guid.NewGuid().ToString("D");
|
||||
var findingId = Guid.NewGuid();
|
||||
@@ -159,8 +154,8 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
public async Task ReplayMultipleTimes_AlwaysProducesIdenticalState()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryLedgerEventRepository();
|
||||
var reducer = new LedgerProjectionReducer();
|
||||
var repository = new TestInMemoryLedgerEventRepository();
|
||||
var reducer = new TestLedgerProjectionReducer();
|
||||
|
||||
var tenantId = Guid.NewGuid().ToString("D");
|
||||
var findingId = Guid.NewGuid();
|
||||
@@ -171,7 +166,7 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
await repository.AppendAsync(evt, CancellationToken.None);
|
||||
|
||||
// Act - Replay 10 times
|
||||
var projections = new List<LedgerProjection>();
|
||||
var projections = new List<TestLedgerProjection>();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var projection = await ProjectLedgerStateAsync(repository, reducer, tenantId, findingId);
|
||||
@@ -188,21 +183,21 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
public async Task EventStream_AfterAppendingMore_StateUpdatesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryLedgerEventRepository();
|
||||
var reducer = new LedgerProjectionReducer();
|
||||
var repository = new TestInMemoryLedgerEventRepository();
|
||||
var reducer = new TestLedgerProjectionReducer();
|
||||
|
||||
var tenantId = Guid.NewGuid().ToString("D");
|
||||
var findingId = Guid.NewGuid();
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
// Initial events
|
||||
var initialEvents = new List<LedgerEvent>
|
||||
var initialEvents = new List<TestLedgerEvent>
|
||||
{
|
||||
new LedgerEvent(
|
||||
new TestLedgerEvent(
|
||||
EventId: Guid.NewGuid(),
|
||||
TenantId: tenantId,
|
||||
FindingId: findingId,
|
||||
EventType: LedgerEventType.FindingCreated,
|
||||
EventType: TestLedgerEventType.FindingCreated,
|
||||
Timestamp: now,
|
||||
Sequence: 1,
|
||||
Payload: "{}",
|
||||
@@ -217,11 +212,11 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
var initialProjection = await ProjectLedgerStateAsync(repository, reducer, tenantId, findingId);
|
||||
|
||||
// Append more events
|
||||
var additionalEvent = new LedgerEvent(
|
||||
var additionalEvent = new TestLedgerEvent(
|
||||
EventId: Guid.NewGuid(),
|
||||
TenantId: tenantId,
|
||||
FindingId: findingId,
|
||||
EventType: LedgerEventType.StatusChanged,
|
||||
EventType: TestLedgerEventType.StatusChanged,
|
||||
Timestamp: now.AddMinutes(5),
|
||||
Sequence: 2,
|
||||
Payload: JsonSerializer.Serialize(new { newStatus = "resolved" }),
|
||||
@@ -243,8 +238,8 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
public async Task ConcurrentReplays_ProduceIdenticalResults()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryLedgerEventRepository();
|
||||
var reducer = new LedgerProjectionReducer();
|
||||
var repository = new TestInMemoryLedgerEventRepository();
|
||||
var reducer = new TestLedgerProjectionReducer();
|
||||
|
||||
var tenantId = Guid.NewGuid().ToString("D");
|
||||
var findingId = Guid.NewGuid();
|
||||
@@ -275,8 +270,8 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
public async Task LedgerState_AtPointInTime_IsReproducible()
|
||||
{
|
||||
// Arrange
|
||||
var repository = new InMemoryLedgerEventRepository();
|
||||
var reducer = new LedgerProjectionReducer();
|
||||
var repository = new TestInMemoryLedgerEventRepository();
|
||||
var reducer = new TestLedgerProjectionReducer();
|
||||
|
||||
var tenantId = Guid.NewGuid().ToString("D");
|
||||
var findingId = Guid.NewGuid();
|
||||
@@ -300,9 +295,9 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
|
||||
#region Helpers
|
||||
|
||||
private static async Task<LedgerProjection> ProjectLedgerStateAsync(
|
||||
InMemoryLedgerEventRepository repository,
|
||||
LedgerProjectionReducer reducer,
|
||||
private static async Task<TestLedgerProjection> ProjectLedgerStateAsync(
|
||||
TestInMemoryLedgerEventRepository repository,
|
||||
TestLedgerProjectionReducer reducer,
|
||||
string tenantId,
|
||||
Guid findingId)
|
||||
{
|
||||
@@ -310,9 +305,9 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
return reducer.Project(events.ToList());
|
||||
}
|
||||
|
||||
private static async Task<LedgerProjection> ProjectLedgerStateAtTimeAsync(
|
||||
InMemoryLedgerEventRepository repository,
|
||||
LedgerProjectionReducer reducer,
|
||||
private static async Task<TestLedgerProjection> ProjectLedgerStateAtTimeAsync(
|
||||
TestInMemoryLedgerEventRepository repository,
|
||||
TestLedgerProjectionReducer reducer,
|
||||
string tenantId,
|
||||
Guid findingId,
|
||||
DateTimeOffset asOf)
|
||||
@@ -322,35 +317,35 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
return reducer.Project(filteredEvents);
|
||||
}
|
||||
|
||||
private static List<LedgerEvent> CreateStandardEventSequence(string tenantId, Guid findingId, DateTimeOffset baseTime)
|
||||
private static List<TestLedgerEvent> CreateStandardEventSequence(string tenantId, Guid findingId, DateTimeOffset baseTime)
|
||||
{
|
||||
return new List<LedgerEvent>
|
||||
return new List<TestLedgerEvent>
|
||||
{
|
||||
new LedgerEvent(
|
||||
new TestLedgerEvent(
|
||||
EventId: Guid.NewGuid(),
|
||||
TenantId: tenantId,
|
||||
FindingId: findingId,
|
||||
EventType: LedgerEventType.FindingCreated,
|
||||
EventType: TestLedgerEventType.FindingCreated,
|
||||
Timestamp: baseTime,
|
||||
Sequence: 1,
|
||||
Payload: JsonSerializer.Serialize(new { cveId = "CVE-2024-1234" }),
|
||||
Hash: ComputeEventHash(1, "FindingCreated", baseTime)
|
||||
),
|
||||
new LedgerEvent(
|
||||
new TestLedgerEvent(
|
||||
EventId: Guid.NewGuid(),
|
||||
TenantId: tenantId,
|
||||
FindingId: findingId,
|
||||
EventType: LedgerEventType.StatusChanged,
|
||||
EventType: TestLedgerEventType.StatusChanged,
|
||||
Timestamp: baseTime.AddMinutes(5),
|
||||
Sequence: 2,
|
||||
Payload: JsonSerializer.Serialize(new { newStatus = "investigating" }),
|
||||
Hash: ComputeEventHash(2, "StatusChanged", baseTime.AddMinutes(5))
|
||||
),
|
||||
new LedgerEvent(
|
||||
new TestLedgerEvent(
|
||||
EventId: Guid.NewGuid(),
|
||||
TenantId: tenantId,
|
||||
FindingId: findingId,
|
||||
EventType: LedgerEventType.VexApplied,
|
||||
EventType: TestLedgerEventType.VexApplied,
|
||||
Timestamp: baseTime.AddMinutes(10),
|
||||
Sequence: 3,
|
||||
Payload: JsonSerializer.Serialize(new { vexStatus = "not_affected" }),
|
||||
@@ -359,25 +354,25 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
};
|
||||
}
|
||||
|
||||
private static List<LedgerEvent> CreateAlternateEventSequence(string tenantId, Guid findingId, DateTimeOffset baseTime)
|
||||
private static List<TestLedgerEvent> CreateAlternateEventSequence(string tenantId, Guid findingId, DateTimeOffset baseTime)
|
||||
{
|
||||
return new List<LedgerEvent>
|
||||
return new List<TestLedgerEvent>
|
||||
{
|
||||
new LedgerEvent(
|
||||
new TestLedgerEvent(
|
||||
EventId: Guid.NewGuid(),
|
||||
TenantId: tenantId,
|
||||
FindingId: findingId,
|
||||
EventType: LedgerEventType.FindingCreated,
|
||||
EventType: TestLedgerEventType.FindingCreated,
|
||||
Timestamp: baseTime,
|
||||
Sequence: 1,
|
||||
Payload: JsonSerializer.Serialize(new { cveId = "CVE-2024-5678" }), // Different CVE
|
||||
Hash: ComputeEventHash(1, "FindingCreated", baseTime)
|
||||
),
|
||||
new LedgerEvent(
|
||||
new TestLedgerEvent(
|
||||
EventId: Guid.NewGuid(),
|
||||
TenantId: tenantId,
|
||||
FindingId: findingId,
|
||||
EventType: LedgerEventType.StatusChanged,
|
||||
EventType: TestLedgerEventType.StatusChanged,
|
||||
Timestamp: baseTime.AddMinutes(5),
|
||||
Sequence: 2,
|
||||
Payload: JsonSerializer.Serialize(new { newStatus = "resolved" }), // Different status
|
||||
@@ -396,17 +391,17 @@ public sealed class FindingsLedgerIntegrationTests
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Supporting Types (if not available in the project)
|
||||
#region Supporting Types (test-local implementations)
|
||||
|
||||
/// <summary>
|
||||
/// Simplified in-memory repository for testing.
|
||||
/// </summary>
|
||||
internal class InMemoryLedgerEventRepository
|
||||
internal class TestInMemoryLedgerEventRepository
|
||||
{
|
||||
private readonly List<LedgerEvent> _events = new();
|
||||
private readonly List<TestLedgerEvent> _events = new();
|
||||
private readonly object _lock = new();
|
||||
|
||||
public Task AppendAsync(LedgerEvent evt, CancellationToken ct)
|
||||
public Task AppendAsync(TestLedgerEvent evt, CancellationToken ct)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -415,7 +410,7 @@ internal class InMemoryLedgerEventRepository
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<IEnumerable<LedgerEvent>> GetEventsAsync(string tenantId, Guid findingId, CancellationToken ct)
|
||||
public Task<IEnumerable<TestLedgerEvent>> GetEventsAsync(string tenantId, Guid findingId, CancellationToken ct)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -423,7 +418,7 @@ internal class InMemoryLedgerEventRepository
|
||||
.Where(e => e.TenantId == tenantId && e.FindingId == findingId)
|
||||
.OrderBy(e => e.Sequence)
|
||||
.ToList();
|
||||
return Task.FromResult<IEnumerable<LedgerEvent>>(filtered);
|
||||
return Task.FromResult<IEnumerable<TestLedgerEvent>>(filtered);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -431,12 +426,12 @@ internal class InMemoryLedgerEventRepository
|
||||
/// <summary>
|
||||
/// Simplified projection reducer for testing.
|
||||
/// </summary>
|
||||
internal class LedgerProjectionReducer
|
||||
internal class TestLedgerProjectionReducer
|
||||
{
|
||||
public LedgerProjection Project(IList<LedgerEvent> events)
|
||||
public TestLedgerProjection Project(IList<TestLedgerEvent> events)
|
||||
{
|
||||
if (events.Count == 0)
|
||||
return new LedgerProjection(Guid.Empty, "unknown", "", 0, DateTimeOffset.MinValue);
|
||||
return new TestLedgerProjection(Guid.Empty, "unknown", "", 0, DateTimeOffset.MinValue);
|
||||
|
||||
var findingId = events[0].FindingId;
|
||||
var status = "open";
|
||||
@@ -444,7 +439,7 @@ internal class LedgerProjectionReducer
|
||||
|
||||
foreach (var evt in events)
|
||||
{
|
||||
if (evt.EventType == LedgerEventType.StatusChanged)
|
||||
if (evt.EventType == TestLedgerEventType.StatusChanged)
|
||||
{
|
||||
using var doc = JsonDocument.Parse(evt.Payload);
|
||||
if (doc.RootElement.TryGetProperty("newStatus", out var newStatus))
|
||||
@@ -459,13 +454,12 @@ internal class LedgerProjectionReducer
|
||||
// Compute cycle hash from all events
|
||||
var cycleHash = ComputeCycleHash(events);
|
||||
|
||||
return new LedgerProjection(findingId, status, cycleHash, events.Count, lastTimestamp);
|
||||
return new TestLedgerProjection(findingId, status, cycleHash, events.Count, lastTimestamp);
|
||||
}
|
||||
|
||||
private static string ComputeCycleHash(IList<LedgerEvent> events)
|
||||
private static string ComputeCycleHash(IList<TestLedgerEvent> events)
|
||||
{
|
||||
using var sha256 = SHA256.Create();
|
||||
using StellaOps.TestKit;
|
||||
var combined = new StringBuilder();
|
||||
|
||||
foreach (var evt in events.OrderBy(e => e.Sequence))
|
||||
@@ -481,7 +475,7 @@ using StellaOps.TestKit;
|
||||
/// <summary>
|
||||
/// Ledger event type enumeration.
|
||||
/// </summary>
|
||||
internal enum LedgerEventType
|
||||
internal enum TestLedgerEventType
|
||||
{
|
||||
FindingCreated,
|
||||
StatusChanged,
|
||||
@@ -493,11 +487,11 @@ internal enum LedgerEventType
|
||||
/// <summary>
|
||||
/// Ledger event record.
|
||||
/// </summary>
|
||||
internal record LedgerEvent(
|
||||
internal record TestLedgerEvent(
|
||||
Guid EventId,
|
||||
string TenantId,
|
||||
Guid FindingId,
|
||||
LedgerEventType EventType,
|
||||
TestLedgerEventType EventType,
|
||||
DateTimeOffset Timestamp,
|
||||
int Sequence,
|
||||
string Payload,
|
||||
@@ -507,7 +501,7 @@ internal record LedgerEvent(
|
||||
/// <summary>
|
||||
/// Ledger projection record.
|
||||
/// </summary>
|
||||
internal record LedgerProjection(
|
||||
internal record TestLedgerProjection(
|
||||
Guid FindingId,
|
||||
string Status,
|
||||
string CycleHash,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
// FindingsLedgerWebServiceContractTests.cs
|
||||
// Sprint: SPRINT_5100_0010_0001_evidencelocker_tests
|
||||
// Task: FINDINGS-5100-004
|
||||
@@ -272,7 +272,6 @@ public sealed class FindingsLedgerWebServiceContractTests : IDisposable
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
using var doc = JsonDocument.Parse(content);
|
||||
|
||||
using StellaOps.TestKit;
|
||||
// Navigate to FindingSummary schema
|
||||
if (doc.RootElement.TryGetProperty("components", out var components) &&
|
||||
components.TryGetProperty("schemas", out var schemas) &&
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Findings.Ledger.Options;
|
||||
using StellaOps.Findings.Ledger.Services.Incident;
|
||||
using StellaOps.Findings.Ledger.Tests.Observability;
|
||||
@@ -18,7 +12,7 @@ public class LedgerIncidentCoordinatorTests
|
||||
[Fact]
|
||||
public async Task Activation_updates_state_and_notifies()
|
||||
{
|
||||
var options = Options.Create(new LedgerIncidentOptions { RetentionExtensionDays = 45, LagTraceThresholdSeconds = 0.0 });
|
||||
var options = Microsoft.Extensions.Options.Options.Create(new LedgerIncidentOptions { RetentionExtensionDays = 45, LagTraceThresholdSeconds = 0.0 });
|
||||
var logger = new TestLogger<LedgerIncidentCoordinator>();
|
||||
var notifier = new TestNotifier();
|
||||
var incidentService = new StubIncidentModeService();
|
||||
@@ -36,7 +30,7 @@ public class LedgerIncidentCoordinatorTests
|
||||
[Fact]
|
||||
public async Task RecordProjectionLag_emits_when_active_and_above_threshold()
|
||||
{
|
||||
var options = Options.Create(new LedgerIncidentOptions { LagTraceThresholdSeconds = 0.1, RetentionExtensionDays = 5 });
|
||||
var options = Microsoft.Extensions.Options.Options.Create(new LedgerIncidentOptions { LagTraceThresholdSeconds = 0.1, RetentionExtensionDays = 5 });
|
||||
var logger = new TestLogger<LedgerIncidentCoordinator>();
|
||||
var notifier = new TestNotifier();
|
||||
var incidentService = new StubIncidentModeService();
|
||||
@@ -116,7 +110,7 @@ public class LedgerIncidentCoordinatorTests
|
||||
}
|
||||
|
||||
public Task<DateTimeOffset?> ExtendTtlAsync(TimeSpan extension, string actor, CancellationToken ct = default) =>
|
||||
Task.FromResult<DateTimeOffset?>(_state?.ExpiresAt?.Add(extension));
|
||||
Task.FromResult<DateTimeOffset?>(_state?.ExpiresAt.Add(extension));
|
||||
|
||||
public IReadOnlyDictionary<string, string> GetIncidentTags() => new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public sealed class LedgerReplayDeterminismTests
|
||||
var baseTime = DateTimeOffset.UtcNow;
|
||||
|
||||
var events = CreateFindingEventSequence(tenantId, findingId, chainId, baseTime);
|
||||
var reversedEvents = events.Reverse().ToList();
|
||||
var reversedEvents = events.AsEnumerable().Reverse().ToList();
|
||||
|
||||
// Act - Replay in forward and reverse order
|
||||
var projectionForward = ReplayEvents(events);
|
||||
@@ -212,7 +212,7 @@ public sealed class LedgerReplayDeterminismTests
|
||||
var canonicalJson = CreateCanonicalProjectionJson(projection!);
|
||||
|
||||
// Assert - Multiple calls should produce identical JSON
|
||||
var canonicalJson2 = CreateCanonicalProjectionJson(projection);
|
||||
var canonicalJson2 = CreateCanonicalProjectionJson(projection!);
|
||||
canonicalJson.Should().Be(canonicalJson2);
|
||||
}
|
||||
|
||||
@@ -417,9 +417,10 @@ public sealed class LedgerReplayDeterminismTests
|
||||
}
|
||||
};
|
||||
|
||||
var canonicalJson = LedgerCanonicalJsonSerializer.Serialize(eventBody);
|
||||
var eventHash = LedgerHashing.ComputeEventHash(canonicalJson, previousHash);
|
||||
var merkleLeaf = LedgerHashing.ComputeMerkleLeaf(eventBody);
|
||||
var hashResult = LedgerHashing.ComputeHashes(eventBody, sequence);
|
||||
var eventHash = hashResult.EventHash;
|
||||
var merkleLeaf = hashResult.MerkleLeafHash;
|
||||
var canonicalJson = hashResult.CanonicalJson;
|
||||
|
||||
return new LedgerEventRecord(
|
||||
TenantId: tenantId,
|
||||
|
||||
@@ -11,8 +11,8 @@ public class LedgerMetricsTests
|
||||
[Fact]
|
||||
public void RecordProjectionApply_emits_histogram_and_counter_with_tags()
|
||||
{
|
||||
var histogramValues = new List<Measurement<double>>();
|
||||
var counterValues = new List<Measurement<long>>();
|
||||
var histogramValues = new List<(double Value, KeyValuePair<string, object?>[] Tags)>();
|
||||
var counterValues = new List<(long Value, KeyValuePair<string, object?>[] Tags)>();
|
||||
|
||||
using var listener = new MeterListener
|
||||
{
|
||||
@@ -25,19 +25,19 @@ public class LedgerMetricsTests
|
||||
}
|
||||
};
|
||||
|
||||
listener.SetMeasurementEventCallback<double>((instrument, measurement, _) =>
|
||||
listener.SetMeasurementEventCallback<double>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
if (instrument.Name is "ledger_projection_apply_seconds" or "ledger_projection_lag_seconds")
|
||||
{
|
||||
histogramValues.Add(measurement);
|
||||
histogramValues.Add((measurement, tags.ToArray()));
|
||||
}
|
||||
});
|
||||
|
||||
listener.SetMeasurementEventCallback<long>((instrument, measurement, _) =>
|
||||
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
|
||||
{
|
||||
if (instrument.Name == "ledger_projection_events_total")
|
||||
{
|
||||
counterValues.Add(measurement);
|
||||
counterValues.Add((measurement, tags.ToArray()));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ internal sealed class TestLogger<T> : ILogger<T>
|
||||
_entries.Add(new LogEntry(logLevel, eventId, state));
|
||||
}
|
||||
|
||||
internal sealed record LogEntry(LogLevel Level, EventId EventId, object State);
|
||||
internal sealed record LogEntry(LogLevel Level, EventId EventId, object? State);
|
||||
|
||||
private sealed class NullScope : IDisposable
|
||||
{
|
||||
|
||||
@@ -23,25 +23,10 @@ public class LedgerEventWriteServiceIncidentTests
|
||||
.ReturnsAsync((LedgerEventRecord?)null);
|
||||
|
||||
var chainId = Guid.NewGuid();
|
||||
var chainHead = new LedgerEventRecord(
|
||||
"tenant-a",
|
||||
chainId,
|
||||
1,
|
||||
Guid.NewGuid(),
|
||||
LedgerEventConstants.EventFindingCreated,
|
||||
"v1",
|
||||
"finding-1",
|
||||
"artifact-1",
|
||||
null,
|
||||
"actor-1",
|
||||
"operator",
|
||||
DateTimeOffset.UtcNow,
|
||||
DateTimeOffset.UtcNow,
|
||||
new JsonObject(),
|
||||
"hash-prev",
|
||||
LedgerEventConstants.EmptyHash,
|
||||
"leaf-hash",
|
||||
"{}");
|
||||
var chainHead = new LedgerChainHead(
|
||||
SequenceNumber: 1,
|
||||
EventHash: "hash-prev",
|
||||
RecordedAt: DateTimeOffset.UtcNow);
|
||||
|
||||
repo.Setup(r => r.GetChainHeadAsync("tenant-a", chainId, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(chainHead);
|
||||
|
||||
@@ -5,16 +5,26 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<!-- Project-specific settings (from deleted Directory.Build.props) -->
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);**/tools/**/*</DefaultItemExcludes>
|
||||
<DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../StellaOps.Findings.Ledger/StellaOps.Findings.Ledger.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
<Compile Remove="**/tools/**/*.cs" />
|
||||
<None Remove="**/tools/**/*" />
|
||||
<None Include="**/tools/**/*" Pack="false" CopyToOutputDirectory="Never" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.5.4" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<ProjectReference Include="../StellaOps.Findings.Ledger/StellaOps.Findings.Ledger.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Findings.Ledger.WebService/StellaOps.Findings.Ledger.WebService.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
<ProjectReference Include="../../Telemetry/StellaOps.Telemetry.Core/StellaOps.Telemetry.Core/StellaOps.Telemetry.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,3 +1,4 @@
|
||||
#pragma warning disable CS0436 // Type conflicts with imported type - local Program class is intentionally used
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -1942,7 +1943,7 @@ static async Task<Results<FileStreamHttpResult, JsonHttpResult<ExportPage<T>>, P
|
||||
}
|
||||
httpContext.Response.Headers["X-Stella-Result-Count"] = page.Items.Count.ToString();
|
||||
|
||||
var acceptsNdjson = httpContext.Request.Headers.Accept.Any(h => h.Contains("application/x-ndjson", StringComparison.OrdinalIgnoreCase));
|
||||
var acceptsNdjson = httpContext.Request.Headers.Accept.Any(h => h?.Contains("application/x-ndjson", StringComparison.OrdinalIgnoreCase) == true);
|
||||
if (acceptsNdjson)
|
||||
{
|
||||
httpContext.Response.ContentType = "application/x-ndjson";
|
||||
@@ -1975,32 +1976,32 @@ static bool TryGetTenant(HttpContext httpContext, out ProblemHttpResult? problem
|
||||
return true;
|
||||
}
|
||||
|
||||
static int? ParseInt(string value)
|
||||
static int? ParseInt(string? value)
|
||||
{
|
||||
return int.TryParse(value, out var result) ? result : null;
|
||||
}
|
||||
|
||||
static long? ParseLong(string value)
|
||||
static long? ParseLong(string? value)
|
||||
{
|
||||
return long.TryParse(value, out var result) ? result : null;
|
||||
}
|
||||
|
||||
static DateTimeOffset? ParseDate(string value)
|
||||
static DateTimeOffset? ParseDate(string? value)
|
||||
{
|
||||
return DateTimeOffset.TryParse(value, out var result) ? result : null;
|
||||
}
|
||||
|
||||
static decimal? ParseDecimal(string value)
|
||||
static decimal? ParseDecimal(string? value)
|
||||
{
|
||||
return decimal.TryParse(value, out var result) ? result : null;
|
||||
}
|
||||
|
||||
static bool? ParseBool(string value)
|
||||
static bool? ParseBool(string? value)
|
||||
{
|
||||
return bool.TryParse(value, out var result) ? result : null;
|
||||
}
|
||||
|
||||
static Guid? ParseGuid(string value)
|
||||
static Guid? ParseGuid(string? value)
|
||||
{
|
||||
return Guid.TryParse(value, out var result) ? result : null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"profiles": {
|
||||
"StellaOps.Findings.Ledger.WebService": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:62523;http://localhost:62524"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,7 +297,7 @@ public sealed class FindingScoringService : IFindingScoringService
|
||||
CancellationToken ct)
|
||||
{
|
||||
var cacheKey = GetCacheKey(findingId);
|
||||
if (_cache.TryGetValue<EvidenceWeightedScoreResponse>(cacheKey, out var cached))
|
||||
if (_cache.TryGetValue<EvidenceWeightedScoreResponse>(cacheKey, out var cached) && cached is not null)
|
||||
{
|
||||
return Task.FromResult<EvidenceWeightedScoreResponse?>(cached with { FromCache = true });
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -21,7 +21,7 @@
|
||||
<ProjectReference Include="..\..\Telemetry\StellaOps.Telemetry.Core\StellaOps.Telemetry.Core\StellaOps.Telemetry.Core.csproj" />
|
||||
<ProjectReference Include="..\..\Scanner\__Libraries\StellaOps.Scanner.Reachability\StellaOps.Scanner.Reachability.csproj" />
|
||||
<ProjectReference Include="..\..\Scanner\__Libraries\StellaOps.Scanner.Analyzers.Native\StellaOps.Scanner.Analyzers.Native.csproj" />
|
||||
<ProjectReference Include="..\..\__Libraries\StellaOps.Router.AspNet\StellaOps.Router.AspNet.csproj" />
|
||||
<ProjectReference Include="..\..\Router/__Libraries/StellaOps.Router.AspNet\StellaOps.Router.AspNet.csproj" />
|
||||
<ProjectReference Include="..\..\Signals\StellaOps.Signals\StellaOps.Signals.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ public sealed class AttachmentEncryptionService : IAttachmentEncryptionService
|
||||
var nonce = RandomNumberGenerator.GetBytes(12);
|
||||
var ciphertext = new byte[plaintext.Length];
|
||||
var tag = new byte[16];
|
||||
using var aes = new AesGcm(masterKey);
|
||||
using var aes = new AesGcm(masterKey, 16); // Specify tag size for AES-256-GCM
|
||||
aes.Encrypt(nonce, plaintext, ciphertext, tag);
|
||||
|
||||
return new AttachmentEncryptionResult(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
@@ -18,12 +18,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.0" />
|
||||
<PackageReference Include="Npgsql" Version="7.0.7" />
|
||||
<PackageReference Include="Npgsql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
<ProjectReference Include="..\..\StellaOps.Findings.Ledger.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.CommandLine;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
@@ -13,6 +14,7 @@ using StellaOps.Findings.Ledger.Domain;
|
||||
using StellaOps.Findings.Ledger.Hashing;
|
||||
using StellaOps.Findings.Ledger.Infrastructure;
|
||||
using StellaOps.Findings.Ledger.Infrastructure.Merkle;
|
||||
using StellaOps.Findings.Ledger.Infrastructure.Policy;
|
||||
using StellaOps.Findings.Ledger.Infrastructure.Postgres;
|
||||
using StellaOps.Findings.Ledger.Infrastructure.Projection;
|
||||
using StellaOps.Findings.Ledger.Options;
|
||||
@@ -20,55 +22,66 @@ using StellaOps.Findings.Ledger.Observability;
|
||||
using StellaOps.Findings.Ledger.Services;
|
||||
|
||||
// Command-line options
|
||||
var fixturesOption = new Option<FileInfo[]>(
|
||||
name: "--fixture",
|
||||
description: "NDJSON fixtures containing canonical ledger envelopes (sequence-ordered)")
|
||||
var fixturesOption = new Option<FileInfo[]>("--fixture")
|
||||
{
|
||||
IsRequired = true
|
||||
};
|
||||
fixturesOption.AllowMultipleArgumentsPerToken = true;
|
||||
|
||||
var connectionOption = new Option<string>(
|
||||
name: "--connection",
|
||||
description: "PostgreSQL connection string for ledger DB")
|
||||
{
|
||||
IsRequired = true
|
||||
Description = "NDJSON fixtures containing canonical ledger envelopes (sequence-ordered)",
|
||||
Required = true,
|
||||
AllowMultipleArgumentsPerToken = true
|
||||
};
|
||||
|
||||
var tenantOption = new Option<string>(
|
||||
name: "--tenant",
|
||||
getDefaultValue: () => "tenant-a",
|
||||
description: "Tenant identifier for appended events");
|
||||
var connectionOption = new Option<string>("--connection")
|
||||
{
|
||||
Description = "PostgreSQL connection string for ledger DB",
|
||||
Required = true
|
||||
};
|
||||
|
||||
var maxParallelOption = new Option<int>(
|
||||
name: "--maxParallel",
|
||||
getDefaultValue: () => 4,
|
||||
description: "Maximum concurrent append operations");
|
||||
var tenantOption = new Option<string>("--tenant")
|
||||
{
|
||||
Description = "Tenant identifier for appended events",
|
||||
DefaultValueFactory = _ => "tenant-a"
|
||||
};
|
||||
|
||||
var reportOption = new Option<FileInfo?>(
|
||||
name: "--report",
|
||||
description: "Path to write harness report JSON (with DSSE placeholder)");
|
||||
var maxParallelOption = new Option<int>("--maxParallel")
|
||||
{
|
||||
Description = "Maximum concurrent append operations",
|
||||
DefaultValueFactory = _ => 4
|
||||
};
|
||||
|
||||
var metricsOption = new Option<FileInfo?>(
|
||||
name: "--metrics",
|
||||
description: "Optional path to write metrics snapshot JSON");
|
||||
var reportOption = new Option<FileInfo?>("--report")
|
||||
{
|
||||
Description = "Path to write harness report JSON (with DSSE placeholder)"
|
||||
};
|
||||
|
||||
var expectedChecksumOption = new Option<FileInfo?>(
|
||||
name: "--expected-checksum",
|
||||
description: "Optional JSON file containing expected eventStream/projection checksums");
|
||||
var metricsOption = new Option<FileInfo?>("--metrics")
|
||||
{
|
||||
Description = "Optional path to write metrics snapshot JSON"
|
||||
};
|
||||
|
||||
var expectedChecksumOption = new Option<FileInfo?>("--expected-checksum")
|
||||
{
|
||||
Description = "Optional JSON file containing expected eventStream/projection checksums"
|
||||
};
|
||||
|
||||
var root = new RootCommand("Findings Ledger Replay Harness (LEDGER-29-008)");
|
||||
root.AddOption(fixturesOption);
|
||||
root.AddOption(connectionOption);
|
||||
root.AddOption(tenantOption);
|
||||
root.AddOption(maxParallelOption);
|
||||
root.AddOption(reportOption);
|
||||
root.AddOption(metricsOption);
|
||||
root.AddOption(expectedChecksumOption);
|
||||
root.Add(fixturesOption);
|
||||
root.Add(connectionOption);
|
||||
root.Add(tenantOption);
|
||||
root.Add(maxParallelOption);
|
||||
root.Add(reportOption);
|
||||
root.Add(metricsOption);
|
||||
root.Add(expectedChecksumOption);
|
||||
|
||||
root.SetHandler(async (FileInfo[] fixtures, string connection, string tenant, int maxParallel, FileInfo? reportFile, FileInfo? metricsFile, FileInfo? expectedChecksumsFile) =>
|
||||
root.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
await using var host = BuildHost(connection);
|
||||
var fixtures = parseResult.GetValue(fixturesOption)!;
|
||||
var connection = parseResult.GetValue(connectionOption)!;
|
||||
var tenant = parseResult.GetValue(tenantOption)!;
|
||||
var maxParallel = parseResult.GetValue(maxParallelOption);
|
||||
var reportFile = parseResult.GetValue(reportOption);
|
||||
var metricsFile = parseResult.GetValue(metricsOption);
|
||||
var expectedChecksumsFile = parseResult.GetValue(expectedChecksumOption);
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
using var host = BuildHost(connection);
|
||||
using var scope = host.Services.CreateScope();
|
||||
|
||||
var writeService = scope.ServiceProvider.GetRequiredService<ILedgerEventWriteService>();
|
||||
@@ -77,7 +90,6 @@ root.SetHandler(async (FileInfo[] fixtures, string connection, string tenant, in
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILoggerFactory>().CreateLogger("Harness");
|
||||
var timeProvider = scope.ServiceProvider.GetRequiredService<TimeProvider>();
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
var projectionTask = projectionWorker.StartAsync(cts.Token);
|
||||
var anchorTask = anchorWorker.StartAsync(cts.Token);
|
||||
|
||||
@@ -124,7 +136,7 @@ root.SetHandler(async (FileInfo[] fixtures, string connection, string tenant, in
|
||||
fixtures.Select(f => f.FullName).ToArray(),
|
||||
eventsWritten,
|
||||
sw.Elapsed.TotalSeconds,
|
||||
status: verification.Success ? "pass" : "fail",
|
||||
Status: verification.Success ? "pass" : "fail",
|
||||
WriteLatencyP95Ms: writeLatencyP95Ms,
|
||||
ProjectionRebuildP95Ms: rebuildP95Ms,
|
||||
ProjectionLagSecondsMax: projectionLagSeconds,
|
||||
@@ -154,9 +166,9 @@ root.SetHandler(async (FileInfo[] fixtures, string connection, string tenant, in
|
||||
|
||||
cts.Cancel();
|
||||
await Task.WhenAll(projectionTask, anchorTask).WaitAsync(TimeSpan.FromSeconds(5));
|
||||
}, fixturesOption, connectionOption, tenantOption, maxParallelOption, reportOption, metricsOption);
|
||||
});
|
||||
|
||||
await root.InvokeAsync(args);
|
||||
await root.Parse(args).InvokeAsync();
|
||||
|
||||
static async Task WriteDssePlaceholderAsync(string reportPath, string json, string? policyHash, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -246,9 +258,9 @@ static async IAsyncEnumerable<LedgerEventDraft> ReadDraftsAsync(FileInfo file, s
|
||||
using var reader = new StreamReader(stream);
|
||||
var recordedAtBase = timeProvider.GetUtcNow();
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
string? line;
|
||||
while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) is not null)
|
||||
{
|
||||
var line = await reader.ReadLineAsync().ConfigureAwait(false);
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
@@ -281,7 +293,7 @@ static LedgerEventDraft ToDraft(JsonObject node, string defaultTenant, DateTimeO
|
||||
var findingId = required("finding_id");
|
||||
var artifactId = required("artifact_id");
|
||||
var sourceRunId = node.TryGetPropertyValue("source_run_id", out var sourceRunNode) && sourceRunNode is not null && !string.IsNullOrWhiteSpace(sourceRunNode.GetValue<string>())
|
||||
? Guid.Parse(sourceRunNode!.GetValue<string>())
|
||||
? (Guid?)Guid.Parse(sourceRunNode!.GetValue<string>())
|
||||
: null;
|
||||
var actorId = required("actor_id");
|
||||
var actorType = required("actor_type");
|
||||
@@ -331,7 +343,8 @@ static async Task<VerificationResult> VerifyLedgerAsync(IServiceProvider service
|
||||
await using (var countCommand = new Npgsql.NpgsqlCommand("select count(*) from ledger_events where tenant_id = @tenant", connection))
|
||||
{
|
||||
countCommand.Parameters.AddWithValue("tenant", tenant);
|
||||
var count = (long)await countCommand.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);
|
||||
var countResult = await countCommand.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false);
|
||||
var count = countResult is long l ? l : 0L;
|
||||
if (count < expectedEvents)
|
||||
{
|
||||
errors.Add($"event_count_mismatch:{count}/{expectedEvents}");
|
||||
@@ -464,6 +477,21 @@ static double Percentile(IEnumerable<double> values, double percentile)
|
||||
return data[lowerIndex] + (data[upperIndex] - data[lowerIndex]) * fraction;
|
||||
}
|
||||
|
||||
// Local function - must be before type declarations
|
||||
static ExpectedChecksums LoadExpectedChecksums(FileInfo? file)
|
||||
{
|
||||
if (file is null)
|
||||
{
|
||||
return ExpectedChecksums.Empty;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(File.ReadAllText(file.FullName));
|
||||
var root = doc.RootElement;
|
||||
var eventStream = root.TryGetProperty("eventStream", out var ev) ? ev.GetString() : null;
|
||||
var projection = root.TryGetProperty("projection", out var pr) ? pr.GetString() : null;
|
||||
return new ExpectedChecksums(eventStream, projection);
|
||||
}
|
||||
|
||||
internal sealed record HarnessReport(
|
||||
string Tenant,
|
||||
IReadOnlyList<string> Fixtures,
|
||||
@@ -508,20 +536,6 @@ internal sealed class MetricsBag
|
||||
};
|
||||
}
|
||||
|
||||
static ExpectedChecksums LoadExpectedChecksums(FileInfo? file)
|
||||
{
|
||||
if (file is null)
|
||||
{
|
||||
return ExpectedChecksums.Empty;
|
||||
}
|
||||
|
||||
using var doc = JsonDocument.Parse(File.ReadAllText(file.FullName));
|
||||
var root = doc.RootElement;
|
||||
var eventStream = root.TryGetProperty("eventStream", out var ev) ? ev.GetString() : null;
|
||||
var projection = root.TryGetProperty("projection", out var pr) ? pr.GetString() : null;
|
||||
return new ExpectedChecksums(eventStream, projection);
|
||||
}
|
||||
|
||||
// Harness lightweight no-op implementations for projection/merkle to keep replay fast
|
||||
internal sealed class NoOpPolicyEvaluationService : IPolicyEvaluationService
|
||||
{
|
||||
@@ -535,6 +549,7 @@ internal sealed class NoOpPolicyEvaluationService : IPolicyEvaluationService
|
||||
RiskSeverity: current?.RiskSeverity,
|
||||
RiskProfileVersion: current?.RiskProfileVersion,
|
||||
RiskExplanationId: current?.RiskExplanationId,
|
||||
RiskEventSequence: null,
|
||||
Labels: labels,
|
||||
ExplainRef: null,
|
||||
Rationale: new JsonArray()));
|
||||
@@ -556,6 +571,21 @@ internal sealed class NoOpProjectionRepository : IFindingProjectionRepository
|
||||
Task.FromResult(new ProjectionCheckpoint(DateTimeOffset.MinValue, Guid.Empty, DateTimeOffset.MinValue));
|
||||
|
||||
public Task UpsertAsync(FindingProjection projection, CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task<FindingStatsResult> GetFindingStatsSinceAsync(string tenantId, DateTimeOffset since, CancellationToken cancellationToken) =>
|
||||
Task.FromResult(new FindingStatsResult(0, 0, 0, 0, 0, 0));
|
||||
|
||||
public Task<(IReadOnlyList<FindingProjection> Projections, int TotalCount)> QueryScoredAsync(ScoredFindingsQuery query, CancellationToken cancellationToken) =>
|
||||
Task.FromResult<(IReadOnlyList<FindingProjection>, int)>((Array.Empty<FindingProjection>(), 0));
|
||||
|
||||
public Task<SeverityDistribution> GetSeverityDistributionAsync(string tenantId, string? policyVersion, CancellationToken cancellationToken) =>
|
||||
Task.FromResult(new SeverityDistribution());
|
||||
|
||||
public Task<ScoreDistribution> GetScoreDistributionAsync(string tenantId, string? policyVersion, CancellationToken cancellationToken) =>
|
||||
Task.FromResult(new ScoreDistribution());
|
||||
|
||||
public Task<(int Total, int Scored, decimal AvgScore, decimal MaxScore)> GetRiskAggregatesAsync(string tenantId, string? policyVersion, CancellationToken cancellationToken) =>
|
||||
Task.FromResult((0, 0, 0m, 0m));
|
||||
}
|
||||
|
||||
internal sealed class NoOpMerkleAnchorRepository : IMerkleAnchorRepository
|
||||
|
||||
705
src/Findings/StellaOps.Findings.sln
Normal file
705
src/Findings/StellaOps.Findings.sln
Normal file
@@ -0,0 +1,705 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Findings.Ledger", "StellaOps.Findings.Ledger", "{DBE4FCDA-B1A6-F61C-4DAB-6F814B25A158}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Findings.Ledger.Tests", "StellaOps.Findings.Ledger.Tests", "{A3DEA15D-11D3-CC57-BF26-7F162261228B}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Findings.Ledger.WebService", "StellaOps.Findings.Ledger.WebService", "{F9890C81-CDBE-C84D-D3D4-B7A862B78606}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{7CC40687-561F-4A18-09A2-19EB6C9A5EDD}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LedgerReplayHarness", "LedgerReplayHarness", "{DEF71302-8A95-66E5-5BCE-C61CCD2880C5}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "__External", "__External", "{5B52EF8A-3661-DCFF-797D-BC4D6AC60BDA}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AirGap", "AirGap", "{F310596E-88BB-9E54-885E-21C61971917E}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.AirGap.Policy", "StellaOps.AirGap.Policy", "{D9492ED1-A812-924B-65E4-F518592B49BB}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.AirGap.Policy", "StellaOps.AirGap.Policy", "{3823DE1E-2ACE-C956-99E1-00DB786D9E1D}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Attestor", "Attestor", "{5AC09D9A-F2A5-9CFA-B3C5-8D25F257651C}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Attestor", "StellaOps.Attestor", "{33B1AE27-692A-1778-48C1-CCEC2B9BC78F}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Attestor.Envelope", "StellaOps.Attestor.Envelope", "{018E0E11-1CCE-A2BE-641D-21EE14D2E90D}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Attestor.Core", "StellaOps.Attestor.Core", "{5F27FB4E-CF09-3A6B-F5B4-BF5A709FA609}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "__Libraries", "__Libraries", "{AB67BDB9-D701-3AC9-9CDF-ECCDCCD8DB6D}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Attestor.GraphRoot", "StellaOps.Attestor.GraphRoot", "{3F605548-87E2-8A1D-306D-0CE6960B8242}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Attestor.ProofChain", "StellaOps.Attestor.ProofChain", "{45F7FA87-7451-6970-7F6E-F8BAE45E081B}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Authority", "Authority", "{C1DCEFBD-12A5-EAAE-632E-8EEB9BE491B6}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Authority", "StellaOps.Authority", "{A6928CBA-4D4D-AB2B-CA19-FEE6E73ECE70}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Auth.Abstractions", "StellaOps.Auth.Abstractions", "{F2E6CB0E-DF77-1FAA-582B-62B040DF3848}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Auth.Client", "StellaOps.Auth.Client", "{C494ECBE-DEA5-3576-D2AF-200FF12BC144}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Auth.ServerIntegration", "StellaOps.Auth.ServerIntegration", "{7E890DF9-B715-B6DF-2498-FD74DDA87D71}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Authority.Plugins.Abstractions", "StellaOps.Authority.Plugins.Abstractions", "{64689413-46D7-8499-68A6-B6367ACBC597}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Concelier", "Concelier", "{157C3671-CA0B-69FA-A7C9-74A1FDA97B99}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "__Libraries", "__Libraries", "{F39E09D6-BF93-B64A-CFE7-2BA92815C0FE}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Concelier.SourceIntel", "StellaOps.Concelier.SourceIntel", "{F2B58F4E-6F28-A25F-5BFB-CDEBAD6B9A3E}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Feedser", "Feedser", "{C4A90603-BE42-0044-CAB4-3EB910AD51A5}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Feedser.BinaryAnalysis", "StellaOps.Feedser.BinaryAnalysis", "{054761F9-16D3-B2F8-6F4D-EFC2248805CD}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Feedser.Core", "StellaOps.Feedser.Core", "{B54CE64C-4167-1DD1-B7D6-2FD7A5AEF715}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Policy", "Policy", "{8E6B774C-CC4E-CE7C-AD4B-8AF7C92889A6}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Policy.RiskProfile", "StellaOps.Policy.RiskProfile", "{BC12ED55-6015-7C8B-8384-B39CE93C76D6}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "__Libraries", "__Libraries", "{FF70543D-AFF9-1D38-4950-4F8EE18D60BB}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Policy", "StellaOps.Policy", "{831265B0-8896-9C95-3488-E12FD9F6DC53}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Provenance", "Provenance", "{316BBD0A-04D2-85C9-52EA-7993CC6C8930}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Provenance.Attestation", "StellaOps.Provenance.Attestation", "{9D6AB85A-85EA-D85A-5566-A121D34016E6}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Router", "Router", "{FC018E5B-1E2F-DE19-1E97-0C845058C469}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "__Libraries", "__Libraries", "{1BE5B76C-B486-560B-6CB2-44C6537249AA}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Messaging", "StellaOps.Messaging", "{F4F1CBE2-1CDD-CAA4-41F0-266DB4677C05}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Microservice", "StellaOps.Microservice", "{3DE1DCDC-C845-4AC7-7B66-34B0A9E8626B}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Microservice.AspNetCore", "StellaOps.Microservice.AspNetCore", "{6FA01E92-606B-0CB8-8583-6F693A903CFC}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Router.AspNet", "StellaOps.Router.AspNet", "{A5994E92-7E0E-89FE-5628-DE1A0176B8BA}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Router.Common", "StellaOps.Router.Common", "{54C11B29-4C54-7255-AB44-BEB63AF9BD1F}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scanner", "Scanner", "{5896C4B3-31D1-1EDD-11D0-C46DB178DC12}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.Analyzers.Native", "StellaOps.Scanner.Analyzers.Native", "{B469ABBF-DC3D-4A71-7AA7-BD1839F4D793}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "__Libraries", "__Libraries", "{D4D193A8-47D7-0B1A-1327-F9C580E7AD07}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.Analyzers.Native", "StellaOps.Scanner.Analyzers.Native", "{612BA831-66B7-FC6C-9035-DB4368589E92}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.Cache", "StellaOps.Scanner.Cache", "{76EA64F4-C653-981E-CF8B-596DF7DC64AB}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.Core", "StellaOps.Scanner.Core", "{C9BCCEDF-7B8A-BCD8-A6B4-75EB25689FE8}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.Evidence", "StellaOps.Scanner.Evidence", "{C858A6E9-AEDF-1B98-0578-7761D09C2E97}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.Explainability", "StellaOps.Scanner.Explainability", "{18E8E925-7269-0AC8-8621-836C42E6F7F1}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.ProofSpine", "StellaOps.Scanner.ProofSpine", "{9F30DC58-7747-31D8-2403-D7D0F5454C87}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.Reachability", "StellaOps.Scanner.Reachability", "{47C8324C-B8C1-6E1A-C749-BCACF4BE3D71}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.SmartDiff", "StellaOps.Scanner.SmartDiff", "{269FC82B-1702-1933-65BC-D3F90CBB9643}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.Storage.Oci", "StellaOps.Scanner.Storage.Oci", "{0E8DA218-E337-6D7F-8B78-36900DF402AE}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Scanner.Surface.Env", "StellaOps.Scanner.Surface.Env", "{336213F7-1241-D268-8EA5-1C73F0040714}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Signals", "Signals", "{AD65DDE7-9FEA-7380-8C10-FA165F745354}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Signals", "StellaOps.Signals", "{076B8074-5735-5367-1EEA-CA16A5B8ABD7}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Signer", "Signer", "{3247EE0D-B3E9-9C11-B0AE-FE719410390B}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Signer", "StellaOps.Signer", "{CD7C09DA-FEC8-2CC5-D00C-E525638DFF4A}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Signer.Core", "StellaOps.Signer.Core", "{79B10804-91E9-972E-1913-EE0F0B11663E}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Telemetry", "Telemetry", "{E9A667F9-9627-4297-EF5E-0333593FDA14}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Telemetry.Core", "StellaOps.Telemetry.Core", "{B81E0B20-6C85-AC09-1DB6-5BD6CBB8AA62}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Telemetry.Core", "StellaOps.Telemetry.Core", "{74C64C1F-14F4-7B75-C354-9F252494A758}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "__Libraries", "__Libraries", "{1345DD29-BB3A-FB5F-4B3D-E29F6045A27A}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Auth.Security", "StellaOps.Auth.Security", "{9C2DD234-FA33-FDB6-86F0-EF9B75A13450}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Canonical.Json", "StellaOps.Canonical.Json", "{79E122F4-2325-3E92-438E-5825A307B594}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Configuration", "StellaOps.Configuration", "{538E2D98-5325-3F54-BE74-EFE5FC1ECBD8}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography", "StellaOps.Cryptography", "{66557252-B5C4-664B-D807-07018C627474}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.DependencyInjection", "StellaOps.Cryptography.DependencyInjection", "{7203223D-FF02-7BEB-2798-D1639ACC01C4}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.Kms", "StellaOps.Cryptography.Kms", "{5AC9EE40-1881-5F8A-46A2-2C303950D3C8}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.Plugin.CryptoPro", "StellaOps.Cryptography.Plugin.CryptoPro", "{3C69853C-90E3-D889-1960-3B9229882590}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.Plugin.OpenSslGost", "StellaOps.Cryptography.Plugin.OpenSslGost", "{643E4D4C-BC96-A37F-E0EC-488127F0B127}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.Plugin.Pkcs11Gost", "StellaOps.Cryptography.Plugin.Pkcs11Gost", "{6F2CA7F5-3E7C-C61B-94E6-E7DD1227B5B1}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.Plugin.PqSoft", "StellaOps.Cryptography.Plugin.PqSoft", "{F04B7DBB-77A5-C978-B2DE-8C189A32AA72}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.Plugin.SimRemote", "StellaOps.Cryptography.Plugin.SimRemote", "{7C72F22A-20FF-DF5B-9191-6DFD0D497DB2}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.Plugin.SmRemote", "StellaOps.Cryptography.Plugin.SmRemote", "{C896CC0A-F5E6-9AA4-C582-E691441F8D32}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.Plugin.SmSoft", "StellaOps.Cryptography.Plugin.SmSoft", "{0AA3A418-AB45-CCA4-46D4-EEBFE011FECA}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.Plugin.WineCsp", "StellaOps.Cryptography.Plugin.WineCsp", "{225D9926-4AE8-E539-70AD-8698E688F271}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Cryptography.PluginLoader", "StellaOps.Cryptography.PluginLoader", "{D6E8E69C-F721-BBCB-8C39-9716D53D72AD}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.DependencyInjection", "StellaOps.DependencyInjection", "{589A43FD-8213-E9E3-6CFF-9CBA72D53E98}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Evidence.Bundle", "StellaOps.Evidence.Bundle", "{2BACF7E3-1278-FE99-8343-8221E6FBA9DE}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Evidence.Core", "StellaOps.Evidence.Core", "{75E47125-E4D7-8482-F1A4-726564970864}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Plugin", "StellaOps.Plugin", "{772B02B5-6280-E1D4-3E2E-248D0455C2FB}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Replay.Core", "StellaOps.Replay.Core", "{083067CF-CE89-EF39-9BD3-4741919E26F3}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.TestKit", "StellaOps.TestKit", "{8380A20C-A5B8-EE91-1A58-270323688CB9}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "__Tests", "__Tests", "{BB76B5A5-14BA-E317-828D-110B711D71F5}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StellaOps.Findings.Ledger.Tests", "StellaOps.Findings.Ledger.Tests", "{2E456655-42A9-55BB-D240-2DBD7B1B4E0E}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{F9D35D43-770D-3909-2A66-3E665E82AE1D}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LedgerReplayHarness", "LedgerReplayHarness", "{227B8E37-08C4-699B-7432-95ECA602F68C}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LedgerReplayHarness", "StellaOps.Findings.Ledger\tools\LedgerReplayHarness\LedgerReplayHarness.csproj", "{F5FB90E2-4621-B51E-84C4-61BD345FD31C}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LedgerReplayHarness", "tools\LedgerReplayHarness\LedgerReplayHarness.csproj", "{D18D1912-6E44-8578-C851-983BA0F6CD9F}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.AirGap.Policy", "E:\dev\git.stella-ops.org\src\AirGap\StellaOps.AirGap.Policy\StellaOps.AirGap.Policy\StellaOps.AirGap.Policy.csproj", "{AD31623A-BC43-52C2-D906-AC1D8784A541}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Attestor.Core", "E:\dev\git.stella-ops.org\src\Attestor\StellaOps.Attestor\StellaOps.Attestor.Core\StellaOps.Attestor.Core.csproj", "{5B4DF41E-C8CC-2606-FA2D-967118BD3C59}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Attestor.Envelope", "E:\dev\git.stella-ops.org\src\Attestor\StellaOps.Attestor.Envelope\StellaOps.Attestor.Envelope.csproj", "{3D8C5A6C-462D-7487-5BD0-A3EF6B657EB6}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Attestor.GraphRoot", "E:\dev\git.stella-ops.org\src\Attestor\__Libraries\StellaOps.Attestor.GraphRoot\StellaOps.Attestor.GraphRoot.csproj", "{2609BC1A-6765-29BE-78CC-C0F1D2814F10}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Attestor.ProofChain", "E:\dev\git.stella-ops.org\src\Attestor\__Libraries\StellaOps.Attestor.ProofChain\StellaOps.Attestor.ProofChain.csproj", "{C6822231-A4F4-9E69-6CE2-4FDB3E81C728}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Abstractions", "E:\dev\git.stella-ops.org\src\Authority\StellaOps.Authority\StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj", "{55D9B653-FB76-FCE8-1A3C-67B1BEDEC214}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Client", "E:\dev\git.stella-ops.org\src\Authority\StellaOps.Authority\StellaOps.Auth.Client\StellaOps.Auth.Client.csproj", "{DE5BF139-1E5C-D6EA-4FAA-661EF353A194}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Security", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Auth.Security\StellaOps.Auth.Security.csproj", "{335E62C0-9E69-A952-680B-753B1B17C6D0}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.ServerIntegration", "E:\dev\git.stella-ops.org\src\Authority\StellaOps.Authority\StellaOps.Auth.ServerIntegration\StellaOps.Auth.ServerIntegration.csproj", "{ECA25786-A3A8-92C4-4AA3-D4A73C69FDCA}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugins.Abstractions", "E:\dev\git.stella-ops.org\src\Authority\StellaOps.Authority\StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj", "{97F94029-5419-6187-5A63-5C8FD9232FAE}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Canonical.Json", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Canonical.Json\StellaOps.Canonical.Json.csproj", "{AF9E7F02-25AD-3540-18D7-F6A4F8BA5A60}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.SourceIntel", "E:\dev\git.stella-ops.org\src\Concelier\__Libraries\StellaOps.Concelier.SourceIntel\StellaOps.Concelier.SourceIntel.csproj", "{EB093C48-CDAC-106B-1196-AE34809B34C0}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Configuration", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Configuration\StellaOps.Configuration.csproj", "{92C62F7B-8028-6EE1-B71B-F45F459B8E97}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography\StellaOps.Cryptography.csproj", "{F664A948-E352-5808-E780-77A03F19E93E}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.DependencyInjection", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.DependencyInjection\StellaOps.Cryptography.DependencyInjection.csproj", "{FA83F778-5252-0B80-5555-E69F790322EA}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Kms", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.Kms\StellaOps.Cryptography.Kms.csproj", "{F3A27846-6DE0-3448-222C-25A273E86B2E}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Plugin.CryptoPro", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.Plugin.CryptoPro\StellaOps.Cryptography.Plugin.CryptoPro.csproj", "{C53E0895-879A-D9E6-0A43-24AD17A2F270}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Plugin.OpenSslGost", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.Plugin.OpenSslGost\StellaOps.Cryptography.Plugin.OpenSslGost.csproj", "{0AED303F-69E6-238F-EF80-81985080EDB7}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Plugin.Pkcs11Gost", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.Plugin.Pkcs11Gost\StellaOps.Cryptography.Plugin.Pkcs11Gost.csproj", "{2904D288-CE64-A565-2C46-C2E85A96A1EE}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Plugin.PqSoft", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.Plugin.PqSoft\StellaOps.Cryptography.Plugin.PqSoft.csproj", "{A6667CC3-B77F-023E-3A67-05F99E9FF46A}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Plugin.SimRemote", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.Plugin.SimRemote\StellaOps.Cryptography.Plugin.SimRemote.csproj", "{A26E2816-F787-F76B-1D6C-E086DD3E19CE}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Plugin.SmRemote", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.Plugin.SmRemote\StellaOps.Cryptography.Plugin.SmRemote.csproj", "{B3DEC619-67AC-1B5A-4F3E-A1F24C3F6877}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Plugin.SmSoft", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.Plugin.SmSoft\StellaOps.Cryptography.Plugin.SmSoft.csproj", "{90DB65B4-8F6E-FB8E-0281-505AD8BC6BA6}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.Plugin.WineCsp", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.Plugin.WineCsp\StellaOps.Cryptography.Plugin.WineCsp.csproj", "{059FBB86-DEE6-8207-3F23-2A1A3EC00DEA}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography.PluginLoader", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Cryptography.PluginLoader\StellaOps.Cryptography.PluginLoader.csproj", "{8BBA3159-C4CC-F685-A28C-7FE6CBD3D2A1}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.DependencyInjection", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.DependencyInjection\StellaOps.DependencyInjection.csproj", "{632A1F0D-1BA5-C84B-B716-2BE638A92780}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Evidence.Bundle", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Evidence.Bundle\StellaOps.Evidence.Bundle.csproj", "{9DE7852B-7E2D-257E-B0F1-45D2687854ED}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Evidence.Core", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Evidence.Core\StellaOps.Evidence.Core.csproj", "{DC2AFC89-C3C8-4E9B-13A7-027EB6386EFA}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Feedser.BinaryAnalysis", "E:\dev\git.stella-ops.org\src\Feedser\StellaOps.Feedser.BinaryAnalysis\StellaOps.Feedser.BinaryAnalysis.csproj", "{CB296A20-2732-77C1-7F23-27D5BAEDD0C7}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Feedser.Core", "E:\dev\git.stella-ops.org\src\Feedser\StellaOps.Feedser.Core\StellaOps.Feedser.Core.csproj", "{0DBEC9BA-FE1D-3898-B2C6-E4357DC23E0F}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Findings.Ledger", "StellaOps.Findings.Ledger\StellaOps.Findings.Ledger.csproj", "{356E10E9-4223-A6BC-BE0C-0DC376DDC391}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Findings.Ledger.Tests", "__Tests\StellaOps.Findings.Ledger.Tests\StellaOps.Findings.Ledger.Tests.csproj", "{09D88001-1724-612D-3B2D-1F3AC6F49690}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Findings.Ledger.Tests", "StellaOps.Findings.Ledger.Tests\StellaOps.Findings.Ledger.Tests.csproj", "{0066F933-EBB7-CF9D-0A28-B35BBDC24CC6}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Findings.Ledger.WebService", "StellaOps.Findings.Ledger.WebService\StellaOps.Findings.Ledger.WebService.csproj", "{BC1D62FA-C2B1-96BD-3EFF-F944CDA26ED3}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Messaging", "E:\dev\git.stella-ops.org\src\Router\__Libraries\StellaOps.Messaging\StellaOps.Messaging.csproj", "{97998C88-E6E1-D5E2-B632-537B58E00CBF}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Microservice", "E:\dev\git.stella-ops.org\src\Router\__Libraries\StellaOps.Microservice\StellaOps.Microservice.csproj", "{BAD08D96-A80A-D27F-5D9C-656AEEB3D568}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Microservice.AspNetCore", "E:\dev\git.stella-ops.org\src\Router\__Libraries\StellaOps.Microservice.AspNetCore\StellaOps.Microservice.AspNetCore.csproj", "{F63694F1-B56D-6E72-3F5D-5D38B1541F0F}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Plugin", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Plugin\StellaOps.Plugin.csproj", "{38A9EE9B-6FC8-93BC-0D43-2A906E678D66}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Policy", "E:\dev\git.stella-ops.org\src\Policy\__Libraries\StellaOps.Policy\StellaOps.Policy.csproj", "{19868E2D-7163-2108-1094-F13887C4F070}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Policy.RiskProfile", "E:\dev\git.stella-ops.org\src\Policy\StellaOps.Policy.RiskProfile\StellaOps.Policy.RiskProfile.csproj", "{CC319FC5-F4B1-C3DD-7310-4DAD343E0125}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Provenance.Attestation", "E:\dev\git.stella-ops.org\src\Provenance\StellaOps.Provenance.Attestation\StellaOps.Provenance.Attestation.csproj", "{A78EBC0F-C62C-8F56-95C0-330E376242A2}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Replay.Core", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.Replay.Core\StellaOps.Replay.Core.csproj", "{6D26FB21-7E48-024B-E5D4-E3F0F31976BB}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Router.AspNet", "E:\dev\git.stella-ops.org\src\Router\__Libraries\StellaOps.Router.AspNet\StellaOps.Router.AspNet.csproj", "{79104479-B087-E5D0-5523-F1803282A246}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Router.Common", "E:\dev\git.stella-ops.org\src\Router\__Libraries\StellaOps.Router.Common\StellaOps.Router.Common.csproj", "{F17A6F0B-3120-2BA9-84D8-5F8BA0B9705D}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.Analyzers.Native", "E:\dev\git.stella-ops.org\src\Scanner\__Libraries\StellaOps.Scanner.Analyzers.Native\StellaOps.Scanner.Analyzers.Native.csproj", "{F22333B6-7E27-679B-8475-B4B9AB1CB186}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.Analyzers.Native", "E:\dev\git.stella-ops.org\src\Scanner\StellaOps.Scanner.Analyzers.Native\StellaOps.Scanner.Analyzers.Native.csproj", "{CE042F3A-6851-FAAB-9E9C-AD905B4AAC8D}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.Cache", "E:\dev\git.stella-ops.org\src\Scanner\__Libraries\StellaOps.Scanner.Cache\StellaOps.Scanner.Cache.csproj", "{BA492274-A505-BCD5-3DA5-EE0C94DD5748}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.Core", "E:\dev\git.stella-ops.org\src\Scanner\__Libraries\StellaOps.Scanner.Core\StellaOps.Scanner.Core.csproj", "{58D8630F-C0F4-B772-8572-BCC98FF0F0D8}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.Evidence", "E:\dev\git.stella-ops.org\src\Scanner\__Libraries\StellaOps.Scanner.Evidence\StellaOps.Scanner.Evidence.csproj", "{37F1D83D-073C-C165-4C53-664AD87628E6}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.Explainability", "E:\dev\git.stella-ops.org\src\Scanner\__Libraries\StellaOps.Scanner.Explainability\StellaOps.Scanner.Explainability.csproj", "{ACC2785F-F4B9-13E4-EED2-C5D067242175}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.ProofSpine", "E:\dev\git.stella-ops.org\src\Scanner\__Libraries\StellaOps.Scanner.ProofSpine\StellaOps.Scanner.ProofSpine.csproj", "{7CB7FEA8-8A12-A5D6-0057-AA65DB328617}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.Reachability", "E:\dev\git.stella-ops.org\src\Scanner\__Libraries\StellaOps.Scanner.Reachability\StellaOps.Scanner.Reachability.csproj", "{35A06F00-71AB-8A31-7D60-EBF41EA730CA}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.SmartDiff", "E:\dev\git.stella-ops.org\src\Scanner\__Libraries\StellaOps.Scanner.SmartDiff\StellaOps.Scanner.SmartDiff.csproj", "{7F0FFA06-EAC8-CC9A-3386-389638F12B59}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.Storage.Oci", "E:\dev\git.stella-ops.org\src\Scanner\__Libraries\StellaOps.Scanner.Storage.Oci\StellaOps.Scanner.Storage.Oci.csproj", "{A80D212B-7E80-4251-16C0-60FA3670A5B4}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Scanner.Surface.Env", "E:\dev\git.stella-ops.org\src\Scanner\__Libraries\StellaOps.Scanner.Surface.Env\StellaOps.Scanner.Surface.Env.csproj", "{52698305-D6F8-C13C-0882-48FC37726404}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Signals", "E:\dev\git.stella-ops.org\src\Signals\StellaOps.Signals\StellaOps.Signals.csproj", "{A79CBC0C-5313-4ECF-A24E-27CE236BCF2C}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Signer.Core", "E:\dev\git.stella-ops.org\src\Signer\StellaOps.Signer\StellaOps.Signer.Core\StellaOps.Signer.Core.csproj", "{0AF13355-173C-3128-5AFC-D32E540DA3EF}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Telemetry.Core", "E:\dev\git.stella-ops.org\src\Telemetry\StellaOps.Telemetry.Core\StellaOps.Telemetry.Core\StellaOps.Telemetry.Core.csproj", "{8CD19568-1638-B8F6-8447-82CFD4F17ADF}"
|
||||
|
||||
EndProject
|
||||
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.TestKit", "E:\dev\git.stella-ops.org\src\__Libraries\StellaOps.TestKit\StellaOps.TestKit.csproj", "{AF043113-CCE3-59C1-DF71-9804155F26A8}"
|
||||
|
||||
EndProject
|
||||
|
||||
Global
|
||||
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
||||
Release|Any CPU = Release|Any CPU
|
||||
|
||||
EndGlobalSection
|
||||
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
|
||||
{F5FB90E2-4621-B51E-84C4-61BD345FD31C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{F5FB90E2-4621-B51E-84C4-61BD345FD31C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{F5FB90E2-4621-B51E-84C4-61BD345FD31C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{F5FB90E2-4621-B51E-84C4-61BD345FD31C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{D18D1912-6E44-8578-C851-983BA0F6CD9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{D18D1912-6E44-8578-C851-983BA0F6CD9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{D18D1912-6E44-8578-C851-983BA0F6CD9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{D18D1912-6E44-8578-C851-983BA0F6CD9F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{AD31623A-BC43-52C2-D906-AC1D8784A541}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{AD31623A-BC43-52C2-D906-AC1D8784A541}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{AD31623A-BC43-52C2-D906-AC1D8784A541}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{AD31623A-BC43-52C2-D906-AC1D8784A541}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{5B4DF41E-C8CC-2606-FA2D-967118BD3C59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{5B4DF41E-C8CC-2606-FA2D-967118BD3C59}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{5B4DF41E-C8CC-2606-FA2D-967118BD3C59}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{5B4DF41E-C8CC-2606-FA2D-967118BD3C59}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{3D8C5A6C-462D-7487-5BD0-A3EF6B657EB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{3D8C5A6C-462D-7487-5BD0-A3EF6B657EB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{3D8C5A6C-462D-7487-5BD0-A3EF6B657EB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{3D8C5A6C-462D-7487-5BD0-A3EF6B657EB6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{2609BC1A-6765-29BE-78CC-C0F1D2814F10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{2609BC1A-6765-29BE-78CC-C0F1D2814F10}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{2609BC1A-6765-29BE-78CC-C0F1D2814F10}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{2609BC1A-6765-29BE-78CC-C0F1D2814F10}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{C6822231-A4F4-9E69-6CE2-4FDB3E81C728}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{C6822231-A4F4-9E69-6CE2-4FDB3E81C728}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{C6822231-A4F4-9E69-6CE2-4FDB3E81C728}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{C6822231-A4F4-9E69-6CE2-4FDB3E81C728}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{55D9B653-FB76-FCE8-1A3C-67B1BEDEC214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{55D9B653-FB76-FCE8-1A3C-67B1BEDEC214}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{55D9B653-FB76-FCE8-1A3C-67B1BEDEC214}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{55D9B653-FB76-FCE8-1A3C-67B1BEDEC214}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{DE5BF139-1E5C-D6EA-4FAA-661EF353A194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{DE5BF139-1E5C-D6EA-4FAA-661EF353A194}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{DE5BF139-1E5C-D6EA-4FAA-661EF353A194}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{DE5BF139-1E5C-D6EA-4FAA-661EF353A194}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{335E62C0-9E69-A952-680B-753B1B17C6D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{335E62C0-9E69-A952-680B-753B1B17C6D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{335E62C0-9E69-A952-680B-753B1B17C6D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{335E62C0-9E69-A952-680B-753B1B17C6D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{ECA25786-A3A8-92C4-4AA3-D4A73C69FDCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{ECA25786-A3A8-92C4-4AA3-D4A73C69FDCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
{ECA25786-A3A8-92C4-4AA3-D4A73C69FDCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
{ECA25786-A3A8-92C4-4AA3-D4A73C69FDCA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
|
||||
{97F94029-5419-6187-5A63-5C8FD9232FAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
||||
{97F94029-5419-6187-5A63-5C8FD9232FAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using LedgerReplayHarness;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
@@ -23,7 +23,6 @@ public class HarnessRunnerTests
|
||||
|
||||
var json = await File.ReadAllTextAsync(tempReport);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
using StellaOps.TestKit;
|
||||
doc.RootElement.GetProperty("eventsWritten").GetInt64().Should().BeGreaterThan(0);
|
||||
doc.RootElement.GetProperty("status").GetString().Should().Be("pass");
|
||||
doc.RootElement.GetProperty("tenant").GetString().Should().Be("tenant-test");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Findings.Ledger.Observability;
|
||||
@@ -195,7 +195,6 @@ public class LedgerMetricsTests
|
||||
public void VersionInfoGauge_EmitsConstantOne()
|
||||
{
|
||||
using var listener = CreateListener();
|
||||
using StellaOps.TestKit;
|
||||
var measurements = new List<(long Value, KeyValuePair<string, object?>[] Tags)>();
|
||||
|
||||
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Net;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
@@ -90,7 +90,6 @@ public sealed class PolicyEngineEvaluationServiceTests
|
||||
var factory = new TestHttpClientFactory(handler);
|
||||
var options = CreateOptions(baseAddress: null);
|
||||
using var cache = new PolicyEvaluationCache(options.PolicyEngine, NullLogger<PolicyEvaluationCache>.Instance);
|
||||
using StellaOps.TestKit;
|
||||
var inline = new InlinePolicyEvaluationService(NullLogger<InlinePolicyEvaluationService>.Instance);
|
||||
var service = new PolicyEngineEvaluationService(factory, inline, cache, Microsoft.Extensions.Options.Options.Create(options), NullLogger<PolicyEngineEvaluationService>.Instance);
|
||||
|
||||
|
||||
@@ -89,10 +89,12 @@ public sealed class OpenApiSchemaTests
|
||||
// Arrange
|
||||
var request = new DecisionRequest
|
||||
{
|
||||
Decision = "accept_risk",
|
||||
Rationale = "Test rationale",
|
||||
JustificationCode = null,
|
||||
Metadata = null
|
||||
DecisionStatus = "accept_risk",
|
||||
ReasonCode = "vulnerable_code_not_in_execute_path",
|
||||
ReasonText = "Test reason text",
|
||||
EvidenceHashes = null,
|
||||
PolicyContext = null,
|
||||
RulesVersion = null
|
||||
};
|
||||
|
||||
// Act
|
||||
@@ -100,12 +102,12 @@ public sealed class OpenApiSchemaTests
|
||||
var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
// Assert - decision and rationale are required per OpenAPI spec
|
||||
root.TryGetProperty("decision", out var decision).Should().BeTrue();
|
||||
decision.GetString().Should().NotBeNullOrEmpty();
|
||||
// Assert - decision_status and reason_code are required per OpenAPI spec
|
||||
root.TryGetProperty("decision_status", out var decisionStatus).Should().BeTrue();
|
||||
decisionStatus.GetString().Should().NotBeNullOrEmpty();
|
||||
|
||||
root.TryGetProperty("rationale", out var rationale).Should().BeTrue();
|
||||
rationale.GetString().Should().NotBeNullOrEmpty();
|
||||
root.TryGetProperty("reason_code", out var reasonCode).Should().BeTrue();
|
||||
reasonCode.GetString().Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "BundleVerificationResponse includes all fields")]
|
||||
@@ -168,8 +170,8 @@ public sealed class OpenApiSchemaTests
|
||||
{
|
||||
var request = new DecisionRequest
|
||||
{
|
||||
Decision = decision,
|
||||
Rationale = "Test rationale"
|
||||
DecisionStatus = decision,
|
||||
ReasonCode = "vulnerable_code_not_in_execute_path"
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request, JsonOptions);
|
||||
|
||||
@@ -12,14 +12,7 @@
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" Version="8.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Moq" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using StellaOps.Findings.Ledger.Domain;
|
||||
@@ -32,7 +34,6 @@ public sealed class HarnessRunner
|
||||
var hashesValid = true;
|
||||
DateTimeOffset? earliest = null;
|
||||
DateTimeOffset? latest = null;
|
||||
var latencies = new List<double>();
|
||||
var leafHashes = new List<string>();
|
||||
string? expectedMerkleRoot = null;
|
||||
var latencies = new ConcurrentBag<double>();
|
||||
@@ -139,8 +140,7 @@ public sealed class HarnessRunner
|
||||
{
|
||||
await using var stream = File.OpenRead(path);
|
||||
using var reader = new StreamReader(stream);
|
||||
string? line;
|
||||
while (!reader.EndOfStream && !cancellationToken.IsCancellationRequested && (line = await reader.ReadLineAsync()) is not null)
|
||||
while (!cancellationToken.IsCancellationRequested && await reader.ReadLineAsync() is { } line)
|
||||
{
|
||||
yield return line;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<ProjectReference Include="..\\..\\StellaOps.Findings.Ledger\\StellaOps.Findings.Ledger.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,22 +1,47 @@
|
||||
using System.CommandLine;
|
||||
using LedgerReplayHarness;
|
||||
|
||||
var fixtureOption = new Option<string[]>("--fixture", "NDJSON fixture path(s)") { IsRequired = true, AllowMultipleArgumentsPerToken = true };
|
||||
var tenantOption = new Option<string>("--tenant", () => "default", "Tenant identifier");
|
||||
var reportOption = new Option<string>("--report", () => "harness-report.json", "Path to write JSON report");
|
||||
var parallelOption = new Option<int>("--maxParallel", () => 4, "Maximum parallelism when sending events");
|
||||
var fixtureOption = new Option<string[]>("--fixture")
|
||||
{
|
||||
Description = "NDJSON fixture path(s)",
|
||||
Required = true,
|
||||
AllowMultipleArgumentsPerToken = true
|
||||
};
|
||||
|
||||
var tenantOption = new Option<string>("--tenant")
|
||||
{
|
||||
Description = "Tenant identifier",
|
||||
DefaultValueFactory = _ => "default"
|
||||
};
|
||||
|
||||
var reportOption = new Option<string>("--report")
|
||||
{
|
||||
Description = "Path to write JSON report",
|
||||
DefaultValueFactory = _ => "harness-report.json"
|
||||
};
|
||||
|
||||
var parallelOption = new Option<int>("--maxParallel")
|
||||
{
|
||||
Description = "Maximum parallelism when sending events",
|
||||
DefaultValueFactory = _ => 4
|
||||
};
|
||||
|
||||
var root = new RootCommand("Findings Ledger replay & determinism harness");
|
||||
root.AddOption(fixtureOption);
|
||||
root.AddOption(tenantOption);
|
||||
root.AddOption(reportOption);
|
||||
root.AddOption(parallelOption);
|
||||
root.Add(fixtureOption);
|
||||
root.Add(tenantOption);
|
||||
root.Add(reportOption);
|
||||
root.Add(parallelOption);
|
||||
|
||||
root.SetHandler(async (fixtures, tenant, report, maxParallel) =>
|
||||
root.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var runner = new HarnessRunner(new InMemoryLedgerClient(), maxParallel);
|
||||
var exitCode = await runner.RunAsync(fixtures, tenant, report, CancellationToken.None);
|
||||
Environment.Exit(exitCode);
|
||||
}, fixtureOption, tenantOption, reportOption, parallelOption);
|
||||
var fixtures = parseResult.GetValue(fixtureOption)!;
|
||||
var tenant = parseResult.GetValue(tenantOption)!;
|
||||
var report = parseResult.GetValue(reportOption)!;
|
||||
var maxParallel = parseResult.GetValue(parallelOption);
|
||||
|
||||
return await root.InvokeAsync(args);
|
||||
var runner = new HarnessRunner(new InMemoryLedgerClient(), maxParallel);
|
||||
var exitCode = await runner.RunAsync(fixtures, tenant, report, ct);
|
||||
return exitCode;
|
||||
});
|
||||
|
||||
return await root.Parse(args).InvokeAsync();
|
||||
|
||||
Reference in New Issue
Block a user