113 lines
5.1 KiB
C#
113 lines
5.1 KiB
C#
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Npgsql;
|
|
using StellaOps.EvidenceLocker.Core.Configuration;
|
|
using StellaOps.EvidenceLocker.Core.Domain;
|
|
using StellaOps.EvidenceLocker.Infrastructure.Db;
|
|
using StellaOps.TestKit;
|
|
using System;
|
|
using System.Net.Http;
|
|
using Testcontainers.PostgreSql;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.EvidenceLocker.Tests;
|
|
|
|
public sealed class DatabaseMigrationTests : IClassFixture<PostgreSqlFixture>
|
|
{
|
|
private readonly PostgreSqlFixture _fixture;
|
|
|
|
public DatabaseMigrationTests(PostgreSqlFixture fixture)
|
|
{
|
|
_fixture = fixture;
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Integration)]
|
|
[Fact]
|
|
public async Task ApplyAsync_CreatesExpectedSchemaAndPolicies()
|
|
{
|
|
await _fixture.EnsureInitializedAsync();
|
|
|
|
if (_fixture.SkipReason is not null)
|
|
{
|
|
Assert.Skip(_fixture.SkipReason);
|
|
}
|
|
|
|
var cancellationToken = CancellationToken.None;
|
|
|
|
// Migrations already applied by the fixture; verify schema state.
|
|
await using var connection = await _fixture.DataSource!.OpenConnectionAsync(cancellationToken);
|
|
await using var tablesCommand = new NpgsqlCommand(
|
|
"SELECT table_name FROM information_schema.tables WHERE table_schema = 'evidence_locker' ORDER BY table_name;",
|
|
connection);
|
|
var tables = new List<string>();
|
|
await using (var reader = await tablesCommand.ExecuteReaderAsync(cancellationToken))
|
|
{
|
|
while (await reader.ReadAsync(cancellationToken))
|
|
{
|
|
tables.Add(reader.GetString(0));
|
|
}
|
|
}
|
|
|
|
Assert.Contains("evidence_artifacts", tables);
|
|
Assert.Contains("evidence_bundles", tables);
|
|
Assert.Contains("evidence_gate_artifacts", tables);
|
|
Assert.Contains("evidence_holds", tables);
|
|
Assert.Contains("evidence_schema_version", tables);
|
|
|
|
await using var versionCommand = new NpgsqlCommand(
|
|
"SELECT COUNT(*) FROM evidence_locker.evidence_schema_version WHERE version = 1;",
|
|
connection);
|
|
var applied = Convert.ToInt64(await versionCommand.ExecuteScalarAsync(cancellationToken) ?? 0L);
|
|
Assert.Equal(1, applied);
|
|
|
|
await using var versionFourCommand = new NpgsqlCommand(
|
|
"SELECT COUNT(*) FROM evidence_locker.evidence_schema_version WHERE version = 4;",
|
|
connection);
|
|
var appliedVersionFour = Convert.ToInt64(await versionFourCommand.ExecuteScalarAsync(cancellationToken) ?? 0L);
|
|
Assert.Equal(1, appliedVersionFour);
|
|
|
|
var tenant = TenantId.FromGuid(Guid.NewGuid());
|
|
await using var tenantConnection = await _fixture.DataSource.OpenConnectionAsync(tenant, cancellationToken);
|
|
await using var insertCommand = new NpgsqlCommand(@"
|
|
INSERT INTO evidence_locker.evidence_bundles
|
|
(bundle_id, tenant_id, kind, status, root_hash, storage_key)
|
|
VALUES
|
|
(@bundle, @tenant, 1, 3, @hash, @key);",
|
|
tenantConnection);
|
|
insertCommand.Parameters.AddWithValue("bundle", Guid.NewGuid());
|
|
insertCommand.Parameters.AddWithValue("tenant", tenant.Value);
|
|
insertCommand.Parameters.AddWithValue("hash", new string('a', 64));
|
|
insertCommand.Parameters.AddWithValue("key", $"tenants/{tenant.Value:N}/bundles/test/resource");
|
|
await insertCommand.ExecuteNonQueryAsync(cancellationToken);
|
|
|
|
await using var isolationConnection = await _fixture.DataSource.OpenConnectionAsync(tenant, cancellationToken);
|
|
await using var selectCommand = new NpgsqlCommand(
|
|
"SELECT COUNT(*) FROM evidence_locker.evidence_bundles;",
|
|
isolationConnection);
|
|
var visibleCount = Convert.ToInt64(await selectCommand.ExecuteScalarAsync(cancellationToken) ?? 0L);
|
|
Assert.Equal(1, visibleCount);
|
|
|
|
await using var otherTenantConnection = await _fixture.DataSource.OpenConnectionAsync(TenantId.FromGuid(Guid.NewGuid()), cancellationToken);
|
|
await using var otherSelectCommand = new NpgsqlCommand(
|
|
"SELECT COUNT(*) FROM evidence_locker.evidence_bundles;",
|
|
otherTenantConnection);
|
|
var otherVisible = Convert.ToInt64(await otherSelectCommand.ExecuteScalarAsync(cancellationToken) ?? 0L);
|
|
Assert.Equal(0, otherVisible);
|
|
|
|
await using var violationConnection = await _fixture.DataSource.OpenConnectionAsync(tenant, cancellationToken);
|
|
await using var violationCommand = new NpgsqlCommand(@"
|
|
INSERT INTO evidence_locker.evidence_bundles
|
|
(bundle_id, tenant_id, kind, status, root_hash, storage_key)
|
|
VALUES
|
|
(@bundle, @tenant, 1, 3, @hash, @key);",
|
|
violationConnection);
|
|
violationCommand.Parameters.AddWithValue("bundle", Guid.NewGuid());
|
|
violationCommand.Parameters.AddWithValue("tenant", Guid.NewGuid());
|
|
violationCommand.Parameters.AddWithValue("hash", new string('b', 64));
|
|
violationCommand.Parameters.AddWithValue("key", "tenants/other/bundles/resource");
|
|
|
|
await Assert.ThrowsAsync<PostgresException>(() => violationCommand.ExecuteNonQueryAsync(cancellationToken));
|
|
}
|
|
}
|
|
|