Repair release investigation workspace contracts
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using StellaOps.SbomService.Models;
|
||||
using StellaOps.SbomService.Services;
|
||||
using StellaOps.TestKit;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public sealed class ChangeTraceCompatibilityEndpointsTests : IClassFixture<WebApplicationFactory<StellaOps.SbomService.Program>>
|
||||
{
|
||||
private const string TenantId = "github.com/acme/change-trace";
|
||||
|
||||
private readonly WebApplicationFactory<StellaOps.SbomService.Program> _factory;
|
||||
|
||||
public ChangeTraceCompatibilityEndpointsTests(WebApplicationFactory<StellaOps.SbomService.Program> factory)
|
||||
{
|
||||
_factory = factory.WithWebHostBuilder(_ => { });
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Build_endpoint_returns_deterministic_change_trace_for_uploaded_artifacts()
|
||||
{
|
||||
var client = CreateAuthenticatedClient();
|
||||
var firstUpload = await UploadAsync(client, "1.0.0");
|
||||
var secondUpload = await UploadAsync(client, "1.1.0");
|
||||
|
||||
var response = await client.PostAsJsonAsync("/api/change-traces/build", new ChangeTraceBuildRequest
|
||||
{
|
||||
TenantId = TenantId,
|
||||
FromDigest = firstUpload.Digest,
|
||||
ToDigest = secondUpload.Digest,
|
||||
IncludeByteDiff = false,
|
||||
});
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
var payload = await response.Content.ReadFromJsonAsync<ChangeTraceDocument>();
|
||||
|
||||
payload.Should().NotBeNull();
|
||||
payload!.TraceId.Should().NotBeNullOrWhiteSpace();
|
||||
payload.Subject.FromDigest.Should().Be(firstUpload.Digest);
|
||||
payload.Subject.ToDigest.Should().Be(secondUpload.Digest);
|
||||
payload.Deltas.Should().ContainSingle();
|
||||
payload.Deltas[0].ChangeType.Should().BeOneOf("upgraded", "patched");
|
||||
payload.Summary.ChangedPackages.Should().Be(1);
|
||||
payload.Commitment.Should().NotBeNull();
|
||||
payload.Commitment!.Sha256.Should().StartWith("sha256:");
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Get_endpoint_rehydrates_trace_from_trace_id()
|
||||
{
|
||||
var client = CreateAuthenticatedClient();
|
||||
var firstUpload = await UploadAsync(client, "2.0.0");
|
||||
var secondUpload = await UploadAsync(client, "2.0.1");
|
||||
|
||||
var buildResponse = await client.PostAsJsonAsync("/api/change-traces/build", new ChangeTraceBuildRequest
|
||||
{
|
||||
TenantId = TenantId,
|
||||
FromDigest = firstUpload.Digest,
|
||||
ToDigest = secondUpload.Digest,
|
||||
IncludeByteDiff = false,
|
||||
});
|
||||
buildResponse.EnsureSuccessStatusCode();
|
||||
|
||||
var builtTrace = await buildResponse.Content.ReadFromJsonAsync<ChangeTraceDocument>();
|
||||
builtTrace.Should().NotBeNull();
|
||||
|
||||
var getResponse = await client.GetAsync($"/api/change-traces/{Uri.EscapeDataString(builtTrace!.TraceId)}");
|
||||
getResponse.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var rehydratedTrace = await getResponse.Content.ReadFromJsonAsync<ChangeTraceDocument>();
|
||||
rehydratedTrace.Should().NotBeNull();
|
||||
rehydratedTrace!.TraceId.Should().Be(builtTrace.TraceId);
|
||||
rehydratedTrace.Subject.FromDigest.Should().Be(firstUpload.Digest);
|
||||
rehydratedTrace.Subject.ToDigest.Should().Be(secondUpload.Digest);
|
||||
rehydratedTrace.Summary.ChangedPackages.Should().Be(1);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task Get_endpoint_rejects_invalid_trace_id()
|
||||
{
|
||||
var client = CreateAuthenticatedClient();
|
||||
|
||||
var response = await client.GetAsync("/api/change-traces/not-a-valid-trace-id");
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
var payload = await response.Content.ReadFromJsonAsync<JsonElement>();
|
||||
payload.GetProperty("error").GetString().Should().Be("invalid traceId");
|
||||
}
|
||||
|
||||
private async Task<SbomUploadResponse> UploadAsync(HttpClient client, string version)
|
||||
{
|
||||
var response = await client.PostAsJsonAsync(
|
||||
"/sbom/upload",
|
||||
new SbomUploadRequest
|
||||
{
|
||||
ArtifactRef = "acme/change-trace:demo",
|
||||
Sbom = JsonDocument.Parse($$"""
|
||||
{
|
||||
"spdxVersion": "SPDX-2.3",
|
||||
"SPDXID": "SPDXRef-DOCUMENT",
|
||||
"name": "sample",
|
||||
"dataLicense": "CC0-1.0",
|
||||
"packages": [
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-lodash",
|
||||
"name": "lodash",
|
||||
"versionInfo": "{{version}}",
|
||||
"licenseDeclared": "MIT",
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:npm/lodash@{{version}}",
|
||||
"referenceCategory": "PACKAGE-MANAGER"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""").RootElement.Clone(),
|
||||
Source = new SbomUploadSource
|
||||
{
|
||||
Tool = "syft",
|
||||
Version = "1.0.0",
|
||||
CiContext = new SbomUploadCiContext
|
||||
{
|
||||
BuildId = $"build-{version}",
|
||||
Repository = TenantId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
var payload = await response.Content.ReadFromJsonAsync<SbomUploadResponse>();
|
||||
payload.Should().NotBeNull();
|
||||
return payload!;
|
||||
}
|
||||
|
||||
private HttpClient CreateAuthenticatedClient()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("X-Tenant-Id", TenantId);
|
||||
client.DefaultRequestHeaders.Add("X-User-Id", "change-trace-test");
|
||||
return client;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user