Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.

This commit is contained in:
StellaOps Bot
2025-12-26 21:54:17 +02:00
parent 335ff7da16
commit c2b9cd8d1f
3717 changed files with 264714 additions and 48202 deletions

View File

@@ -7,6 +7,11 @@ using StellaOps.EvidenceLocker.Storage;
namespace StellaOps.EvidenceLocker.Api;
/// <summary>
/// Logging category for verdict endpoints.
/// </summary>
internal sealed class VerdictEndpointsLogger;
/// <summary>
/// Minimal API endpoints for verdict attestations.
/// </summary>
@@ -63,7 +68,7 @@ public static class VerdictEndpoints
private static async Task<IResult> StoreVerdictAsync(
[FromBody] StoreVerdictRequest request,
[FromServices] IVerdictRepository repository,
[FromServices] ILogger<Program> logger,
[FromServices] ILogger<VerdictEndpointsLogger> logger,
CancellationToken cancellationToken)
{
try
@@ -132,7 +137,7 @@ public static class VerdictEndpoints
private static async Task<IResult> GetVerdictAsync(
string verdictId,
[FromServices] IVerdictRepository repository,
[FromServices] ILogger<Program> logger,
[FromServices] ILogger<VerdictEndpointsLogger> logger,
CancellationToken cancellationToken)
{
try
@@ -184,13 +189,13 @@ public static class VerdictEndpoints
private static async Task<IResult> ListVerdictsForRunAsync(
string runId,
[FromQuery] string? status,
[FromQuery] string? severity,
[FromQuery] int limit = 50,
[FromQuery] int offset = 0,
[FromServices] IVerdictRepository repository,
[FromServices] ILogger<Program> logger,
CancellationToken cancellationToken)
[FromServices] ILogger<VerdictEndpointsLogger> logger,
CancellationToken cancellationToken,
[FromQuery] string? status = null,
[FromQuery] string? severity = null,
[FromQuery] int limit = 50,
[FromQuery] int offset = 0)
{
try
{
@@ -249,7 +254,7 @@ public static class VerdictEndpoints
private static async Task<IResult> VerifyVerdictAsync(
string verdictId,
[FromServices] IVerdictRepository repository,
[FromServices] ILogger<Program> logger,
[FromServices] ILogger<VerdictEndpointsLogger> logger,
CancellationToken cancellationToken)
{
try
@@ -306,7 +311,7 @@ public static class VerdictEndpoints
private static async Task<IResult> DownloadEnvelopeAsync(
string verdictId,
[FromServices] IVerdictRepository repository,
[FromServices] ILogger<Program> logger,
[FromServices] ILogger<VerdictEndpointsLogger> logger,
CancellationToken cancellationToken)
{
try

View File

@@ -0,0 +1,12 @@
{
"profiles": {
"StellaOps.EvidenceLocker": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:62511;http://localhost:62512"
}
}
}

View File

@@ -124,7 +124,7 @@ public sealed class EvidencePortableBundleService
return new EvidenceBundlePackageResult(metadata.StorageKey, details.Bundle.RootHash, Created: true);
}
private static Stream BuildPackageStream(
private Stream BuildPackageStream(
EvidenceBundleDetails details,
ManifestDocument manifest,
DateTimeOffset generatedAt)
@@ -146,7 +146,7 @@ public sealed class EvidencePortableBundleService
WriteTextEntry(
tarWriter,
_options.InstructionsFileName,
BuildInstructions(details, manifest, generatedAt));
BuildInstructions(details, manifest, generatedAt, _options));
WriteTextEntry(
tarWriter,
@@ -224,7 +224,8 @@ public sealed class EvidencePortableBundleService
private static string BuildInstructions(
EvidenceBundleDetails details,
ManifestDocument manifest,
DateTimeOffset generatedAt)
DateTimeOffset generatedAt,
PortableOptions options)
{
var builder = new StringBuilder();
builder.AppendLine("Portable Evidence Bundle Instructions");
@@ -245,9 +246,9 @@ public sealed class EvidencePortableBundleService
builder.Append("Portable Generated At: ").AppendLine(generatedAt.ToString("O"));
builder.AppendLine();
builder.AppendLine("Verification steps:");
builder.Append("1. Copy '").Append(_options.ArtifactName).AppendLine("' into the sealed environment.");
builder.Append("2. Execute './").Append(_options.OfflineScriptFileName).Append(' ');
builder.Append(_options.ArtifactName).AppendLine("' to extract contents and verify checksums.");
builder.Append("1. Copy '").Append(options.ArtifactName).AppendLine("' into the sealed environment.");
builder.Append("2. Execute './").Append(options.OfflineScriptFileName).Append(' ');
builder.Append(options.ArtifactName).AppendLine("' to extract contents and verify checksums.");
builder.AppendLine("3. Review 'bundle.json' for sanitized metadata and incident context.");
builder.AppendLine("4. Run 'stella evidence verify --bundle <path>' or use an offline verifier with 'manifest.json' + 'signature.json'.");
builder.AppendLine("5. Store the bundle and verification output with the receiving enclave's evidence locker.");

View File

@@ -17,15 +17,15 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.303.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="10.0.0" />
<PackageReference Include="Npgsql" Version="9.0.3" />
<PackageReference Include="AWSSDK.S3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" />
<PackageReference Include="Npgsql" />
</ItemGroup>
<ItemGroup>

View File

@@ -11,7 +11,6 @@ using StellaOps.EvidenceLocker.Core.Domain;
using StellaOps.EvidenceLocker.Infrastructure.Db;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.EvidenceLocker.Tests;
@@ -44,7 +43,7 @@ public sealed class DatabaseMigrationTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
await _migrationRunner!.ApplyAsync(cancellationToken);
@@ -101,7 +100,6 @@ public sealed class DatabaseMigrationTests : IAsyncLifetime
Assert.Equal(0, otherVisible);
await using var violationConnection = await _dataSource.OpenConnectionAsync(tenant, cancellationToken);
using StellaOps.TestKit;
await using var violationCommand = new NpgsqlCommand(@"
INSERT INTO evidence_locker.evidence_bundles
(bundle_id, tenant_id, kind, status, root_hash, storage_key)

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Cryptography;
using StellaOps.EvidenceLocker.Core.Builders;
using StellaOps.EvidenceLocker.Core.Domain;
using StellaOps.EvidenceLocker.Core.Repositories;
@@ -19,7 +20,7 @@ public sealed class EvidenceBundleBuilderTests
public EvidenceBundleBuilderTests()
{
_builder = new EvidenceBundleBuilder(_repository, new MerkleTreeCalculator());
_builder = new EvidenceBundleBuilder(_repository, new MerkleTreeCalculator(new DefaultCryptoHasher(HashAlgorithms.Sha256)));
}
[Trait("Category", TestCategories.Unit)]

View File

@@ -64,7 +64,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenantId = TenantId.FromGuid(Guid.NewGuid());
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
var now = DateTimeOffset.UtcNow;
@@ -107,7 +107,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenant1 = TenantId.FromGuid(Guid.NewGuid());
var tenant2 = TenantId.FromGuid(Guid.NewGuid());
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
@@ -153,7 +153,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenantId = TenantId.FromGuid(Guid.NewGuid());
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
var now = DateTimeOffset.UtcNow;
@@ -193,7 +193,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenantId = TenantId.FromGuid(Guid.NewGuid());
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
var nonExistentBundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
@@ -235,7 +235,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenantId = TenantId.FromGuid(Guid.NewGuid());
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
var now = DateTimeOffset.UtcNow;
@@ -311,7 +311,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenantId = TenantId.FromGuid(Guid.NewGuid());
var now = DateTimeOffset.UtcNow;
@@ -360,7 +360,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenantId = TenantId.FromGuid(Guid.NewGuid());
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
var now = DateTimeOffset.UtcNow;
@@ -407,7 +407,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenantId = TenantId.FromGuid(Guid.NewGuid());
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
var now = DateTimeOffset.UtcNow;
@@ -473,7 +473,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenantId = TenantId.FromGuid(Guid.NewGuid());
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
var now = DateTimeOffset.UtcNow;
@@ -515,7 +515,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenantId = TenantId.FromGuid(Guid.NewGuid());
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
var now = DateTimeOffset.UtcNow;
@@ -561,7 +561,7 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
Assert.Skip(_skipReason);
}
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var tenantId = TenantId.FromGuid(Guid.NewGuid());
var bundleId = EvidenceBundleId.FromGuid(Guid.NewGuid());
var now = DateTimeOffset.UtcNow;
@@ -589,7 +589,8 @@ public sealed class EvidenceBundleImmutabilityTests : IAsyncLifetime
CaseId: $"CASE-{i:D4}",
Reason: $"Legal hold reason {i}",
CreatedAt: now.AddMinutes(i),
ExpiresAt: now.AddDays(30 + i));
ExpiresAt: now.AddDays(30 + i),
ReleasedAt: null);
var createdHold = await _repository.CreateHoldAsync(hold, cancellationToken);
holds.Add(createdHold);

View File

@@ -1,4 +1,4 @@
using System.Buffers.Binary;
using System.Buffers.Binary;
using System.Formats.Tar;
using System.IO.Compression;
using System.Security.Cryptography;
@@ -348,11 +348,12 @@ public sealed class EvidenceBundlePackagingServiceTests
continue;
}
var posixEntry = entry as PosixTarEntry;
entries[entry.Name] = new TarEntryMetadata(
entry.Uid,
entry.Gid,
entry.UserName ?? string.Empty,
entry.GroupName ?? string.Empty,
posixEntry?.UserName ?? string.Empty,
posixEntry?.GroupName ?? string.Empty,
entry.ModificationTime);
}
@@ -443,7 +444,6 @@ public sealed class EvidenceBundlePackagingServiceTests
{
Stored = true;
using var memory = new MemoryStream();
using StellaOps.TestKit;
content.CopyTo(memory);
StoredBytes = memory.ToArray();

View File

@@ -1,8 +1,8 @@
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// EvidenceLockerIntegrationTests.cs
// Sprint: SPRINT_5100_0010_0001_evidencelocker_tests
// Task: EVIDENCE-5100-007
// Description: Integration test: store artifact retrieve artifact verify hash matches
// Description: Integration test: store artifact → retrieve artifact → verify hash matches
// -----------------------------------------------------------------------------
using System.Net;
@@ -20,7 +20,7 @@ namespace StellaOps.EvidenceLocker.Tests;
/// <summary>
/// Integration Tests for EvidenceLocker
/// Task EVIDENCE-5100-007: store artifact retrieve artifact verify hash matches
/// Task EVIDENCE-5100-007: store artifact → retrieve artifact → verify hash matches
/// </summary>
public sealed class EvidenceLockerIntegrationTests : IDisposable
{
@@ -34,7 +34,7 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
_client = _factory.CreateClient();
}
#region EVIDENCE-5100-007: Store Retrieve Verify Hash
#region EVIDENCE-5100-007: Store â Retrieve â Verify Hash
[Trait("Category", TestCategories.Unit)]
[Fact]
@@ -72,10 +72,10 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
var storeResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
payload,
TestContext.Current.CancellationToken);
CancellationToken.None);
storeResponse.EnsureSuccessStatusCode();
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = storeResult.GetProperty("bundleId").GetString();
var storedRootHash = storeResult.GetProperty("rootHash").GetString();
@@ -85,10 +85,10 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
// Act - Retrieve
var retrieveResponse = await _client.GetAsync(
$"/evidence/{bundleId}",
TestContext.Current.CancellationToken);
CancellationToken.None);
retrieveResponse.EnsureSuccessStatusCode();
var retrieveResult = await retrieveResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var retrieveResult = await retrieveResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var retrievedRootHash = retrieveResult.GetProperty("rootHash").GetString();
var retrievedBundleId = retrieveResult.GetProperty("bundleId").GetString();
@@ -111,22 +111,22 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
var storeResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
payload,
TestContext.Current.CancellationToken);
CancellationToken.None);
storeResponse.EnsureSuccessStatusCode();
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = storeResult.GetProperty("bundleId").GetString();
// Act - Download
var downloadResponse = await _client.GetAsync(
$"/evidence/{bundleId}/download",
TestContext.Current.CancellationToken);
CancellationToken.None);
downloadResponse.EnsureSuccessStatusCode();
// Assert
downloadResponse.Content.Headers.ContentType?.MediaType.Should().Be("application/gzip");
var archiveBytes = await downloadResponse.Content.ReadAsByteArrayAsync(TestContext.Current.CancellationToken);
var archiveBytes = await downloadResponse.Content.ReadAsByteArrayAsync(CancellationToken.None);
archiveBytes.Should().NotBeEmpty();
// Verify archive contains manifest with correct bundleId
@@ -174,10 +174,10 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
var response = await _client.PostAsJsonAsync(
"/evidence/snapshot",
payload,
TestContext.Current.CancellationToken);
CancellationToken.None);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var result = await response.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
hashes.Add(result.GetProperty("rootHash").GetString()!);
}
@@ -199,10 +199,10 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
var storeResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
payload,
TestContext.Current.CancellationToken);
CancellationToken.None);
storeResponse.EnsureSuccessStatusCode();
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
// Assert - Signature should be present and valid
storeResult.TryGetProperty("signature", out var signature).Should().BeTrue();
@@ -249,19 +249,19 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
var storeResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
payload,
TestContext.Current.CancellationToken);
CancellationToken.None);
storeResponse.EnsureSuccessStatusCode();
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = storeResult.GetProperty("bundleId").GetString();
// Act - Retrieve
var retrieveResponse = await _client.GetAsync(
$"/evidence/{bundleId}",
TestContext.Current.CancellationToken);
CancellationToken.None);
retrieveResponse.EnsureSuccessStatusCode();
var retrieveResult = await retrieveResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var retrieveResult = await retrieveResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
// Assert - Metadata preserved
retrieveResult.TryGetProperty("metadata", out var retrievedMetadata).Should().BeTrue();
@@ -289,10 +289,10 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
var storeResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
payload,
TestContext.Current.CancellationToken);
CancellationToken.None);
storeResponse.EnsureSuccessStatusCode();
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = storeResult.GetProperty("bundleId").GetString();
// Assert - Timeline event emitted
@@ -318,22 +318,22 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
var storeResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
payload,
TestContext.Current.CancellationToken);
CancellationToken.None);
storeResponse.EnsureSuccessStatusCode();
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var storeResult = await storeResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = storeResult.GetProperty("bundleId").GetString();
// Act - Portable download
var portableResponse = await _client.GetAsync(
$"/evidence/{bundleId}/portable",
TestContext.Current.CancellationToken);
CancellationToken.None);
portableResponse.EnsureSuccessStatusCode();
// Assert
portableResponse.Content.Headers.ContentType?.MediaType.Should().Be("application/gzip");
var archiveBytes = await portableResponse.Content.ReadAsByteArrayAsync(TestContext.Current.CancellationToken);
var archiveBytes = await portableResponse.Content.ReadAsByteArrayAsync(CancellationToken.None);
var entries = ReadGzipTarEntries(archiveBytes);
// Portable bundle should have manifest but be sanitized
@@ -395,7 +395,6 @@ public sealed class EvidenceLockerIntegrationTests : IDisposable
if (entry.DataStream is not null)
{
using var contentStream = new MemoryStream();
using StellaOps.TestKit;
entry.DataStream.CopyTo(contentStream);
entries[entry.Name] = Encoding.UTF8.GetString(contentStream.ToArray());
}

