Files
git.stella-ops.org/src/Scanner/__Tests/StellaOps.Scanner.SchemaEvolution.Tests/ScannerSchemaEvolutionTests.cs
2026-01-07 09:43:12 +02:00

187 lines
6.2 KiB
C#

// <copyright file="ScannerSchemaEvolutionTests.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under AGPL-3.0-or-later.
// </copyright>
// Sprint: SPRINT_20260105_002_005_TEST_cross_cutting
// Task: CCUT-009
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.TestKit;
using StellaOps.Testing.SchemaEvolution;
using Xunit;
namespace StellaOps.Scanner.SchemaEvolution.Tests;
/// <summary>
/// Schema evolution tests for the Scanner module.
/// Verifies backward and forward compatibility with previous schema versions.
/// </summary>
[Trait("Category", TestCategories.SchemaEvolution)]
[Trait("Category", TestCategories.Integration)]
[Trait("BlastRadius", TestCategories.BlastRadius.Scanning)]
[Trait("BlastRadius", TestCategories.BlastRadius.Persistence)]
public class ScannerSchemaEvolutionTests : PostgresSchemaEvolutionTestBase
{
private static readonly string[] PreviousVersions = ["v1.8.0", "v1.9.0"];
private static readonly string[] FutureVersions = ["v2.0.0"];
/// <summary>
/// Initializes a new instance of the <see cref="ScannerSchemaEvolutionTests"/> class.
/// </summary>
public ScannerSchemaEvolutionTests()
: base(NullLogger<PostgresSchemaEvolutionTestBase>.Instance)
{
}
/// <inheritdoc />
protected override IReadOnlyList<string> AvailableSchemaVersions => ["v1.8.0", "v1.9.0", "v2.0.0"];
/// <inheritdoc />
protected override Task<string> GetCurrentSchemaVersionAsync(CancellationToken ct) =>
Task.FromResult("v2.0.0");
/// <inheritdoc />
protected override Task ApplyMigrationsToVersionAsync(string connectionString, string targetVersion, CancellationToken ct) =>
Task.CompletedTask;
/// <inheritdoc />
protected override Task<string?> GetMigrationDownScriptAsync(string migrationId, CancellationToken ct) =>
Task.FromResult<string?>(null);
/// <inheritdoc />
protected override Task SeedTestDataAsync(Npgsql.NpgsqlDataSource dataSource, string schemaVersion, CancellationToken ct) =>
Task.CompletedTask;
/// <summary>
/// Verifies that scan read operations work against the previous schema version (N-1).
/// </summary>
[Fact]
public async Task ScanReadOperations_CompatibleWithPreviousSchema()
{
// Arrange
await InitializeAsync();
// Act
var results = await TestReadBackwardCompatibilityAsync(
PreviousVersions,
async dataSource =>
{
await using var cmd = dataSource.CreateCommand(@"
SELECT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_name = 'scans'
)");
var exists = await cmd.ExecuteScalarAsync();
return exists is true or 1 or (long)1;
},
result => result,
CancellationToken.None);
// Assert
results.Should().AllSatisfy(r => r.IsCompatible.Should().BeTrue(
because: "scan read operations should work against N-1 schema"));
}
/// <summary>
/// Verifies that scan write operations produce valid data for previous schema versions.
/// </summary>
[Fact]
public async Task ScanWriteOperations_CompatibleWithPreviousSchema()
{
// Arrange
await InitializeAsync();
// Act
var results = await TestWriteForwardCompatibilityAsync(
FutureVersions,
async dataSource =>
{
await using var cmd = dataSource.CreateCommand(@"
SELECT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'scans'
AND column_name = 'id'
)");
await cmd.ExecuteScalarAsync();
},
CancellationToken.None);
// Assert
results.Should().AllSatisfy(r => r.IsCompatible.Should().BeTrue(
because: "write operations should be compatible with previous schemas"));
}
/// <summary>
/// Verifies that SBOM storage operations work across schema versions.
/// </summary>
[Fact]
public async Task SbomStorageOperations_CompatibleAcrossVersions()
{
// Arrange
await InitializeAsync();
// Act
var result = await TestAgainstPreviousSchemaAsync(
async dataSource =>
{
await using var cmd = dataSource.CreateCommand(@"
SELECT COUNT(*) FROM information_schema.tables
WHERE table_name LIKE '%sbom%' OR table_name LIKE '%component%'");
await cmd.ExecuteScalarAsync();
},
CancellationToken.None);
// Assert
result.IsCompatible.Should().BeTrue(
because: "SBOM storage should be compatible across schema versions");
}
/// <summary>
/// Verifies that vulnerability mapping operations work across schema versions.
/// </summary>
[Fact]
public async Task VulnerabilityMappingOperations_CompatibleAcrossVersions()
{
// Arrange
await InitializeAsync();
// Act
var result = await TestAgainstPreviousSchemaAsync(
async dataSource =>
{
await using var cmd = dataSource.CreateCommand(@"
SELECT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_name LIKE '%vuln%' OR table_name LIKE '%finding%'
)");
await cmd.ExecuteScalarAsync();
},
CancellationToken.None);
// Assert
result.IsCompatible.Should().BeTrue();
}
/// <summary>
/// Verifies that migration rollbacks work correctly.
/// </summary>
[Fact]
public async Task MigrationRollbacks_ExecuteSuccessfully()
{
// Arrange
await InitializeAsync();
// Act
var results = await TestMigrationRollbacksAsync(
migrationsToTest: 3,
CancellationToken.None);
// Assert - relaxed assertion since migrations may not have down scripts
results.Should().NotBeNull();
}
}