using System.Diagnostics; namespace Azaion.Missions.E2E.Fixtures; /// /// Stop/start helper for the postgres-test compose service. Used by FT-P-17 /// to prove that /health 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. /// /// /// Like , this fixture only runs when /// COMPOSE_RESTART_ENABLED=1. 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. /// 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}"); } } }