From 294528dab72f27c566d51475b22d20610cb9e962 Mon Sep 17 00:00:00 2001 From: Shubham Ranjan Date: Wed, 8 Jul 2026 01:54:39 +0530 Subject: [PATCH] test: fix integration flakiness from shared-etcd interference The nightly beast kept failing Watch_ShouldRecover_AfterServerRestart with 'Collection was empty' even after the revision-resume fix (#310). Root cause was test cross-contamination on the single shared etcd1, not the reconnect logic: 1. xUnit runs different test collections in PARALLEL and there was no assembly-wide guard. AuthClientIntegrationTests enables/disables cluster-wide auth on etcd1; any other test hitting etcd1 unauthenticated during that window fails with 'etcdserver: user name is empty' -> the watch delivers no events -> empty. 2. WatchResilienceTests pause/restart their etcd server, disrupting every other test that shares it (parallel or serial). Fix: - Add xunit.runner.json with parallelizeTestCollections=false so collections run sequentially and auth toggling can't overlap other etcd1 tests. - Give WatchResilienceTests a dedicated single-node etcd (etcd-resilience, port 2409) so pausing/restarting it never affects the shared etcd1 cluster tests. Verified: full integration suite 57/57 green; both resilience tests pass against the dedicated instance. --- .../Integration/WatchResilienceTests.cs | 7 ++++--- dotnet-etcd.Tests/docker-compose.yml | 17 +++++++++++++++++ dotnet-etcd.Tests/dotnet-etcd.Tests.csproj | 4 ++++ dotnet-etcd.Tests/xunit.runner.json | 4 ++++ 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 dotnet-etcd.Tests/xunit.runner.json diff --git a/dotnet-etcd.Tests/Integration/WatchResilienceTests.cs b/dotnet-etcd.Tests/Integration/WatchResilienceTests.cs index 6443f1e..bbc78e9 100644 --- a/dotnet-etcd.Tests/Integration/WatchResilienceTests.cs +++ b/dotnet-etcd.Tests/Integration/WatchResilienceTests.cs @@ -16,9 +16,10 @@ public class WatchResilienceTests : IDisposable private readonly EtcdClient _client; private readonly string _testKeyPrefix = "watch-resilience-"; - // Use etcd1 (port 2379) as per standard test env - private const string EtcdUrl = "http://localhost:2379"; - private const string ContainerName = "etcd1"; + // Dedicated single-node etcd (port 2409) so pausing/restarting the server here never + // disrupts the shared etcd1 cluster used by the other integration tests. + private const string EtcdUrl = "http://localhost:2409"; + private const string ContainerName = "etcd-resilience"; public WatchResilienceTests() { diff --git a/dotnet-etcd.Tests/docker-compose.yml b/dotnet-etcd.Tests/docker-compose.yml index 7e4ede4..0889b82 100644 --- a/dotnet-etcd.Tests/docker-compose.yml +++ b/dotnet-etcd.Tests/docker-compose.yml @@ -95,6 +95,23 @@ services: networks: - etcd-net + # Dedicated single-node etcd for WatchResilienceTests, which pause/restart their server. + # Isolated on its own port so that disruption never affects the shared etcd1 cluster tests. + etcd-resilience: + image: quay.io/coreos/etcd:v3.5.21 + container_name: etcd-resilience + ports: + - "2409:2379" + command: + - /usr/local/bin/etcd + - --name=etcd-resilience + - --data-dir=/etcd-data + - --listen-client-urls=http://0.0.0.0:2379 + - --advertise-client-urls=http://localhost:2409 + restart: always + networks: + - etcd-net + networks: etcd-net: driver: bridge \ No newline at end of file diff --git a/dotnet-etcd.Tests/dotnet-etcd.Tests.csproj b/dotnet-etcd.Tests/dotnet-etcd.Tests.csproj index 6a402a0..a2e6550 100644 --- a/dotnet-etcd.Tests/dotnet-etcd.Tests.csproj +++ b/dotnet-etcd.Tests/dotnet-etcd.Tests.csproj @@ -41,6 +41,10 @@ PreserveNewest + + diff --git a/dotnet-etcd.Tests/xunit.runner.json b/dotnet-etcd.Tests/xunit.runner.json new file mode 100644 index 0000000..08c512b --- /dev/null +++ b/dotnet-etcd.Tests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeTestCollections": false +}