V13-FE-011: finalize search list layout slice
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Kbx.Tests.Scenarios;
|
||||
|
||||
public sealed record KbxScenarioArtifact(string Type, string Path, string? Sha256 = null);
|
||||
|
||||
public sealed record KbxScenarioEvidence(
|
||||
string ScenarioId,
|
||||
string RunId,
|
||||
DateTimeOffset StartedAt,
|
||||
DateTimeOffset? FinishedAt,
|
||||
string Result,
|
||||
IReadOnlyDictionary<string,string> ContractVersions,
|
||||
IReadOnlyList<string> CorrelationIds,
|
||||
IReadOnlyList<KbxScenarioArtifact> Artifacts);
|
||||
|
||||
public static class KbxScenarioHash
|
||||
{
|
||||
public static string Sha256File(string path)
|
||||
{
|
||||
using var stream = File.OpenRead(path);
|
||||
return Convert.ToHexString(SHA256.HashData(stream)).ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Xunit;
|
||||
using Npgsql;
|
||||
using Testcontainers.PostgreSql;
|
||||
|
||||
namespace Kbx.Tests.Scenarios;
|
||||
|
||||
/// <summary>
|
||||
/// Host repository reference fixture. Requires Testcontainers.PostgreSql and Npgsql.
|
||||
/// The container is disposable; never point this fixture at a production connection string.
|
||||
/// </summary>
|
||||
public sealed class KbxScenarioPostgresFixture : IAsyncLifetime
|
||||
{
|
||||
private readonly PostgreSqlContainer _postgres = new PostgreSqlBuilder()
|
||||
.WithImage("postgres:17-alpine")
|
||||
.WithDatabase("kbx_scenario_test")
|
||||
.WithUsername("kbx")
|
||||
.WithPassword("kbx-test-only")
|
||||
.Build();
|
||||
|
||||
public string ConnectionString => _postgres.GetConnectionString();
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await _postgres.StartAsync();
|
||||
// Host: run DbUp migrations in filename order, then bootstrap/reset/seed SQL.
|
||||
}
|
||||
|
||||
public Task DisposeAsync() => _postgres.DisposeAsync().AsTask();
|
||||
|
||||
public static async Task AssertTestGuardAsync(string connectionString, CancellationToken ct = default)
|
||||
{
|
||||
await using var connection = new NpgsqlConnection(connectionString);
|
||||
await connection.OpenAsync(ct);
|
||||
await using var command = new NpgsqlCommand(
|
||||
"select exists(select 1 from kbx_test.environment_guard where marker='KBX_SCENARIO_TEST_ONLY')", connection);
|
||||
if (await command.ExecuteScalarAsync(ct) is not true)
|
||||
throw new InvalidOperationException("KBX scenario database guard is missing.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Text.Json;
|
||||
using ClosedXML.Excel;
|
||||
|
||||
namespace Kbx.Tests.Scenarios;
|
||||
|
||||
/// <summary>Builds an XLSX from the checked-in synthetic JSON fixture. No production spreadsheet is stored in source control.</summary>
|
||||
public static class KbxSyntheticImportWorkbook
|
||||
{
|
||||
private sealed record Fixture(string Sheet, string[] Columns, JsonElement[][] Rows);
|
||||
|
||||
public static void Build(string jsonPath, string xlsxPath)
|
||||
{
|
||||
var fixture = JsonSerializer.Deserialize<Fixture>(File.ReadAllText(jsonPath),
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true })
|
||||
?? throw new InvalidOperationException("Invalid synthetic import fixture.");
|
||||
|
||||
using var workbook = new XLWorkbook();
|
||||
var sheet = workbook.Worksheets.Add(fixture.Sheet);
|
||||
for (var c = 0; c < fixture.Columns.Length; c++)
|
||||
sheet.Cell(1, c + 1).Value = fixture.Columns[c];
|
||||
|
||||
for (var r = 0; r < fixture.Rows.Length; r++)
|
||||
for (var c = 0; c < fixture.Rows[r].Length; c++)
|
||||
{
|
||||
var cell = sheet.Cell(r + 2, c + 1);
|
||||
var value = fixture.Rows[r][c];
|
||||
cell.Value = value.ValueKind switch
|
||||
{
|
||||
JsonValueKind.Number when value.TryGetDecimal(out var number) => number,
|
||||
JsonValueKind.String => value.GetString() ?? string.Empty,
|
||||
JsonValueKind.True => true,
|
||||
JsonValueKind.False => false,
|
||||
_ => value.ToString()
|
||||
};
|
||||
}
|
||||
workbook.SaveAs(xlsxPath);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user