T8: Fix PostgresExceptionApplicationRepositoryTests to use NpgsqlDataSource

This commit is contained in:
StellaOps Bot
2025-12-21 09:47:30 +02:00
parent 8a4edee665
commit a216d7eea4

View File

@@ -1,95 +1,168 @@
// <copyright file="PostgresExceptionApplicationRepositoryTests.cs" company="StellaOps">
// Copyright (c) StellaOps. All rights reserved.
// Licensed under the AGPL-3.0-or-later license.
// </copyright>
using System.Collections.Immutable;
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Npgsql;
using StellaOps.Policy.Exceptions.Models;
using StellaOps.Policy.Exceptions.Repositories;
using Xunit;
namespace StellaOps.Policy.Storage.Postgres.Tests;
/// <summary>
/// Integration tests for PostgresExceptionApplicationRepository.
/// Tests exception application audit trail persistence.
/// </summary>
[Collection(PolicyPostgresCollection.Name)]
public sealed class PostgresExceptionApplicationRepositoryTests : IAsyncLifetime
{
private readonly PolicyPostgresFixture _fixture;
private readonly PostgresExceptionApplicationRepository _repository;
private readonly NpgsqlDataSource _dataSource;
private readonly Guid _tenantId = Guid.NewGuid();
public PostgresExceptionApplicationRepositoryTests(PolicyPostgresFixture fixture)
{
_fixture = fixture;
var options = fixture.Fixture.CreateOptions();
options.SchemaName = fixture.SchemaName;
var dataSource = new PolicyDataSource(Options.Create(options), NullLogger<PolicyDataSource>.Instance);
_repository = new PostgresExceptionApplicationRepository(dataSource.DataSource);
// Create NpgsqlDataSource from connection string
var builder = new NpgsqlDataSourceBuilder(fixture.ConnectionString);
_dataSource = builder.Build();
_repository = new PostgresExceptionApplicationRepository(_dataSource);
}
public Task InitializeAsync() => _fixture.TruncateAllTablesAsync();
public Task DisposeAsync() => Task.CompletedTask;
public async Task InitializeAsync()
{
await _fixture.TruncateAllTablesAsync();
// Set search path to include the test schema
await using var conn = await _dataSource.OpenConnectionAsync();
await using var cmd = new NpgsqlCommand($"SET search_path TO {_fixture.SchemaName}, public;", conn);
await cmd.ExecuteNonQueryAsync();
}
public async Task DisposeAsync()
{
await _dataSource.DisposeAsync();
}
[Fact]
public async Task RecordAsync_ShouldPersist()
{
// Arrange
var app = CreateApp("EXC-001", "FIND-001");
// Act
var result = await _repository.RecordAsync(app);
// Assert
result.Should().NotBeNull();
result.ExceptionId.Should().Be("EXC-001");
result.FindingId.Should().Be("FIND-001");
}
[Fact]
public async Task RecordBatchAsync_ShouldPersistMultiple()
{
var apps = new[] { CreateApp("EXC-B1", "F1"), CreateApp("EXC-B1", "F2"), CreateApp("EXC-B2", "F3") };
// Arrange
var apps = new[]
{
CreateApp("EXC-B1", "F1"),
CreateApp("EXC-B1", "F2"),
CreateApp("EXC-B2", "F3"),
};
// Act
var result = await _repository.RecordBatchAsync(apps);
// Assert
result.Should().HaveCount(3);
}
[Fact]
public async Task RecordBatchAsync_EmptyReturnsEmpty()
{
// Act
var result = await _repository.RecordBatchAsync(Array.Empty<ExceptionApplication>());
// Assert
result.Should().BeEmpty();
}
[Fact]
public async Task GetByExceptionIdAsync_ReturnsMatches()
{
// Arrange
await _repository.RecordAsync(CreateApp("EXC-G1", "F1"));
await _repository.RecordAsync(CreateApp("EXC-G1", "F2"));
await _repository.RecordAsync(CreateApp("EXC-OTH", "F3"));
// Act
var results = await _repository.GetByExceptionIdAsync(_tenantId, "EXC-G1");
// Assert
results.Should().HaveCount(2);
results.Should().AllSatisfy(r => r.ExceptionId.Should().Be("EXC-G1"));
}
[Fact]
public async Task GetByFindingIdAsync_ReturnsMatches()
{
// Arrange
await _repository.RecordAsync(CreateApp("E1", "FIND-G1"));
await _repository.RecordAsync(CreateApp("E2", "FIND-G1"));
await _repository.RecordAsync(CreateApp("E3", "FIND-OTH"));
// Act
var results = await _repository.GetByFindingIdAsync(_tenantId, "FIND-G1");
// Assert
results.Should().HaveCount(2);
results.Should().AllSatisfy(r => r.FindingId.Should().Be("FIND-G1"));
}
[Fact]
public async Task GetByVulnerabilityIdAsync_ReturnsMatches()
{
// Arrange
await _repository.RecordAsync(CreateApp("E1", "F1", "CVE-2024-1234"));
await _repository.RecordAsync(CreateApp("E2", "F2", "CVE-2024-1234"));
await _repository.RecordAsync(CreateApp("E3", "F3", "CVE-2024-5678"));
// Act
var results = await _repository.GetByVulnerabilityIdAsync(_tenantId, "CVE-2024-1234");
// Assert
results.Should().HaveCount(2);
results.Should().AllSatisfy(r => r.VulnerabilityId.Should().Be("CVE-2024-1234"));
}
[Fact]
public async Task CountAsync_WithFilter_ReturnsFiltered()
{
await _repository.RecordBatchAsync(new[] { CreateApp("E1", "F1", eff: "suppress"), CreateApp("E2", "F2", eff: "suppress"), CreateApp("E3", "F3", eff: "modify") });
// Arrange
await _repository.RecordBatchAsync(new[]
{
CreateApp("E1", "F1", eff: "suppress"),
CreateApp("E2", "F2", eff: "suppress"),
CreateApp("E3", "F3", eff: "modify"),
});
var filter = new ExceptionApplicationFilter(EffectType: "suppress");
// Act
var count = await _repository.CountAsync(_tenantId, filter);
// Assert
count.Should().Be(2);
}
private ExceptionApplication CreateApp(string excId, string findId, string? vulnId = null, string eff = "suppress") =>
private ExceptionApplication CreateApp(
string excId,
string findId,
string? vulnId = null,
string eff = "suppress") =>
ExceptionApplication.Create(_tenantId, excId, findId, "affected", "not_affected", "test", eff, vulnId);
}