Files
git.stella-ops.org/src/SbomService/StellaOps.SbomService.Tests/ProjectionEndpointTests.cs
2025-11-24 00:34:20 +02:00

105 lines
3.8 KiB
C#

using System.Net;
using System.Net.Http.Json;
using System.Reflection;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using StellaOps.SbomService.Repositories;
using Xunit;
namespace StellaOps.SbomService.Tests;
public class ProjectionEndpointTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public ProjectionEndpointTests(WebApplicationFactory<Program> factory)
{
var contentRoot = ResolveContentRoot();
_factory = factory.WithWebHostBuilder(builder =>
{
var fixturePath = GetProjectionFixturePath();
if (!File.Exists(fixturePath))
{
throw new InvalidOperationException($"Projection fixture missing at {fixturePath}");
}
builder.ConfigureAppConfiguration((_, config) =>
{
config.AddInMemoryCollection(new Dictionary<string, string?>
{
["SbomService:ProjectionsPath"] = fixturePath
});
});
builder.ConfigureServices(services =>
{
// Avoid MongoDB dependency in tests; use seeded in-memory repo.
services.RemoveAll<IComponentLookupRepository>();
services.AddSingleton<IComponentLookupRepository, InMemoryComponentLookupRepository>();
});
builder.UseSetting(WebHostDefaults.ContentRootKey, contentRoot);
});
}
[Fact]
public async Task Projection_requires_tenant()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("/sboms/snap-001/projection");
if (response.StatusCode != HttpStatusCode.BadRequest)
{
var body = await response.Content.ReadAsStringAsync();
throw new Xunit.Sdk.XunitException($"Expected 400 but got {(int)response.StatusCode}: {response.StatusCode}. Body: {body}");
}
}
[Fact]
public async Task Projection_returns_payload_and_hash()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("/sboms/snap-001/projection?tenant=tenant-a");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadFromJsonAsync<ProjectionResponse>();
json.Should().NotBeNull();
json!.snapshotId.Should().Be("snap-001");
json.tenantId.Should().Be("tenant-a");
json.hash.Should().NotBeNullOrEmpty();
json.projection.GetProperty("purl").GetString().Should().Be("pkg:npm/lodash@4.17.21");
}
private sealed record ProjectionResponse(string snapshotId, string tenantId, string schemaVersion, string hash, System.Text.Json.JsonElement projection);
private static string GetProjectionFixturePath()
{
// Resolve docs/modules/sbomservice/fixtures/lnm-v1/projections.json relative to test bin directory.
var baseDir = ResolveContentRoot();
return Path.Combine(baseDir, "docs", "modules", "sbomservice", "fixtures", "lnm-v1", "projections.json");
}
private static string ResolveContentRoot()
{
// Walk up from bin folder to repo root (containing docs/).
var dir = AppContext.BaseDirectory;
for (var i = 0; i < 6; i++)
{
var candidate = Path.GetFullPath(Path.Combine(dir, ".."));
if (Directory.Exists(Path.Combine(candidate, "docs")) &&
Directory.Exists(Path.Combine(candidate, "src")))
{
return candidate;
}
dir = candidate;
}
return AppContext.BaseDirectory;
}
}