View File

@@ -29,9 +29,11 @@ using StellaOps.EvidenceLocker.Core.Incident;
using StellaOps.EvidenceLocker.Core.Timeline;
using StellaOps.EvidenceLocker.Core.Storage;
using EvidenceLockerProgram = StellaOps.EvidenceLocker.WebService.Program;
namespace StellaOps.EvidenceLocker.Tests;
internal sealed class EvidenceLockerWebApplicationFactory : WebApplicationFactory<Program>
internal sealed class EvidenceLockerWebApplicationFactory : WebApplicationFactory<EvidenceLockerProgram>
{
private readonly string _contentRoot;

View File

@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// EvidenceLockerWebServiceContractTests.cs
// Sprint: SPRINT_5100_0010_0001_evidencelocker_tests
// Tasks: EVIDENCE-5100-004, EVIDENCE-5100-005, EVIDENCE-5100-006
@@ -53,12 +53,12 @@ public sealed class EvidenceLockerWebServiceContractTests : IDisposable
var payload = CreateValidSnapshotPayload();
// Act
var response = await _client.PostAsJsonAsync("/evidence/snapshot", payload, TestContext.Current.CancellationToken);
var response = await _client.PostAsJsonAsync("/evidence/snapshot", payload, CancellationToken.None);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var content = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
var content = await response.Content.ReadAsStringAsync(CancellationToken.None);
using var doc = JsonDocument.Parse(content);
var root = doc.RootElement;
@@ -85,21 +85,20 @@ public sealed class EvidenceLockerWebServiceContractTests : IDisposable
var createResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
createResponse.EnsureSuccessStatusCode();
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = created.GetProperty("bundleId").GetString();
// Act
var response = await _client.GetAsync($"/evidence/{bundleId}", TestContext.Current.CancellationToken);
var response = await _client.GetAsync($"/evidence/{bundleId}", CancellationToken.None);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var content = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
var content = await response.Content.ReadAsStringAsync(CancellationToken.None);
using var doc = JsonDocument.Parse(content);
using StellaOps.TestKit;
var root = doc.RootElement;
// Verify contract schema for retrieved bundle
@@ -121,14 +120,14 @@ using StellaOps.TestKit;
var createResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
createResponse.EnsureSuccessStatusCode();
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = created.GetProperty("bundleId").GetString();
// Act
var response = await _client.GetAsync($"/evidence/{bundleId}/download", TestContext.Current.CancellationToken);
var response = await _client.GetAsync($"/evidence/{bundleId}/download", CancellationToken.None);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
@@ -145,7 +144,7 @@ using StellaOps.TestKit;
var response = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
// Assert - Unauthorized should return consistent error schema
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
@@ -161,7 +160,7 @@ using StellaOps.TestKit;
var nonExistentId = Guid.NewGuid();
// Act
var response = await _client.GetAsync($"/evidence/{nonExistentId}", TestContext.Current.CancellationToken);
var response = await _client.GetAsync($"/evidence/{nonExistentId}", CancellationToken.None);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
@@ -181,7 +180,7 @@ using StellaOps.TestKit;
var response = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
@@ -199,7 +198,7 @@ using StellaOps.TestKit;
var response = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
// Assert
response.StatusCode.Should().BeOneOf(HttpStatusCode.Forbidden, HttpStatusCode.Unauthorized);
@@ -217,7 +216,7 @@ using StellaOps.TestKit;
var response = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
@@ -234,17 +233,17 @@ using StellaOps.TestKit;
var createResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
createResponse.EnsureSuccessStatusCode();
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = created.GetProperty("bundleId").GetString();
// Change to no read scope
ConfigureAuthHeaders(_client, tenantId, scopes: StellaOpsScopes.EvidenceCreate);
// Act
var response = await _client.GetAsync($"/evidence/{bundleId}", TestContext.Current.CancellationToken);
var response = await _client.GetAsync($"/evidence/{bundleId}", CancellationToken.None);
// Assert
response.StatusCode.Should().BeOneOf(HttpStatusCode.Forbidden, HttpStatusCode.Unauthorized);
@@ -261,10 +260,10 @@ using StellaOps.TestKit;
var createResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
createResponse.EnsureSuccessStatusCode();
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = created.GetProperty("bundleId").GetString();
// Try to access as tenant B
@@ -272,7 +271,7 @@ using StellaOps.TestKit;
ConfigureAuthHeaders(_client, tenantB, scopes: $"{StellaOpsScopes.EvidenceRead}");
// Act
var response = await _client.GetAsync($"/evidence/{bundleId}", TestContext.Current.CancellationToken);
var response = await _client.GetAsync($"/evidence/{bundleId}", CancellationToken.None);
// Assert - Should not be accessible across tenants
response.StatusCode.Should().BeOneOf(HttpStatusCode.NotFound, HttpStatusCode.Forbidden);
@@ -289,17 +288,17 @@ using StellaOps.TestKit;
var createResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
createResponse.EnsureSuccessStatusCode();
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = created.GetProperty("bundleId").GetString();
// Remove read scope
ConfigureAuthHeaders(_client, tenantId, scopes: StellaOpsScopes.EvidenceCreate);
// Act
var response = await _client.GetAsync($"/evidence/{bundleId}/download", TestContext.Current.CancellationToken);
var response = await _client.GetAsync($"/evidence/{bundleId}/download", CancellationToken.None);
// Assert
response.StatusCode.Should().BeOneOf(HttpStatusCode.Forbidden, HttpStatusCode.Unauthorized);
@@ -340,10 +339,10 @@ using StellaOps.TestKit;
var response = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
response.EnsureSuccessStatusCode();
var created = await response.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var created = await response.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = created.GetProperty("bundleId").GetString();
// Assert
@@ -369,7 +368,7 @@ using StellaOps.TestKit;
var response = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
response.EnsureSuccessStatusCode();
// Assert
@@ -391,17 +390,17 @@ using StellaOps.TestKit;
var createResponse = await _client.PostAsJsonAsync(
"/evidence/snapshot",
CreateValidSnapshotPayload(),
TestContext.Current.CancellationToken);
CancellationToken.None);
createResponse.EnsureSuccessStatusCode();
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(TestContext.Current.CancellationToken);
var created = await createResponse.Content.ReadFromJsonAsync<JsonElement>(CancellationToken.None);
var bundleId = created.GetProperty("bundleId").GetString();
// Clear timeline events before retrieve
_factory.TimelinePublisher.ClearEvents();
// Act
var response = await _client.GetAsync($"/evidence/{bundleId}", TestContext.Current.CancellationToken);
var response = await _client.GetAsync($"/evidence/{bundleId}", CancellationToken.None);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
@@ -417,12 +416,12 @@ using StellaOps.TestKit;
ConfigureAuthHeaders(_client, tenantId, scopes: StellaOpsScopes.EvidenceRead);
// Act - Request non-existent bundle
var response = await _client.GetAsync($"/evidence/{Guid.NewGuid()}", TestContext.Current.CancellationToken);
var response = await _client.GetAsync($"/evidence/{Guid.NewGuid()}", CancellationToken.None);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
var content = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
var content = await response.Content.ReadAsStringAsync(CancellationToken.None);
content.Should().NotContain("Exception");
content.Should().NotContain("StackTrace");
content.Should().NotContain("InnerException");

