T8: Fix PostgresExceptionApplicationRepositoryTests to use NpgsqlDataSource
This commit is contained in:
@@ -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 System.Collections.Immutable;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Npgsql;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using StellaOps.Policy.Exceptions.Models;
|
using StellaOps.Policy.Exceptions.Models;
|
||||||
using StellaOps.Policy.Exceptions.Repositories;
|
using StellaOps.Policy.Exceptions.Repositories;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace StellaOps.Policy.Storage.Postgres.Tests;
|
namespace StellaOps.Policy.Storage.Postgres.Tests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Integration tests for PostgresExceptionApplicationRepository.
|
||||||
|
/// Tests exception application audit trail persistence.
|
||||||
|
/// </summary>
|
||||||
[Collection(PolicyPostgresCollection.Name)]
|
[Collection(PolicyPostgresCollection.Name)]
|
||||||
public sealed class PostgresExceptionApplicationRepositoryTests : IAsyncLifetime
|
public sealed class PostgresExceptionApplicationRepositoryTests : IAsyncLifetime
|
||||||
{
|
{
|
||||||
private readonly PolicyPostgresFixture _fixture;
|
private readonly PolicyPostgresFixture _fixture;
|
||||||
private readonly PostgresExceptionApplicationRepository _repository;
|
private readonly PostgresExceptionApplicationRepository _repository;
|
||||||
|
private readonly NpgsqlDataSource _dataSource;
|
||||||
private readonly Guid _tenantId = Guid.NewGuid();
|
private readonly Guid _tenantId = Guid.NewGuid();
|
||||||
|
|
||||||
public PostgresExceptionApplicationRepositoryTests(PolicyPostgresFixture fixture)
|
public PostgresExceptionApplicationRepositoryTests(PolicyPostgresFixture fixture)
|
||||||
{
|
{
|
||||||
_fixture = fixture;
|
_fixture = fixture;
|
||||||
var options = fixture.Fixture.CreateOptions();
|
|
||||||
options.SchemaName = fixture.SchemaName;
|
// Create NpgsqlDataSource from connection string
|
||||||
var dataSource = new PolicyDataSource(Options.Create(options), NullLogger<PolicyDataSource>.Instance);
|
var builder = new NpgsqlDataSourceBuilder(fixture.ConnectionString);
|
||||||
_repository = new PostgresExceptionApplicationRepository(dataSource.DataSource);
|
_dataSource = builder.Build();
|
||||||
|
_repository = new PostgresExceptionApplicationRepository(_dataSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task InitializeAsync() => _fixture.TruncateAllTablesAsync();
|
public async Task InitializeAsync()
|
||||||
public Task DisposeAsync() => Task.CompletedTask;
|
{
|
||||||
|
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]
|
[Fact]
|
||||||
public async Task RecordAsync_ShouldPersist()
|
public async Task RecordAsync_ShouldPersist()
|
||||||
{
|
{
|
||||||
|
// Arrange
|
||||||
var app = CreateApp("EXC-001", "FIND-001");
|
var app = CreateApp("EXC-001", "FIND-001");
|
||||||
|
|
||||||
|
// Act
|
||||||
var result = await _repository.RecordAsync(app);
|
var result = await _repository.RecordAsync(app);
|
||||||
|
|
||||||
|
// Assert
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
result.ExceptionId.Should().Be("EXC-001");
|
result.ExceptionId.Should().Be("EXC-001");
|
||||||
|
result.FindingId.Should().Be("FIND-001");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RecordBatchAsync_ShouldPersistMultiple()
|
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);
|
var result = await _repository.RecordBatchAsync(apps);
|
||||||
|
|
||||||
|
// Assert
|
||||||
result.Should().HaveCount(3);
|
result.Should().HaveCount(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RecordBatchAsync_EmptyReturnsEmpty()
|
public async Task RecordBatchAsync_EmptyReturnsEmpty()
|
||||||
{
|
{
|
||||||
|
// Act
|
||||||
var result = await _repository.RecordBatchAsync(Array.Empty<ExceptionApplication>());
|
var result = await _repository.RecordBatchAsync(Array.Empty<ExceptionApplication>());
|
||||||
|
|
||||||
|
// Assert
|
||||||
result.Should().BeEmpty();
|
result.Should().BeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetByExceptionIdAsync_ReturnsMatches()
|
public async Task GetByExceptionIdAsync_ReturnsMatches()
|
||||||
{
|
{
|
||||||
|
// Arrange
|
||||||
await _repository.RecordAsync(CreateApp("EXC-G1", "F1"));
|
await _repository.RecordAsync(CreateApp("EXC-G1", "F1"));
|
||||||
await _repository.RecordAsync(CreateApp("EXC-G1", "F2"));
|
await _repository.RecordAsync(CreateApp("EXC-G1", "F2"));
|
||||||
await _repository.RecordAsync(CreateApp("EXC-OTH", "F3"));
|
await _repository.RecordAsync(CreateApp("EXC-OTH", "F3"));
|
||||||
|
|
||||||
|
// Act
|
||||||
var results = await _repository.GetByExceptionIdAsync(_tenantId, "EXC-G1");
|
var results = await _repository.GetByExceptionIdAsync(_tenantId, "EXC-G1");
|
||||||
|
|
||||||
|
// Assert
|
||||||
results.Should().HaveCount(2);
|
results.Should().HaveCount(2);
|
||||||
|
results.Should().AllSatisfy(r => r.ExceptionId.Should().Be("EXC-G1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetByFindingIdAsync_ReturnsMatches()
|
public async Task GetByFindingIdAsync_ReturnsMatches()
|
||||||
{
|
{
|
||||||
|
// Arrange
|
||||||
await _repository.RecordAsync(CreateApp("E1", "FIND-G1"));
|
await _repository.RecordAsync(CreateApp("E1", "FIND-G1"));
|
||||||
await _repository.RecordAsync(CreateApp("E2", "FIND-G1"));
|
await _repository.RecordAsync(CreateApp("E2", "FIND-G1"));
|
||||||
await _repository.RecordAsync(CreateApp("E3", "FIND-OTH"));
|
await _repository.RecordAsync(CreateApp("E3", "FIND-OTH"));
|
||||||
|
|
||||||
|
// Act
|
||||||
var results = await _repository.GetByFindingIdAsync(_tenantId, "FIND-G1");
|
var results = await _repository.GetByFindingIdAsync(_tenantId, "FIND-G1");
|
||||||
|
|
||||||
|
// Assert
|
||||||
results.Should().HaveCount(2);
|
results.Should().HaveCount(2);
|
||||||
|
results.Should().AllSatisfy(r => r.FindingId.Should().Be("FIND-G1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetByVulnerabilityIdAsync_ReturnsMatches()
|
public async Task GetByVulnerabilityIdAsync_ReturnsMatches()
|
||||||
{
|
{
|
||||||
|
// Arrange
|
||||||
await _repository.RecordAsync(CreateApp("E1", "F1", "CVE-2024-1234"));
|
await _repository.RecordAsync(CreateApp("E1", "F1", "CVE-2024-1234"));
|
||||||
await _repository.RecordAsync(CreateApp("E2", "F2", "CVE-2024-1234"));
|
await _repository.RecordAsync(CreateApp("E2", "F2", "CVE-2024-1234"));
|
||||||
await _repository.RecordAsync(CreateApp("E3", "F3", "CVE-2024-5678"));
|
await _repository.RecordAsync(CreateApp("E3", "F3", "CVE-2024-5678"));
|
||||||
|
|
||||||
|
// Act
|
||||||
var results = await _repository.GetByVulnerabilityIdAsync(_tenantId, "CVE-2024-1234");
|
var results = await _repository.GetByVulnerabilityIdAsync(_tenantId, "CVE-2024-1234");
|
||||||
|
|
||||||
|
// Assert
|
||||||
results.Should().HaveCount(2);
|
results.Should().HaveCount(2);
|
||||||
|
results.Should().AllSatisfy(r => r.VulnerabilityId.Should().Be("CVE-2024-1234"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CountAsync_WithFilter_ReturnsFiltered()
|
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");
|
var filter = new ExceptionApplicationFilter(EffectType: "suppress");
|
||||||
|
|
||||||
|
// Act
|
||||||
var count = await _repository.CountAsync(_tenantId, filter);
|
var count = await _repository.CountAsync(_tenantId, filter);
|
||||||
|
|
||||||
|
// Assert
|
||||||
count.Should().Be(2);
|
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);
|
ExceptionApplication.Create(_tenantId, excId, findId, "affected", "not_affected", "test", eff, vulnId);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user