up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using StellaOps.SbomService.Models;
|
||||
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class EntrypointEndpointsTests : IClassFixture<SbomServiceWebApplicationFactory>
|
||||
public class EntrypointEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
{
|
||||
private readonly SbomServiceWebApplicationFactory _factory;
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
|
||||
public EntrypointEndpointsTests(SbomServiceWebApplicationFactory factory)
|
||||
public EntrypointEndpointsTests(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using StellaOps.SbomService.Models;
|
||||
using System.Text.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class OrchestratorEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
|
||||
public OrchestratorEndpointsTests(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task List_sources_requires_tenant()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
var response = await client.GetAsync("/internal/orchestrator/sources");
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task List_and_register_sources_are_deterministic()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var seeded = await client.GetFromJsonAsync<JsonElement>("/internal/orchestrator/sources?tenant=tenant-a");
|
||||
seeded.TryGetProperty("items", out var items).Should().BeTrue();
|
||||
items.GetArrayLength().Should().BeGreaterOrEqualTo(1);
|
||||
|
||||
var request = new RegisterOrchestratorSourceRequest(
|
||||
TenantId: "tenant-a",
|
||||
ArtifactDigest: "sha256:new123",
|
||||
SourceType: "scanner-index",
|
||||
Metadata: "seeded:test");
|
||||
|
||||
var post = await client.PostAsJsonAsync("/internal/orchestrator/sources", request);
|
||||
post.EnsureSuccessStatusCode();
|
||||
var created = await post.Content.ReadFromJsonAsync<OrchestratorSource>();
|
||||
created.Should().NotBeNull();
|
||||
created!.ArtifactDigest.Should().Be("sha256:new123");
|
||||
|
||||
// Idempotent on digest+type
|
||||
var postAgain = await client.PostAsJsonAsync("/internal/orchestrator/sources", request);
|
||||
postAgain.EnsureSuccessStatusCode();
|
||||
var again = await postAgain.Content.ReadFromJsonAsync<OrchestratorSource>();
|
||||
again.Should().NotBeNull();
|
||||
again!.SourceId.Should().Be(created.SourceId);
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,8 @@ public class ProjectionEndpointTests : IClassFixture<WebApplicationFactory<Progr
|
||||
json.tenantId.Should().Be("tenant-a");
|
||||
json.hash.Should().NotBeNullOrEmpty();
|
||||
json.projection.GetProperty("purl").GetString().Should().Be("pkg:npm/lodash@4.17.21");
|
||||
var metadata = json.projection.GetProperty("metadata");
|
||||
metadata.GetProperty("asset").GetProperty("criticality").GetString().Should().Be("high");
|
||||
}
|
||||
|
||||
private sealed record ProjectionResponse(string snapshotId, string tenantId, string schemaVersion, string hash, System.Text.Json.JsonElement projection);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using StellaOps.SbomService.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class ResolverFeedExportTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
|
||||
public ResolverFeedExportTests(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Export_returns_ndjson_in_deterministic_order()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
// ensure feed populated
|
||||
await client.PostAsync("/internal/sbom/resolver-feed/backfill", null);
|
||||
|
||||
var response = await client.GetAsync("/internal/sbom/resolver-feed/export");
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
response.Content.Headers.ContentType!.MediaType.Should().Be("application/x-ndjson");
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var lines = body.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
lines.Length.Should().BeGreaterThan(0);
|
||||
|
||||
// verify deterministic ordering by first and last line comparison
|
||||
var first = lines.First();
|
||||
var last = lines.Last();
|
||||
first.Should().BeLessOrEqualTo(last, Comparer<string>.Create(StringComparer.Ordinal.Compare));
|
||||
|
||||
// spot-check a known candidate
|
||||
var candidates = await client.GetFromJsonAsync<List<ResolverCandidate>>("/internal/sbom/resolver-feed");
|
||||
candidates.Should().NotBeNull();
|
||||
candidates!.Any(c => c.Purl == "pkg:npm/lodash@4.17.21").Should().BeTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using StellaOps.SbomService.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class SbomAssetEventsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
|
||||
public SbomAssetEventsTests(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Projection_emits_asset_event_once()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var response = await client.GetAsync("/sboms/snap-001/projection?tenant=tenant-a");
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var assetEvents = await client.GetFromJsonAsync<List<SbomAssetUpdatedEvent>>("/internal/sbom/asset-events");
|
||||
assetEvents.Should().NotBeNull();
|
||||
var events = assetEvents!;
|
||||
events.Should().HaveCount(1);
|
||||
|
||||
var evt = events[0];
|
||||
evt.SnapshotId.Should().Be("snap-001");
|
||||
evt.TenantId.Should().Be("tenant-a");
|
||||
evt.Asset.Criticality.Should().Be("high");
|
||||
evt.Asset.Exposure.Should().Contain("internet");
|
||||
evt.Asset.Tags.Should().ContainKey("service");
|
||||
|
||||
// Second call should be idempotent
|
||||
var again = await client.GetAsync("/sboms/snap-001/projection?tenant=tenant-a");
|
||||
again.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var assetEventsAfter = await client.GetFromJsonAsync<List<SbomAssetUpdatedEvent>>("/internal/sbom/asset-events");
|
||||
assetEventsAfter.Should().NotBeNull();
|
||||
assetEventsAfter!.Should().HaveCount(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using StellaOps.SbomService.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class SbomInventoryEventsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
|
||||
public SbomInventoryEventsTests(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Inventory_events_emitted_on_projection()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var projection = await client.GetAsync("/sboms/snap-001/projection?tenant=tenant-a");
|
||||
projection.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var inventory = await client.GetFromJsonAsync<List<SbomInventoryEvidence>>("/internal/sbom/inventory");
|
||||
inventory.Should().NotBeNull();
|
||||
var items = inventory!;
|
||||
items.Should().NotBeEmpty();
|
||||
items.Should().ContainSingle(i => i.Purl == "pkg:npm/lodash@4.17.21" && i.Scope == "runtime");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Inventory_backfill_resets_and_replays()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var pre = await client.GetFromJsonAsync<List<SbomInventoryEvidence>>("/internal/sbom/inventory");
|
||||
pre.Should().NotBeNull();
|
||||
|
||||
var backfill = await client.PostAsync("/internal/sbom/inventory/backfill", null);
|
||||
backfill.EnsureSuccessStatusCode();
|
||||
|
||||
var post = await client.GetFromJsonAsync<List<SbomInventoryEvidence>>("/internal/sbom/inventory");
|
||||
post.Should().NotBeNull();
|
||||
post!.Count.Should().BeGreaterOrEqualTo(pre!.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Resolver_feed_backfill_populates_candidates()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var before = await client.GetFromJsonAsync<List<ResolverCandidate>>("/internal/sbom/resolver-feed");
|
||||
before.Should().NotBeNull();
|
||||
|
||||
var resp = await client.PostAsync("/internal/sbom/resolver-feed/backfill", null);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
|
||||
var feed = await client.GetFromJsonAsync<List<ResolverCandidate>>("/internal/sbom/resolver-feed");
|
||||
feed.Should().NotBeNull();
|
||||
feed!.Should().NotBeEmpty();
|
||||
feed.Should().Contain(c => c.Purl == "pkg:npm/lodash@4.17.21");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user