View File

@@ -1,4 +1,4 @@
using System.Buffers.Binary;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Formats.Tar;
using System.IO;
@@ -50,10 +50,10 @@ public sealed class EvidenceLockerWebServiceTests
}
};
var snapshotResponse = await client.PostAsJsonAsync("/evidence/snapshot", payload, TestContext.Current.CancellationToken);
var snapshotResponse = await client.PostAsJsonAsync("/evidence/snapshot", payload, CancellationToken.None);
snapshotResponse.EnsureSuccessStatusCode();
var snapshot = await snapshotResponse.Content.ReadFromJsonAsync<EvidenceSnapshotResponseDto>(TestContext.Current.CancellationToken);
var snapshot = await snapshotResponse.Content.ReadFromJsonAsync<EvidenceSnapshotResponseDto>(CancellationToken.None);
Assert.NotNull(snapshot);
Assert.NotEqual(Guid.Empty, snapshot!.BundleId);
Assert.False(string.IsNullOrEmpty(snapshot.RootHash));
@@ -65,7 +65,7 @@ public sealed class EvidenceLockerWebServiceTests
Assert.Contains(snapshot.BundleId.ToString("D"), timelineEvent);
Assert.Contains(snapshot.RootHash, timelineEvent);
var bundle = await client.GetFromJsonAsync<EvidenceBundleResponseDto>($"/evidence/{snapshot.BundleId}", TestContext.Current.CancellationToken);
var bundle = await client.GetFromJsonAsync<EvidenceBundleResponseDto>($"/evidence/{snapshot.BundleId}", CancellationToken.None);
Assert.NotNull(bundle);
Assert.Equal(snapshot.RootHash, bundle!.RootHash);
Assert.NotNull(bundle.Signature);
@@ -105,13 +105,13 @@ public sealed class EvidenceLockerWebServiceTests
}
};
var snapshotResponse = await client.PostAsJsonAsync("/evidence/snapshot", payload, TestContext.Current.CancellationToken);
var snapshotResponse = await client.PostAsJsonAsync("/evidence/snapshot", payload, CancellationToken.None);
snapshotResponse.EnsureSuccessStatusCode();
var snapshot = await snapshotResponse.Content.ReadFromJsonAsync<EvidenceSnapshotResponseDto>(TestContext.Current.CancellationToken);
var snapshot = await snapshotResponse.Content.ReadFromJsonAsync<EvidenceSnapshotResponseDto>(CancellationToken.None);
Assert.NotNull(snapshot);
var bundle = await client.GetFromJsonAsync<EvidenceBundleResponseDto>($"/evidence/{snapshot!.BundleId}", TestContext.Current.CancellationToken);
var bundle = await client.GetFromJsonAsync<EvidenceBundleResponseDto>($"/evidence/{snapshot!.BundleId}", CancellationToken.None);
Assert.NotNull(bundle);
Assert.NotNull(bundle!.ExpiresAt);
Assert.True(bundle.ExpiresAt > bundle.CreatedAt);
@@ -141,17 +141,17 @@ public sealed class EvidenceLockerWebServiceTests
}
};
var snapshotResponse = await client.PostAsJsonAsync("/evidence/snapshot", payload, TestContext.Current.CancellationToken);
var snapshotResponse = await client.PostAsJsonAsync("/evidence/snapshot", payload, CancellationToken.None);
snapshotResponse.EnsureSuccessStatusCode();
var snapshot = await snapshotResponse.Content.ReadFromJsonAsync<EvidenceSnapshotResponseDto>(TestContext.Current.CancellationToken);
var snapshot = await snapshotResponse.Content.ReadFromJsonAsync<EvidenceSnapshotResponseDto>(CancellationToken.None);
Assert.NotNull(snapshot);
var downloadResponse = await client.GetAsync($"/evidence/{snapshot!.BundleId}/download", TestContext.Current.CancellationToken);
var downloadResponse = await client.GetAsync($"/evidence/{snapshot!.BundleId}/download", CancellationToken.None);
downloadResponse.EnsureSuccessStatusCode();
Assert.Equal("application/gzip", downloadResponse.Content.Headers.ContentType?.MediaType);
var archiveBytes = await downloadResponse.Content.ReadAsByteArrayAsync(TestContext.Current.CancellationToken);
var archiveBytes = await downloadResponse.Content.ReadAsByteArrayAsync(CancellationToken.None);
var mtime = BinaryPrimitives.ReadInt32LittleEndian(archiveBytes.AsSpan(4, 4));
var expectedSeconds = (int)(new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero) - DateTimeOffset.UnixEpoch).TotalSeconds;
Assert.Equal(expectedSeconds, mtime);
@@ -196,16 +196,16 @@ public sealed class EvidenceLockerWebServiceTests
}
};
var snapshotResponse = await client.PostAsJsonAsync("/evidence/snapshot", payload, TestContext.Current.CancellationToken);
var snapshotResponse = await client.PostAsJsonAsync("/evidence/snapshot", payload, CancellationToken.None);
snapshotResponse.EnsureSuccessStatusCode();
var snapshot = await snapshotResponse.Content.ReadFromJsonAsync<EvidenceSnapshotResponseDto>(TestContext.Current.CancellationToken);
var snapshot = await snapshotResponse.Content.ReadFromJsonAsync<EvidenceSnapshotResponseDto>(CancellationToken.None);
Assert.NotNull(snapshot);
var portableResponse = await client.GetAsync($"/evidence/{snapshot!.BundleId}/portable", TestContext.Current.CancellationToken);
var portableResponse = await client.GetAsync($"/evidence/{snapshot!.BundleId}/portable", CancellationToken.None);
portableResponse.EnsureSuccessStatusCode();
Assert.Equal("application/gzip", portableResponse.Content.Headers.ContentType?.MediaType);
var archiveBytes = await portableResponse.Content.ReadAsByteArrayAsync(TestContext.Current.CancellationToken);
var archiveBytes = await portableResponse.Content.ReadAsByteArrayAsync(CancellationToken.None);
var entries = ReadArchiveEntries(archiveBytes);
Assert.Contains("bundle.json", entries.Keys);
Assert.Contains("instructions-portable.txt", entries.Keys);
@@ -243,11 +243,11 @@ public sealed class EvidenceLockerWebServiceTests
}
};
var response = await client.PostAsJsonAsync("/evidence/snapshot", payload, TestContext.Current.CancellationToken);
var response = await client.PostAsJsonAsync("/evidence/snapshot", payload, CancellationToken.None);
var responseContent = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
var responseContent = await response.Content.ReadAsStringAsync(CancellationToken.None);
Assert.True(response.StatusCode == HttpStatusCode.BadRequest, $"Expected 400 but received {(int)response.StatusCode}: {responseContent}");
var problem = await response.Content.ReadFromJsonAsync<ValidationProblemDetails>(TestContext.Current.CancellationToken);
var problem = await response.Content.ReadFromJsonAsync<ValidationProblemDetails>(CancellationToken.None);
Assert.NotNull(problem);
Assert.True(problem!.Errors.TryGetValue("message", out var messages));
Assert.Contains(messages, m => m.Contains("exceeds", StringComparison.OrdinalIgnoreCase));
@@ -272,9 +272,9 @@ public sealed class EvidenceLockerWebServiceTests
}
};
var response = await client.PostAsJsonAsync("/evidence/snapshot", payload, TestContext.Current.CancellationToken);
var response = await client.PostAsJsonAsync("/evidence/snapshot", payload, CancellationToken.None);
var responseContent = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
var responseContent = await response.Content.ReadAsStringAsync(CancellationToken.None);
Assert.True(response.StatusCode == HttpStatusCode.Forbidden, $"Expected 403 but received {(int)response.StatusCode}: {responseContent}");
}
@@ -296,11 +296,11 @@ public sealed class EvidenceLockerWebServiceTests
{
reason = "legal-hold"
},
TestContext.Current.CancellationToken);
CancellationToken.None);
var responseContent = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
var responseContent = await response.Content.ReadAsStringAsync(CancellationToken.None);
Assert.True(response.StatusCode == HttpStatusCode.BadRequest, $"Expected 400 but received {(int)response.StatusCode}: {responseContent}");
var problem = await response.Content.ReadFromJsonAsync<ValidationProblemDetails>(TestContext.Current.CancellationToken);
var problem = await response.Content.ReadFromJsonAsync<ValidationProblemDetails>(CancellationToken.None);
Assert.NotNull(problem);
Assert.True(problem!.Errors.TryGetValue("message", out var messages));
Assert.Contains(messages, m => m.IndexOf("already exists", StringComparison.OrdinalIgnoreCase) >= 0);
@@ -323,10 +323,10 @@ public sealed class EvidenceLockerWebServiceTests
reason = "retention",
notes = "retain for investigation"
},
TestContext.Current.CancellationToken);
CancellationToken.None);
response.EnsureSuccessStatusCode();
var hold = await response.Content.ReadFromJsonAsync<EvidenceHoldResponseDto>(TestContext.Current.CancellationToken);
var hold = await response.Content.ReadFromJsonAsync<EvidenceHoldResponseDto>(CancellationToken.None);
Assert.NotNull(hold);
Assert.Contains($"hold:{hold!.CaseId}", factory.TimelinePublisher.PublishedEvents);
}
@@ -347,7 +347,6 @@ public sealed class EvidenceLockerWebServiceTests
}
using var entryStream = new MemoryStream();
using StellaOps.TestKit;
entry.DataStream!.CopyTo(entryStream);
var content = Encoding.UTF8.GetString(entryStream.ToArray());
entries[entry.Name] = content;

