189 lines
6.8 KiB
C#
189 lines
6.8 KiB
C#
// -----------------------------------------------------------------------------
|
|
// EvidenceCompositionServiceTests.cs
|
|
// Sprint: SPRINT_3800_0003_0001_evidence_api_endpoint
|
|
// Description: Integration tests for Evidence API endpoints.
|
|
// -----------------------------------------------------------------------------
|
|
|
|
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using StellaOps.Scanner.WebService.Contracts;
|
|
using StellaOps.Scanner.WebService.Endpoints;
|
|
using Xunit;
|
|
using FluentAssertions;
|
|
|
|
|
|
using StellaOps.TestKit;
|
|
namespace StellaOps.Scanner.WebService.Tests;
|
|
|
|
public sealed class EvidenceEndpointsTests
|
|
{
|
|
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task GetEvidence_ReturnsBadRequest_WhenScanIdInvalid()
|
|
{
|
|
using var secrets = new TestSurfaceSecretsScope();
|
|
using var factory = new ScannerApplicationFactory().WithOverrides(configuration =>
|
|
{
|
|
configuration["scanner:authority:enabled"] = "false";
|
|
});
|
|
using var client = factory.CreateClient();
|
|
|
|
// Empty scan ID - route doesn't match
|
|
var response = await client.GetAsync("/api/v1/scans//evidence/CVE-2024-12345@pkg:npm/lodash@4.17.0");
|
|
|
|
response.StatusCode.Should().Be(HttpStatusCode.NotFound); // Route doesn't match
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task GetEvidence_ReturnsNotFound_WhenScanDoesNotExist()
|
|
{
|
|
using var secrets = new TestSurfaceSecretsScope();
|
|
using var factory = new ScannerApplicationFactory().WithOverrides(configuration =>
|
|
{
|
|
configuration["scanner:authority:enabled"] = "false";
|
|
});
|
|
using var client = factory.CreateClient();
|
|
|
|
var response = await client.GetAsync(
|
|
"/api/v1/scans/nonexistent-scan-id/evidence/CVE-2024-12345@pkg:npm/lodash@4.17.0");
|
|
|
|
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task GetEvidence_ReturnsListEndpoint_WhenFindingIdEmpty()
|
|
{
|
|
// When no finding ID is provided, the route matches the list endpoint
|
|
using var secrets = new TestSurfaceSecretsScope();
|
|
using var factory = new ScannerApplicationFactory().WithOverrides(configuration =>
|
|
{
|
|
configuration["scanner:authority:enabled"] = "false";
|
|
});
|
|
using var client = factory.CreateClient();
|
|
|
|
// Create a scan first
|
|
var scanId = await CreateScanAsync(client);
|
|
|
|
// Empty finding ID - route matches list endpoint
|
|
var response = await client.GetAsync($"/api/v1/scans/{scanId}/evidence");
|
|
|
|
// Should return 200 OK with empty list (falls through to list endpoint)
|
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task ListEvidence_ReturnsEmptyList_WhenNoFindings()
|
|
{
|
|
using var secrets = new TestSurfaceSecretsScope();
|
|
using var factory = new ScannerApplicationFactory().WithOverrides(configuration =>
|
|
{
|
|
configuration["scanner:authority:enabled"] = "false";
|
|
});
|
|
using var client = factory.CreateClient();
|
|
|
|
var scanId = await CreateScanAsync(client);
|
|
|
|
var response = await client.GetAsync($"/api/v1/scans/{scanId}/evidence");
|
|
|
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<EvidenceListResponse>(SerializerOptions);
|
|
result.Should().NotBeNull();
|
|
result!.TotalCount.Should().Be(0);
|
|
result.Items.Should().BeEmpty();
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task ListEvidence_ReturnsEmptyList_WhenScanDoesNotExist()
|
|
{
|
|
// The current implementation returns empty list for non-existent scans
|
|
// because the reachability service returns empty findings for unknown scans
|
|
using var secrets = new TestSurfaceSecretsScope();
|
|
using var factory = new ScannerApplicationFactory().WithOverrides(configuration =>
|
|
{
|
|
configuration["scanner:authority:enabled"] = "false";
|
|
});
|
|
using var client = factory.CreateClient();
|
|
|
|
using StellaOps.TestKit;
|
|
var response = await client.GetAsync("/api/v1/scans/nonexistent-scan/evidence");
|
|
|
|
// Current behavior: returns empty list (200 OK) for non-existent scans
|
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
var result = await response.Content.ReadFromJsonAsync<EvidenceListResponse>(SerializerOptions);
|
|
result.Should().NotBeNull();
|
|
result!.TotalCount.Should().Be(0);
|
|
}
|
|
|
|
private static async Task<string> CreateScanAsync(HttpClient client)
|
|
{
|
|
var createRequest = new ScanSubmitRequest
|
|
{
|
|
Image = new ScanImageDescriptor { Reference = "example.com/test:latest" }
|
|
};
|
|
|
|
var createResponse = await client.PostAsJsonAsync("/api/v1/scans", createRequest);
|
|
createResponse.EnsureSuccessStatusCode();
|
|
|
|
var createResult = await createResponse.Content.ReadFromJsonAsync<JsonElement>();
|
|
return createResult.GetProperty("scanId").GetString()!;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests for Evidence TTL and staleness handling (SPRINT_3800_0003_0002).
|
|
/// </summary>
|
|
public sealed class EvidenceTtlTests
|
|
{
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public void DefaultEvidenceTtlDays_DefaultsToSevenDays()
|
|
{
|
|
// Verify the default configuration
|
|
var options = new StellaOps.Scanner.WebService.Services.EvidenceCompositionOptions();
|
|
|
|
options.DefaultEvidenceTtlDays.Should().Be(7);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public void VexEvidenceTtlDays_DefaultsToThirtyDays()
|
|
{
|
|
var options = new StellaOps.Scanner.WebService.Services.EvidenceCompositionOptions();
|
|
|
|
options.VexEvidenceTtlDays.Should().Be(30);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public void StaleWarningThresholdDays_DefaultsToOne()
|
|
{
|
|
var options = new StellaOps.Scanner.WebService.Services.EvidenceCompositionOptions();
|
|
|
|
options.StaleWarningThresholdDays.Should().Be(1);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public void EvidenceCompositionOptions_CanBeConfigured()
|
|
{
|
|
var options = new StellaOps.Scanner.WebService.Services.EvidenceCompositionOptions
|
|
{
|
|
DefaultEvidenceTtlDays = 14,
|
|
VexEvidenceTtlDays = 60,
|
|
StaleWarningThresholdDays = 2
|
|
};
|
|
|
|
options.DefaultEvidenceTtlDays.Should().Be(14);
|
|
options.VexEvidenceTtlDays.Should().Be(60);
|
|
options.StaleWarningThresholdDays.Should().Be(2);
|
|
}
|
|
}
|