Files
Oleksandr Bezdieniezhnykh 6b2c2d998e [AZ-577] [AZ-578] [AZ-579] [AZ-580] Implement E2E test batch 2
Adds 26 blackbox tests (FT-P-01..18, FT-N-01..08) covering full AC
matrices for Vehicles/Missions/Waypoints/Health/Errors. Three
spec-vs-code carry-forwards documented in batch_02_report.md and
pinned with [Trait("carry_forward", ...)].

Shared scaffolding: ApiDtos.cs, AssertProblemEnvelopeAsync helper,
Seeds.cs, StubSchema.cs, CascadeF3/F4 fixtures, PostgresStopStart
fixture (gated by COMPOSE_RESTART_ENABLED). Removes the 4 placeholder
Sanity.cs files (now superseded). docker-compose.test.yml gains the
expected_results volume mount + FIXTURE_SQL_DIR for the consumer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-15 08:28:37 +03:00

87 lines
2.9 KiB
C#

using System.Diagnostics;
namespace Azaion.Missions.E2E.Fixtures;
/// <summary>
/// Stop/start helper for the postgres-test compose service. Used by FT-P-17
/// to prove that <c>/health</c> does not ping the database — the fixture
/// stops postgres-test, the test asserts /health still returns 200, and the
/// fixture restarts postgres-test in teardown.
/// </summary>
/// <remarks>
/// Like <see cref="ComposeRestartFixture"/>, this fixture only runs when
/// <c>COMPOSE_RESTART_ENABLED=1</c>. The e2e-consumer image needs the
/// docker CLI on PATH and a docker socket bind to actually drive compose.
/// Tests using the fixture must skip with a clear reason when disabled.
/// </remarks>
public sealed class PostgresStopStartFixture
{
public bool Enabled => Environment.GetEnvironmentVariable("COMPOSE_RESTART_ENABLED") == "1";
public string ComposeFile =>
Environment.GetEnvironmentVariable("COMPOSE_FILE_PATH") ?? "/workspace/docker-compose.test.yml";
public string ServiceName =>
Environment.GetEnvironmentVariable("POSTGRES_SERVICE_NAME") ?? "postgres-test";
public void Stop()
{
EnsureEnabled();
Run("docker", $"compose -f {ComposeFile} stop {ServiceName}");
}
public void Start()
{
EnsureEnabled();
Run("docker", $"compose -f {ComposeFile} start {ServiceName}");
// Wait for the service to report healthy via pg_isready before
// returning — otherwise the next test would hit ConnectionRefused.
WaitUntilHealthy();
}
private void WaitUntilHealthy()
{
var deadline = DateTime.UtcNow.AddSeconds(30);
while (DateTime.UtcNow < deadline)
{
try
{
Run("docker",
$"compose -f {ComposeFile} exec -T {ServiceName} pg_isready -U postgres -d azaion");
return;
}
catch (InvalidOperationException)
{
Thread.Sleep(500);
}
}
throw new InvalidOperationException(
$"postgres service '{ServiceName}' did not become ready within 30s after start");
}
private void EnsureEnabled()
{
if (!Enabled)
throw new InvalidOperationException(
"PostgresStopStartFixture is disabled; set COMPOSE_RESTART_ENABLED=1 to use it.");
}
private static void Run(string file, string args)
{
var psi = new ProcessStartInfo(file, args)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
using var p = Process.Start(psi)
?? throw new InvalidOperationException($"Failed to launch {file} {args}");
p.WaitForExit();
if (p.ExitCode != 0)
{
var err = p.StandardError.ReadToEnd();
throw new InvalidOperationException($"`{file} {args}` exited {p.ExitCode}: {err}");
}
}
}