View File

@@ -1,4 +1,4 @@
using System.Formats.Tar;
using System.Formats.Tar;
using System.IO.Compression;
using System.Text;
using System.Text.Json;
@@ -248,11 +248,12 @@ public sealed class EvidencePortableBundleServiceTests
continue;
}
var posixEntry = entry as PosixTarEntry;
entries[entry.Name] = new TarEntryMetadata(
entry.Uid,
entry.Gid,
entry.UserName ?? string.Empty,
entry.GroupName ?? string.Empty,
posixEntry?.UserName ?? string.Empty,
posixEntry?.GroupName ?? string.Empty,
entry.ModificationTime);
}
@@ -337,7 +338,6 @@ public sealed class EvidencePortableBundleServiceTests
{
Stored = true;
using var memory = new MemoryStream();
using StellaOps.TestKit;
content.CopyTo(memory);
StoredBytes = memory.ToArray();

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Security.Cryptography;
@@ -200,7 +200,6 @@ public sealed class EvidenceSignatureServiceTests
private static SigningKeyMaterialOptions CreateKeyMaterial()
{
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
using StellaOps.TestKit;
var privatePem = ecdsa.ExportECPrivateKeyPem();
var publicPem = ecdsa.ExportSubjectPublicKeyInfoPem();
return new SigningKeyMaterialOptions

View File

@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Linq;
@@ -477,7 +477,6 @@ public sealed class EvidenceSnapshotServiceTests
CancellationToken cancellationToken)
{
using var memory = new MemoryStream();
using StellaOps.TestKit;
content.CopyTo(memory);
var bytes = memory.ToArray();

View File

@@ -1,4 +1,4 @@
using System.Text;
using System.Text;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.EvidenceLocker.Core.Configuration;
using StellaOps.EvidenceLocker.Core.Domain;
@@ -22,7 +22,7 @@ public sealed class FileSystemEvidenceObjectStoreTests : IDisposable
[Fact]
public async Task StoreAsync_EnforcesWriteOnceWhenConfigured()
{
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var store = CreateStore(enforceWriteOnce: true);
var options = CreateWriteOptions();
@@ -37,7 +37,7 @@ public sealed class FileSystemEvidenceObjectStoreTests : IDisposable
[Fact]
public async Task StoreAsync_AllowsOverwriteWhenWriteOnceDisabled()
{
var cancellationToken = TestContext.Current.CancellationToken;
var cancellationToken = CancellationToken.None;
var store = CreateStore(enforceWriteOnce: false);
var options = CreateWriteOptions() with { EnforceWriteOnce = false };
@@ -45,7 +45,6 @@ public sealed class FileSystemEvidenceObjectStoreTests : IDisposable
var firstMetadata = await store.StoreAsync(first, options, cancellationToken);
using var second = CreateStream("payload-1");
using StellaOps.TestKit;
var secondMetadata = await store.StoreAsync(second, options, cancellationToken);
Assert.Equal(firstMetadata.Sha256, secondMetadata.Sha256);

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
@@ -77,7 +77,6 @@ public sealed class GoldenFixturesTests
private static JsonElement ReadJson(string path)
{
using var doc = JsonDocument.Parse(File.ReadAllText(path), new JsonDocumentOptions { AllowTrailingCommas = true });
using StellaOps.TestKit;
return doc.RootElement.Clone();
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Amazon;
@@ -116,7 +116,6 @@ public sealed class S3EvidenceObjectStoreTests
var ifNoneMatch = request.Headers?["If-None-Match"];
using var memory = new MemoryStream();
using StellaOps.TestKit;
request.InputStream.CopyTo(memory);
PutRequests.Add(new CapturedPutObjectRequest(

View File

@@ -1,22 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<UseXunitV3>true</UseXunitV3>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseConcelierTestInfra>false</UseConcelierTestInfra>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNet.Testcontainers" Version="1.6.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Npgsql" Version="9.0.3" />
<PackageReference Include="xunit.v3" Version="3.0.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3" />
<PackageReference Include="DotNet.Testcontainers" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Npgsql" />
<PackageReference Include="xunit.v3" />
</ItemGroup>
<ItemGroup>
@@ -35,5 +33,6 @@
<ProjectReference Include="..\StellaOps.EvidenceLocker.Infrastructure\StellaOps.EvidenceLocker.Infrastructure.csproj" />
<ProjectReference Include="..\StellaOps.EvidenceLocker.WebService\StellaOps.EvidenceLocker.WebService.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
</ItemGroup>
</Project>
</Project>

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
@@ -125,7 +125,6 @@ public sealed class TimelineIndexerEvidenceTimelinePublisherTests
Assert.Equal(HttpMethod.Post, request.Method);
using var json = JsonDocument.Parse(request.Content!);
using StellaOps.TestKit;
var root = json.RootElement;
Assert.Equal("evidence.hold.created", root.GetProperty("kind").GetString());
Assert.Equal(hold.CaseId, root.GetProperty("attributes").GetProperty("caseId").GetString());

View File

@@ -347,3 +347,9 @@ static IResult ValidationProblem(string message)
{
["message"] = new[] { message }
});
// Make Program class accessible for integration testing
namespace StellaOps.EvidenceLocker.WebService
{
public partial class Program { }
}

View File

@@ -9,7 +9,11 @@
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
</ItemGroup>
<!-- Include Api folder from parent project -->
<ItemGroup>
<Compile Include="..\Api\**\*.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StellaOps.EvidenceLocker.csproj" />
@@ -17,6 +21,6 @@
<ProjectReference Include="..\StellaOps.EvidenceLocker.Infrastructure\StellaOps.EvidenceLocker.Infrastructure.csproj" />
<ProjectReference Include="..\..\..\Authority\StellaOps.Authority\StellaOps.Auth.ServerIntegration\StellaOps.Auth.ServerIntegration.csproj" />
<ProjectReference Include="..\..\..\Authority\StellaOps.Authority\StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Router.AspNet\StellaOps.Router.AspNet.csproj" />
<ProjectReference Include="..\..\..\Router/__Libraries/StellaOps.Router.AspNet\StellaOps.Router.AspNet.csproj" />
</ItemGroup>
</Project>

View File

@@ -21,7 +21,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0"/>
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>

View File

@@ -1,24 +1,33 @@
<?xml version='1.0' encoding='utf-8'?>
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<!-- Exclude subprojects from compilation - they have their own .csproj files -->
<ItemGroup>
<PackageReference Include="Npgsql" Version="9.0.3" />
<PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<Compile Remove="Api\**\*.cs" />
<Compile Remove="StellaOps.EvidenceLocker.Core\**\*.cs" />
<Compile Remove="StellaOps.EvidenceLocker.Infrastructure\**\*.cs" />
<Compile Remove="StellaOps.EvidenceLocker.Tests\**\*.cs" />
<Compile Remove="StellaOps.EvidenceLocker.WebService\**\*.cs" />
<Compile Remove="StellaOps.EvidenceLocker.Worker\**\*.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Npgsql" />
<PackageReference Include="Dapper" />
<PackageReference Include="OpenTelemetry.Exporter.Console" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" />
<PackageReference Include="Serilog.AspNetCore" />
<PackageReference Include="Serilog.Sinks.Console" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,90 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.EvidenceLocker.Core", "StellaOps.EvidenceLocker.Core\StellaOps.EvidenceLocker.Core.csproj", "{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.EvidenceLocker.Infrastructure", "StellaOps.EvidenceLocker.Infrastructure\StellaOps.EvidenceLocker.Infrastructure.csproj", "{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.EvidenceLocker.WebService", "StellaOps.EvidenceLocker.WebService\StellaOps.EvidenceLocker.WebService.csproj", "{392D1580-C75B-4CB2-8F26-45C65268A191}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.EvidenceLocker.Worker", "StellaOps.EvidenceLocker.Worker\StellaOps.EvidenceLocker.Worker.csproj", "{B384F421-48D0-48EB-A63F-0AF28EBC75EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.EvidenceLocker.Tests", "StellaOps.EvidenceLocker.Tests\StellaOps.EvidenceLocker.Tests.csproj", "{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Debug|x64.ActiveCfg = Debug|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Debug|x64.Build.0 = Debug|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Debug|x86.ActiveCfg = Debug|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Debug|x86.Build.0 = Debug|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Release|Any CPU.Build.0 = Release|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Release|x64.ActiveCfg = Release|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Release|x64.Build.0 = Release|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Release|x86.ActiveCfg = Release|Any CPU
{217D54F6-D07F-4B1E-8598-7DCAF0BD65C7}.Release|x86.Build.0 = Release|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Debug|x64.ActiveCfg = Debug|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Debug|x64.Build.0 = Debug|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Debug|x86.ActiveCfg = Debug|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Debug|x86.Build.0 = Debug|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Release|Any CPU.Build.0 = Release|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Release|x64.ActiveCfg = Release|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Release|x64.Build.0 = Release|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Release|x86.ActiveCfg = Release|Any CPU
{BF61F2F5-4ECA-4DA6-AC6B-102C39D225A1}.Release|x86.Build.0 = Release|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Debug|Any CPU.Build.0 = Debug|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Debug|x64.ActiveCfg = Debug|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Debug|x64.Build.0 = Debug|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Debug|x86.ActiveCfg = Debug|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Debug|x86.Build.0 = Debug|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Release|Any CPU.ActiveCfg = Release|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Release|Any CPU.Build.0 = Release|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Release|x64.ActiveCfg = Release|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Release|x64.Build.0 = Release|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Release|x86.ActiveCfg = Release|Any CPU
{392D1580-C75B-4CB2-8F26-45C65268A191}.Release|x86.Build.0 = Release|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Debug|x64.ActiveCfg = Debug|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Debug|x64.Build.0 = Debug|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Debug|x86.ActiveCfg = Debug|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Debug|x86.Build.0 = Debug|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Release|Any CPU.Build.0 = Release|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Release|x64.ActiveCfg = Release|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Release|x64.Build.0 = Release|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Release|x86.ActiveCfg = Release|Any CPU
{B384F421-48D0-48EB-A63F-0AF28EBC75EB}.Release|x86.Build.0 = Release|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Debug|x64.ActiveCfg = Debug|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Debug|x64.Build.0 = Debug|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Debug|x86.ActiveCfg = Debug|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Debug|x86.Build.0 = Debug|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Release|Any CPU.Build.0 = Release|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Release|x64.ActiveCfg = Release|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Release|x64.Build.0 = Release|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Release|x86.ActiveCfg = Release|Any CPU
{B9D6DCF2-1C6F-41E5-8D63-118BD0751839}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal