Refactor code structure for improved readability and maintainability; optimize performance in key functions.
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using StellaOps.Scanner.Storage.ObjectStore;
|
||||
using StellaOps.Scanner.WebService.Contracts;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Scanner.WebService.Tests;
|
||||
|
||||
public sealed class SbomUploadEndpointsTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Upload_accepts_cyclonedx_fixture_and_returns_record()
|
||||
{
|
||||
using var secrets = new TestSurfaceSecretsScope();
|
||||
using var factory = CreateFactory();
|
||||
using var client = factory.CreateClient();
|
||||
|
||||
var request = new SbomUploadRequestDto
|
||||
{
|
||||
ArtifactRef = "example.com/app:1.0",
|
||||
SbomBase64 = LoadFixtureBase64("sample.cdx.json"),
|
||||
Source = new SbomUploadSourceDto
|
||||
{
|
||||
Tool = "syft",
|
||||
Version = "1.0.0",
|
||||
CiContext = new SbomUploadCiContextDto
|
||||
{
|
||||
BuildId = "build-123",
|
||||
Repository = "github.com/example/app"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var response = await client.PostAsJsonAsync("/api/v1/sbom/upload", request);
|
||||
Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<SbomUploadResponseDto>();
|
||||
Assert.NotNull(payload);
|
||||
Assert.Equal("example.com/app:1.0", payload!.ArtifactRef);
|
||||
Assert.Equal("cyclonedx", payload.Format);
|
||||
Assert.Equal("1.6", payload.FormatVersion);
|
||||
Assert.True(payload.ValidationResult.Valid);
|
||||
Assert.False(string.IsNullOrWhiteSpace(payload.AnalysisJobId));
|
||||
|
||||
var recordResponse = await client.GetAsync($"/api/v1/sbom/uploads/{payload.SbomId}");
|
||||
Assert.Equal(HttpStatusCode.OK, recordResponse.StatusCode);
|
||||
|
||||
var record = await recordResponse.Content.ReadFromJsonAsync<SbomUploadRecordDto>();
|
||||
Assert.NotNull(record);
|
||||
Assert.Equal(payload.SbomId, record!.SbomId);
|
||||
Assert.Equal("example.com/app:1.0", record.ArtifactRef);
|
||||
Assert.Equal("syft", record.Source?.Tool);
|
||||
Assert.Equal("build-123", record.Source?.CiContext?.BuildId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Upload_accepts_spdx_fixture_and_reports_quality_score()
|
||||
{
|
||||
using var secrets = new TestSurfaceSecretsScope();
|
||||
using var factory = CreateFactory();
|
||||
using var client = factory.CreateClient();
|
||||
|
||||
var request = new SbomUploadRequestDto
|
||||
{
|
||||
ArtifactRef = "example.com/service:2.0",
|
||||
SbomBase64 = LoadFixtureBase64("sample.spdx.json")
|
||||
};
|
||||
|
||||
var response = await client.PostAsJsonAsync("/api/v1/sbom/upload", request);
|
||||
Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<SbomUploadResponseDto>();
|
||||
Assert.NotNull(payload);
|
||||
Assert.Equal("spdx", payload!.Format);
|
||||
Assert.Equal("2.3", payload.FormatVersion);
|
||||
Assert.True(payload.ValidationResult.Valid);
|
||||
Assert.True(payload.ValidationResult.QualityScore > 0);
|
||||
Assert.True(payload.ValidationResult.ComponentCount > 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Upload_rejects_unknown_format()
|
||||
{
|
||||
using var secrets = new TestSurfaceSecretsScope();
|
||||
using var factory = CreateFactory();
|
||||
using var client = factory.CreateClient();
|
||||
|
||||
var invalid = new SbomUploadRequestDto
|
||||
{
|
||||
ArtifactRef = "example.com/invalid:1.0",
|
||||
SbomBase64 = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("{\"name\":\"oops\"}"))
|
||||
};
|
||||
|
||||
var response = await client.PostAsJsonAsync("/api/v1/sbom/upload", invalid);
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
private static ScannerApplicationFactory CreateFactory()
|
||||
{
|
||||
return new ScannerApplicationFactory().WithOverrides(configuration =>
|
||||
{
|
||||
configuration["scanner:authority:enabled"] = "false";
|
||||
}, configureServices: services =>
|
||||
{
|
||||
services.RemoveAll<IArtifactObjectStore>();
|
||||
services.AddSingleton<IArtifactObjectStore>(new InMemoryArtifactObjectStore());
|
||||
});
|
||||
}
|
||||
|
||||
private static string LoadFixtureBase64(string fileName)
|
||||
{
|
||||
var baseDirectory = AppContext.BaseDirectory;
|
||||
var repoRoot = Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", "..", "..", ".."));
|
||||
var path = Path.Combine(
|
||||
repoRoot,
|
||||
"tests",
|
||||
"AirGap",
|
||||
"StellaOps.AirGap.Importer.Tests",
|
||||
"Reconciliation",
|
||||
"Fixtures",
|
||||
fileName);
|
||||
|
||||
Assert.True(File.Exists(path), $"Fixture not found at {path}.");
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
return Convert.ToBase64String(bytes);
|
||||
}
|
||||
|
||||
private sealed class InMemoryArtifactObjectStore : IArtifactObjectStore
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, byte[]> _objects = new(StringComparer.Ordinal);
|
||||
|
||||
public async Task PutAsync(ArtifactObjectDescriptor descriptor, Stream content, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(descriptor);
|
||||
ArgumentNullException.ThrowIfNull(content);
|
||||
|
||||
using var buffer = new MemoryStream();
|
||||
await content.CopyToAsync(buffer, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var key = $"{descriptor.Bucket}:{descriptor.Key}";
|
||||
_objects[key] = buffer.ToArray();
|
||||
}
|
||||
|
||||
public Task<Stream?> GetAsync(ArtifactObjectDescriptor descriptor, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(descriptor);
|
||||
|
||||
var key = $"{descriptor.Bucket}:{descriptor.Key}";
|
||||
if (!_objects.TryGetValue(key, out var bytes))
|
||||
{
|
||||
return Task.FromResult<Stream?>(null);
|
||||
}
|
||||
|
||||
return Task.FromResult<Stream?>(new MemoryStream(bytes, writable: false));
|
||||
}
|
||||
|
||||
public Task DeleteAsync(ArtifactObjectDescriptor descriptor, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(descriptor);
|
||||
var key = $"{descriptor.Bucket}:{descriptor.Key}";
|
||||
_objects.TryRemove(key, out _);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user