From a0100b82d13520c810853aace4c76098104dbf4a Mon Sep 17 00:00:00 2001 From: Golo Roden Date: Wed, 7 Oct 2026 18:09:51 +0200 Subject: [PATCH] chore: Start the database for the test of an interrupted write outside the timeout of CancellationTest, so that a slow Docker does not break all container tests. CancellationTest gives each test 10 seconds, and the test of a write whose thread is interrupted started its container within that time. When Docker was slow to answer, e.g. right after Docker Desktop resumed, the timeout interrupted Testcontainers while it was looking for Docker. Testcontainers then never tries again, so every later test that needed a container failed with "Previous attempts to find a Docker environment failed. Will not retry." The test now lives in WriteEventsTest, which starts the database in @BeforeEach, and the timeout of a class does not cover that. With the timeout of CancellationTest cut to 2 seconds, the test failed before, and all tests pass now. A comment in CancellationTest says why its timeout must not cover starting a container. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Hc2MPSLu7HDHTm8HiBsrDz --- .../io/eventsourcingdb/CancellationTest.java | 30 ++++--------------- .../io/eventsourcingdb/WriteEventsTest.java | 15 ++++++++++ 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/eventsourcingdb/src/test/java/io/eventsourcingdb/CancellationTest.java b/eventsourcingdb/src/test/java/io/eventsourcingdb/CancellationTest.java index bf8204f..08ce0a2 100644 --- a/eventsourcingdb/src/test/java/io/eventsourcingdb/CancellationTest.java +++ b/eventsourcingdb/src/test/java/io/eventsourcingdb/CancellationTest.java @@ -9,7 +9,6 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.CancellationException; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -24,6 +23,12 @@ // the thread that makes it, and a stream by closing it. Either way, the // request then ends with a CancellationException, so that an aborted read can // be told apart from a complete one. +// +// The timeout of this class must not cover starting a container: if it +// interrupts Testcontainers while it looks for Docker, Testcontainers never +// tries again, and every later test that needs a container fails. Tests that +// need EventSourcingDB itself start it in @BeforeEach elsewhere, e.g. in +// WriteEventsTest, which this timeout does not cover. @Timeout(10) class CancellationTest { static List all() { @@ -124,29 +129,6 @@ void aStreamThatIsClosedBeforeItStartsDoesNotSendTheRequest(Operation operation) } } - @Test - void aWriteOfAnInterruptedThreadWritesNothing() { - var container = Database.start(); - try { - var client = container.getClient(); - var candidate = new EventCandidate( - "https://www.eventsourcingdb.io", "/test", "io.eventsourcingdb.test", Map.of("value", 23)); - - Thread.currentThread().interrupt(); - try { - assertThrows(CancellationException.class, () -> client.writeEvents(List.of(candidate))); - } finally { - assertTrue(Thread.interrupted()); - } - - try (var events = client.readEvents("/", new ReadEventsOptions(true))) { - assertEquals(0, events.count()); - } - } finally { - container.stop(); - } - } - @Test void anInterruptEndsAWriteWhoseResponseStopsInTheMiddle() throws InterruptedException { try (var server = FakeServer.start((exchange, fakeServer) -> { diff --git a/eventsourcingdb/src/test/java/io/eventsourcingdb/WriteEventsTest.java b/eventsourcingdb/src/test/java/io/eventsourcingdb/WriteEventsTest.java index 9072ac5..e8a3550 100644 --- a/eventsourcingdb/src/test/java/io/eventsourcingdb/WriteEventsTest.java +++ b/eventsourcingdb/src/test/java/io/eventsourcingdb/WriteEventsTest.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.CancellationException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -70,6 +71,20 @@ void writesMultipleEvents() { assertEquals(new EventData(42), writtenEvents.get(1).data(EventData.class)); } + @Test + void writesNothingIfTheThreadIsInterrupted() { + Thread.currentThread().interrupt(); + try { + assertThrows(CancellationException.class, () -> client.writeEvents(List.of(candidate(23)))); + } finally { + assertTrue(Thread.interrupted()); + } + + try (var events = client.readEvents("/", new ReadEventsOptions(true))) { + assertEquals(0, events.count()); + } + } + @Test void writesTheTraceContextOfAnEvent() { var traceParent = "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01";