Files
git.stella-ops.org/src/BinaryIndex/__Tests/StellaOps.BinaryIndex.GroundTruth.SecDb.Tests/SecDbConnectorIntegrationTests.cs

169 lines
5.0 KiB
C#

using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StellaOps.BinaryIndex.GroundTruth.SecDb.Configuration;
using StellaOps.BinaryIndex.GroundTruth.SecDb.Tests.Fixtures;
using Xunit;
namespace StellaOps.BinaryIndex.GroundTruth.SecDb.Tests;
/// <summary>
/// Integration tests for SecDb connector.
/// These tests require network access to gitlab.alpinelinux.org.
/// Skip in CI by setting SKIP_INTEGRATION_TESTS=true.
/// </summary>
[Trait("Category", "Integration")]
public class SecDbConnectorIntegrationTests : IAsyncLifetime
{
private ServiceProvider? _services;
private readonly bool _skipTests;
public SecDbConnectorIntegrationTests()
{
_skipTests = Environment.GetEnvironmentVariable("SKIP_INTEGRATION_TESTS")?.ToLowerInvariant() == "true"
|| Environment.GetEnvironmentVariable("CI")?.ToLowerInvariant() == "true";
}
public ValueTask InitializeAsync()
{
if (_skipTests)
return ValueTask.CompletedTask;
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug));
services.AddSecDbConnector(opts =>
{
opts.Branches = ["v3.19"];
opts.Repositories = ["main"];
opts.TimeoutSeconds = 120;
opts.FetchAports = false; // Don't fetch aports for integration tests
});
_services = services.BuildServiceProvider();
return ValueTask.CompletedTask;
}
public ValueTask DisposeAsync()
{
_services?.Dispose();
return ValueTask.CompletedTask;
}
[Fact]
[Trait("Category", "NetworkIntegration")]
public async Task SecDbConnector_CanTestConnectivity()
{
Skip.If(_skipTests, "Integration tests skipped");
// Arrange
var connector = _services!.GetRequiredService<SecDbConnector>();
// Act
try
{
var result = await connector.TestConnectivityAsync();
// Assert - only if network is available
result.IsConnected.Should().BeTrue("Should be able to connect to Alpine GitLab");
result.Latency.Should().BeLessThan(TimeSpan.FromSeconds(30));
}
catch (HttpRequestException)
{
// Network unavailable - skip test
Skip.If(true, "Network unavailable");
}
}
[Fact]
public async Task SecDbConnector_CanGetMetadata()
{
Skip.If(_skipTests, "Integration tests skipped");
// Arrange
var connector = _services!.GetRequiredService<SecDbConnector>();
// Act
var metadata = await connector.GetMetadataAsync();
// Assert
metadata.SourceId.Should().Be("secdb-alpine");
metadata.DisplayName.Should().Contain("Alpine");
metadata.BaseUrl.Should().Contain("gitlab.alpinelinux.org");
}
[Fact]
public void SecDbConnector_HasCorrectProperties()
{
Skip.If(_skipTests, "Integration tests skipped");
// Arrange
var connector = _services!.GetRequiredService<SecDbConnector>();
// Assert
connector.SourceId.Should().Be("secdb-alpine");
connector.DisplayName.Should().Contain("SecDB");
connector.SupportedDistros.Should().Contain("alpine");
}
[Fact]
[Trait("Category", "NetworkIntegration")]
public async Task SecDbConnector_FetchAndGetVulnerabilities_ReturnsData()
{
Skip.If(_skipTests, "Integration tests skipped");
// Arrange
var connector = _services!.GetRequiredService<SecDbConnector>();
try
{
// First fetch the data
await connector.FetchAsync(_services!, CancellationToken.None);
// Act - get vulnerabilities for a well-known package
var vulnerabilities = await connector.GetVulnerabilitiesForPackageAsync("curl");
// Assert
vulnerabilities.Should().NotBeEmpty("curl should have known vulnerabilities");
vulnerabilities.Should().OnlyContain(v => v.CveId.StartsWith("CVE-"));
}
catch (HttpRequestException)
{
// Network unavailable - skip test
Skip.If(true, "Network unavailable");
}
}
}
/// <summary>
/// Provides Skip functionality for xUnit when condition is true.
/// </summary>
public static class Skip
{
public static void If(bool condition, string reason)
{
if (condition)
{
throw new SkipException(reason);
}
}
}
/// <summary>
/// Exception to skip a test.
/// </summary>
public class SkipException : Exception
{
public SkipException(string reason) : base(reason) { }
}
/// <summary>
/// Test meter factory for diagnostics.
/// </summary>
internal sealed class TestMeterFactory : System.Diagnostics.Metrics.IMeterFactory
{
public System.Diagnostics.Metrics.Meter Create(System.Diagnostics.Metrics.MeterOptions options)
=> new(options.Name, options.Version);
public void Dispose() { }
}