up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-03 00:10:19 +02:00
parent ea1d58a89b
commit 37cba83708
158 changed files with 147438 additions and 867 deletions

View File

@@ -0,0 +1,131 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http.Json;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using StellaOps.Excititor.Storage.Mongo;
using StellaOps.Excititor.WebService.Contracts;
using StellaOps.Excititor.WebService.Options;
using Xunit;
namespace StellaOps.Excititor.WebService.Tests;
public sealed class EvidenceLockerEndpointTests : IAsyncLifetime
{
private readonly string _tempDir = Path.Combine(Path.GetTempPath(), "excititor-locker-tests-" + Guid.NewGuid());
private TestWebApplicationFactory _factory = null!;
[Fact]
public async Task LockerEndpoint_ReturnsHashesFromLocalFiles_WhenLockerRootConfigured()
{
Directory.CreateDirectory(_tempDir);
var manifestRel = Path.Combine("locker", "bundle-1", "g1", "manifest.json");
var evidenceRel = Path.Combine("locker", "bundle-1", "g1", "bundle.ndjson");
var manifestFull = Path.Combine(_tempDir, manifestRel);
var evidenceFull = Path.Combine(_tempDir, evidenceRel);
Directory.CreateDirectory(Path.GetDirectoryName(manifestFull)!);
await File.WriteAllTextAsync(manifestFull, "{\n \"id\": \"bundle-1\"\n}\n");
await File.WriteAllTextAsync(evidenceFull, "line1\nline2\n");
var record = new AirgapImportRecord
{
Id = "bundle-1:g1",
TenantId = "test",
BundleId = "bundle-1",
MirrorGeneration = "g1",
Publisher = "test-pub",
PayloadHash = "sha256:payload",
Signature = "sig",
PortableManifestPath = manifestRel,
PortableManifestHash = "sha256:old",
EvidenceLockerPath = evidenceRel,
ImportedAt = DateTimeOffset.UtcNow,
SignedAt = DateTimeOffset.UtcNow,
};
var stub = (StubAirgapImportStore)_factory.Services.GetRequiredService<IAirgapImportStore>();
await stub.SaveAsync(record, CancellationToken.None);
using var client = _factory.WithWebHostBuilder(_ => { }).CreateClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "vex.read");
var response = await client.GetAsync($"/evidence/vex/locker/{record.BundleId}");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var payload = await response.Content.ReadFromJsonAsync<VexEvidenceLockerResponse>();
Assert.NotNull(payload);
Assert.Equal("test", payload!.Tenant);
Assert.Equal(record.BundleId, payload.BundleId);
Assert.Equal("sha256:22734ec66c856d27d0023839d8ea11cdeaac379496952e52d204b3265981af66", payload.ManifestHash);
Assert.Equal("sha256:2751a3a2f303ad21752038085e2b8c5f98ecff61a2e4ebbd43506a941725be80", payload.EvidenceHash);
Assert.Equal(23, payload.ManifestSizeBytes);
Assert.Equal(12, payload.EvidenceSizeBytes);
}
public Task InitializeAsync()
{
_factory = new TestWebApplicationFactory(
configureConfiguration: config =>
{
config.AddInMemoryCollection(new[]
{
new KeyValuePair<string, string?>("Excititor:Airgap:LockerRootPath", _tempDir)
});
},
configureServices: services =>
{
services.RemoveAll<IAirgapImportStore>();
services.AddSingleton<IAirgapImportStore>(new StubAirgapImportStore());
});
return Task.CompletedTask;
}
public Task DisposeAsync()
{
try
{
Directory.Delete(_tempDir, recursive: true);
}
catch
{
// ignore cleanup errors
}
_factory.Dispose();
return Task.CompletedTask;
}
private sealed class StubAirgapImportStore : IAirgapImportStore
{
private AirgapImportRecord? _record;
public Task SaveAsync(AirgapImportRecord record, CancellationToken cancellationToken)
{
_record = record;
return Task.CompletedTask;
}
public Task<AirgapImportRecord?> FindByBundleIdAsync(string tenantId, string bundleId, string? mirrorGeneration, CancellationToken cancellationToken)
{
return Task.FromResult(_record);
}
public Task<IReadOnlyList<AirgapImportRecord>> ListAsync(string tenantId, string? publisherFilter, DateTimeOffset? importedAfter, int limit, int offset, CancellationToken cancellationToken)
{
IReadOnlyList<AirgapImportRecord> list = _record is null ? Array.Empty<AirgapImportRecord>() : new[] { _record };
return Task.FromResult(list);
}
public Task<int> CountAsync(string tenantId, string? publisherFilter, DateTimeOffset? importedAfter, CancellationToken cancellationToken)
{
return Task.FromResult(_record is null ? 0 : 1);
}
}
}