From b4a51eb59894bb672aeff188492bdd7e355c4390 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 9 Apr 2026 11:36:23 +0000
Subject: [PATCH 1/4] Replace Assert.Ignore with Assert.Fail for Docker
availability checks across all test files
Agent-Logs-Url: https://github.com/devstress/My3DLearning/sessions/83390dc6-fc9e-478e-a484-a2d6260821bc
Co-authored-by: devstress <30769729+devstress@users.noreply.github.com>
---
.../rules/completion-log.md | 2 +-
.../Infrastructure/AspireFixture.cs | 28 +++++++++++-----
.../BrokerEndToEndTests.cs | 2 +-
.../InfrastructureTests/ConnectivityTests.cs | 32 +++++++++++++++----
.../PostgresAdvancedEipTests.cs | 16 +++++-----
.../PostgresRoutingIntegrationTests.cs | 16 +++++-----
.../TutorialLabs/Tutorial01/Exam.Answers.cs | 5 ++-
.../tests/TutorialLabs/Tutorial01/Exam.cs | 5 ++-
.../tests/TutorialLabs/Tutorial01/Lab.cs | 5 ++-
9 files changed, 75 insertions(+), 36 deletions(-)
diff --git a/EnterpriseIntegrationPlatform/rules/completion-log.md b/EnterpriseIntegrationPlatform/rules/completion-log.md
index d86c8cf..61174ab 100644
--- a/EnterpriseIntegrationPlatform/rules/completion-log.md
+++ b/EnterpriseIntegrationPlatform/rules/completion-log.md
@@ -604,7 +604,7 @@ See `milestones.md` for current phase status and next chunk.
- `PostgresRoutingIntegrationTests` (7 tests): ContentBasedRouter (MessageType + Metadata + Regex), MessageFilter, RecipientListRouter, DynamicRouter (register + route), Detour (activate/deactivate)
- `PostgresAdvancedEipTests` (7 tests): Splitter (split + causation chain), DeadLetterPublisher (single + multi-reason), Resequencer (out-of-order → in-order + publish), Retry + DLQ pipeline, Aggregator (count completion + concat), full pipeline (Splitter → Router → DLQ)
- All tests use unique topics (Guid-based) to prevent cross-test interference
- - All tests Docker-gated: Assert.Ignore when Aspire Postgres container unavailable
+ - All tests Docker-gated: Assert.Fail when Aspire Postgres container unavailable
- **Files created**:
- `tests/TutorialLabs/InfrastructureTests/PostgresRoutingIntegrationTests.cs` (7 tests)
- `tests/TutorialLabs/InfrastructureTests/PostgresAdvancedEipTests.cs` (7 tests)
diff --git a/EnterpriseIntegrationPlatform/tests/TutorialLabs/Infrastructure/AspireFixture.cs b/EnterpriseIntegrationPlatform/tests/TutorialLabs/Infrastructure/AspireFixture.cs
index bf1bac3..bb87c45 100644
--- a/EnterpriseIntegrationPlatform/tests/TutorialLabs/Infrastructure/AspireFixture.cs
+++ b/EnterpriseIntegrationPlatform/tests/TutorialLabs/Infrastructure/AspireFixture.cs
@@ -80,45 +80,57 @@ public async Task GlobalTearDown()
///
/// Creates a NatsBrokerEndpoint connected to the real NATS JetStream.
- /// Throws Ignore if Docker is not available.
+ /// Throws Fail if Docker is not available.
///
public static NatsBrokerEndpoint CreateNatsEndpoint(string name)
{
if (!IsAvailable || NatsUrl is null)
- Assert.Ignore("Docker not available — skipping real broker test");
+ {
+ Assert.Fail("Docker not available — skipping real broker test");
+ return null!; // unreachable — Assert.Fail throws
+ }
return new NatsBrokerEndpoint(name, NatsUrl);
}
///
/// Creates a PostgresBrokerEndpoint connected to the real PostgreSQL.
- /// Throws Ignore if Docker is not available.
+ /// Throws Fail if Docker is not available.
///
public static PostgresBrokerEndpoint CreatePostgresEndpoint(string name)
{
if (!IsAvailable || PostgresConnectionString is null)
- Assert.Ignore("Docker not available — skipping real Postgres broker test");
+ {
+ Assert.Fail("Docker not available — skipping real Postgres broker test");
+ return null!; // unreachable — Assert.Fail throws
+ }
return new PostgresBrokerEndpoint(name, PostgresConnectionString);
}
///
/// Creates a KafkaBrokerEndpoint connected to the real Apache Kafka.
- /// Throws Ignore if Docker is not available.
+ /// Throws Fail if Docker is not available.
///
public static KafkaBrokerEndpoint CreateKafkaEndpoint(string name)
{
if (!IsAvailable || KafkaBootstrapServers is null)
- Assert.Ignore("Docker not available — skipping real Kafka broker test");
+ {
+ Assert.Fail("Docker not available — skipping real Kafka broker test");
+ return null!; // unreachable — Assert.Fail throws
+ }
return new KafkaBrokerEndpoint(name, KafkaBootstrapServers);
}
///
/// Creates a PulsarBrokerEndpoint connected to the real Apache Pulsar.
- /// Throws Ignore if Docker is not available.
+ /// Throws Fail if Docker is not available.
///
public static PulsarBrokerEndpoint CreatePulsarEndpoint(string name)
{
if (!IsAvailable || PulsarServiceUrl is null)
- Assert.Ignore("Docker not available — skipping real Pulsar broker test");
+ {
+ Assert.Fail("Docker not available — skipping real Pulsar broker test");
+ return null!; // unreachable — Assert.Fail throws
+ }
return new PulsarBrokerEndpoint(name, PulsarServiceUrl);
}
diff --git a/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/BrokerEndToEndTests.cs b/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/BrokerEndToEndTests.cs
index 4ea4f99..1529f55 100644
--- a/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/BrokerEndToEndTests.cs
+++ b/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/BrokerEndToEndTests.cs
@@ -8,7 +8,7 @@
// 3. Apache Pulsar (Key_Shared recipient-keyed distribution)
// 4. PostgreSQL (SQL-based, ACID, pg_notify)
//
-// Requires Docker; tests are skipped (Assert.Ignore) when unavailable.
+// Requires Docker; tests fail (Assert.Fail) when unavailable.
// ============================================================================
using EnterpriseIntegrationPlatform.Contracts;
diff --git a/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/ConnectivityTests.cs b/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/ConnectivityTests.cs
index 7130ffe..183044e 100644
--- a/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/ConnectivityTests.cs
+++ b/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/ConnectivityTests.cs
@@ -10,7 +10,7 @@ namespace TutorialLabs.InfrastructureTests;
///
/// Smoke tests that verify the Aspire-hosted test infrastructure starts
-/// and connects. Requires Docker; tests are skipped when unavailable.
+/// and connects. Requires Docker; tests fail when unavailable.
///
[TestFixture]
public sealed class InfrastructureConnectivityTests
@@ -22,7 +22,10 @@ public async Task Nats_PublishAndReceive_RoundTrip()
{
var natsUrl = await SharedTestAppHost.GetNatsUrlAsync();
if (natsUrl is null)
- Assert.Ignore("Docker not available — skipping NATS test");
+ {
+ Assert.Fail("Docker not available — skipping NATS test");
+ return;
+ }
await using var endpoint = new NatsBrokerEndpoint("nats-test", natsUrl);
@@ -56,7 +59,10 @@ public async Task Nats_ProducerCaptures_PublishedMessages()
{
var natsUrl = await SharedTestAppHost.GetNatsUrlAsync();
if (natsUrl is null)
- Assert.Ignore("Docker not available — skipping NATS test");
+ {
+ Assert.Fail("Docker not available — skipping NATS test");
+ return;
+ }
await using var endpoint = new NatsBrokerEndpoint("capture-test", natsUrl);
@@ -110,7 +116,10 @@ public async Task AspireTestAppHost_StartsSuccessfully()
{
var app = await SharedTestAppHost.GetAppAsync();
if (app is null)
- Assert.Ignore("Docker not available — skipping Aspire test");
+ {
+ Assert.Fail("Docker not available — skipping Aspire test");
+ return;
+ }
Assert.That(SharedTestAppHost.IsAvailable, Is.True,
"Aspire TestAppHost should be running");
@@ -123,7 +132,10 @@ public async Task Postgres_PublishAndPoll_RoundTrip()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
if (connStr is null)
- Assert.Ignore("Docker not available — skipping Postgres test");
+ {
+ Assert.Fail("Docker not available — skipping Postgres test");
+ return;
+ }
await using var endpoint = new PostgresBrokerEndpoint("pg-roundtrip-test", connStr);
await endpoint.EnsureSchemaAsync();
@@ -147,7 +159,10 @@ public async Task Postgres_ProducerCaptures_PublishedMessages()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
if (connStr is null)
- Assert.Ignore("Docker not available — skipping Postgres test");
+ {
+ Assert.Fail("Docker not available — skipping Postgres test");
+ return;
+ }
await using var endpoint = new PostgresBrokerEndpoint("pg-capture-test", connStr);
await endpoint.EnsureSchemaAsync();
@@ -169,7 +184,10 @@ public async Task Postgres_SubscribeAndReceive_EventDriven()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
if (connStr is null)
- Assert.Ignore("Docker not available — skipping Postgres test");
+ {
+ Assert.Fail("Docker not available — skipping Postgres test");
+ return;
+ }
await using var endpoint = new PostgresBrokerEndpoint("pg-subscribe-test", connStr);
await endpoint.EnsureSchemaAsync();
diff --git a/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/PostgresAdvancedEipTests.cs b/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/PostgresAdvancedEipTests.cs
index 63fff67..0fd7211 100644
--- a/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/PostgresAdvancedEipTests.cs
+++ b/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/PostgresAdvancedEipTests.cs
@@ -3,7 +3,7 @@
// ============================================================================
// Proves advanced EIP patterns work on real PostgreSQL broker transport:
// Splitter, Aggregator, Resequencer, Dead-Letter Publisher, Retry Policy.
-// Requires Docker (Aspire Postgres container); tests skipped when unavailable.
+// Requires Docker (Aspire Postgres container); tests fail when unavailable.
// ============================================================================
using EnterpriseIntegrationPlatform.Contracts;
@@ -32,7 +32,7 @@ public sealed class PostgresAdvancedEipTests
public async Task Splitter_SplitsComposite_PublishesEachPart_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("split-pg", connStr);
var splitTopic = $"split-{Guid.NewGuid():N}";
@@ -65,7 +65,7 @@ public async Task Splitter_SplitsComposite_PublishesEachPart_ViaPostgres()
public async Task DeadLetterPublisher_PublishesToDlqTopic_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("dlq-pg", connStr);
var dlqTopic = $"dlq-{Guid.NewGuid():N}";
@@ -105,7 +105,7 @@ await dlq.PublishAsync(
public async Task DeadLetterPublisher_MultipleReasons_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("dlq-multi-pg", connStr);
var dlqTopic = $"dlq-multi-{Guid.NewGuid():N}";
@@ -131,7 +131,7 @@ public async Task DeadLetterPublisher_MultipleReasons_ViaPostgres()
public async Task Resequencer_OrdersOutOfSequenceMessages_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
// Resequencer is in-memory but we prove it works in a Postgres pipeline
var resequencer = new MessageResequencer(
@@ -210,7 +210,7 @@ public async Task Resequencer_OrdersOutOfSequenceMessages_ViaPostgres()
public async Task RetryPolicy_ExhaustsRetries_ThenDlq_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("retry-pg", connStr);
var dlqTopic = $"retry-dlq-{Guid.NewGuid():N}";
@@ -270,7 +270,7 @@ await dlq.PublishAsync(
public async Task Aggregator_CollectsAndPublishesAggregate_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("agg-pg", connStr);
var aggTopic = $"aggregated-{Guid.NewGuid():N}";
@@ -315,7 +315,7 @@ public async Task Aggregator_CollectsAndPublishesAggregate_ViaPostgres()
public async Task FullPipeline_SplitterRouterDlq_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("pipeline-pg", connStr);
diff --git a/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/PostgresRoutingIntegrationTests.cs b/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/PostgresRoutingIntegrationTests.cs
index 0302bd5..0af1616 100644
--- a/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/PostgresRoutingIntegrationTests.cs
+++ b/EnterpriseIntegrationPlatform/tests/TutorialLabs/InfrastructureTests/PostgresRoutingIntegrationTests.cs
@@ -4,7 +4,7 @@
// These tests wire real EIP routing components (ContentBasedRouter, MessageFilter,
// RecipientListRouter, DynamicRouter, Detour) to the real PostgresBrokerEndpoint.
// Each test creates unique topics to prevent cross-test interference.
-// Requires Docker (Aspire Postgres container); tests are skipped when unavailable.
+// Requires Docker (Aspire Postgres container); tests fail when unavailable.
// ============================================================================
using EnterpriseIntegrationPlatform.Contracts;
@@ -30,7 +30,7 @@ public sealed class PostgresRoutingIntegrationTests
public async Task ContentBasedRouter_RoutesOnMessageType_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("cbr-pg", connStr);
@@ -87,7 +87,7 @@ public async Task ContentBasedRouter_RoutesOnMessageType_ViaPostgres()
public async Task ContentBasedRouter_RoutesOnMetadata_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("cbr-meta-pg", connStr);
@@ -133,7 +133,7 @@ public async Task ContentBasedRouter_RoutesOnMetadata_ViaPostgres()
public async Task MessageFilter_FiltersAndRoutes_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("filter-pg", connStr);
@@ -176,7 +176,7 @@ public async Task MessageFilter_FiltersAndRoutes_ViaPostgres()
public async Task RecipientListRouter_FansOutToAllMatching_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("rlist-pg", connStr);
@@ -226,7 +226,7 @@ public async Task RecipientListRouter_FansOutToAllMatching_ViaPostgres()
public async Task DynamicRouter_RegisterAndRoute_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("dynroute-pg", connStr);
@@ -264,7 +264,7 @@ public async Task DynamicRouter_RegisterAndRoute_ViaPostgres()
public async Task Detour_ActivateAndDeactivate_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("detour-pg", connStr);
@@ -309,7 +309,7 @@ public async Task Detour_ActivateAndDeactivate_ViaPostgres()
public async Task ContentBasedRouter_RegexRouting_ViaPostgres()
{
var connStr = await SharedTestAppHost.GetPostgresConnectionStringAsync();
- if (connStr is null) Assert.Ignore("Docker not available");
+ if (connStr is null) { Assert.Fail("Docker not available"); return; }
await using var broker = new PostgresBrokerEndpoint("cbr-regex-pg", connStr);
diff --git a/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Exam.Answers.cs b/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Exam.Answers.cs
index b4de0ef..00b4ce1 100644
--- a/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Exam.Answers.cs
+++ b/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Exam.Answers.cs
@@ -24,7 +24,10 @@ public async Task SetUp()
{
var natsUrl = await SharedTestAppHost.GetNatsUrlAsync();
if (natsUrl is null)
- Assert.Ignore("Docker not available — skipping real broker test");
+ {
+ Assert.Fail("Docker not available — skipping real broker test");
+ return;
+ }
_natsUrl = natsUrl;
_broker = new NatsBrokerEndpoint("broker", _natsUrl);
diff --git a/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Exam.cs b/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Exam.cs
index 5b1fb4a..9a5237f 100644
--- a/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Exam.cs
+++ b/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Exam.cs
@@ -31,7 +31,10 @@ public async Task SetUp()
{
var natsUrl = await SharedTestAppHost.GetNatsUrlAsync();
if (natsUrl is null)
- Assert.Ignore("Docker not available — skipping real broker test");
+ {
+ Assert.Fail("Docker not available — skipping real broker test");
+ return;
+ }
_natsUrl = natsUrl;
_broker = new NatsBrokerEndpoint("broker", _natsUrl);
diff --git a/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Lab.cs b/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Lab.cs
index 17aec10..29c6ca0 100644
--- a/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Lab.cs
+++ b/EnterpriseIntegrationPlatform/tests/TutorialLabs/Tutorial01/Lab.cs
@@ -34,7 +34,10 @@ public async Task SetUp()
{
var natsUrl = await SharedTestAppHost.GetNatsUrlAsync();
if (natsUrl is null)
- Assert.Ignore("Docker not available — skipping real broker test");
+ {
+ Assert.Fail("Docker not available — skipping real broker test");
+ return;
+ }
_broker = new NatsBrokerEndpoint("broker", natsUrl);
}
From 7fc3028d6aa7834f4b77da6c2192ed0dccf76594 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 9 Apr 2026 11:40:57 +0000
Subject: [PATCH 2/4] Use throw new UnreachableException() instead of return
null! in AspireFixture
Agent-Logs-Url: https://github.com/devstress/My3DLearning/sessions/83390dc6-fc9e-478e-a484-a2d6260821bc
Co-authored-by: devstress <30769729+devstress@users.noreply.github.com>
---
.../tests/TutorialLabs/Infrastructure/AspireFixture.cs | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/EnterpriseIntegrationPlatform/tests/TutorialLabs/Infrastructure/AspireFixture.cs b/EnterpriseIntegrationPlatform/tests/TutorialLabs/Infrastructure/AspireFixture.cs
index bb87c45..60c2fb4 100644
--- a/EnterpriseIntegrationPlatform/tests/TutorialLabs/Infrastructure/AspireFixture.cs
+++ b/EnterpriseIntegrationPlatform/tests/TutorialLabs/Infrastructure/AspireFixture.cs
@@ -10,6 +10,7 @@
// test classes in the assembly. It starts once per test run.
// ============================================================================
+using System.Diagnostics;
using NUnit.Framework;
using TutorialLabs.Infrastructure;
@@ -87,7 +88,7 @@ public static NatsBrokerEndpoint CreateNatsEndpoint(string name)
if (!IsAvailable || NatsUrl is null)
{
Assert.Fail("Docker not available — skipping real broker test");
- return null!; // unreachable — Assert.Fail throws
+ throw new UnreachableException();
}
return new NatsBrokerEndpoint(name, NatsUrl);
}
@@ -101,7 +102,7 @@ public static PostgresBrokerEndpoint CreatePostgresEndpoint(string name)
if (!IsAvailable || PostgresConnectionString is null)
{
Assert.Fail("Docker not available — skipping real Postgres broker test");
- return null!; // unreachable — Assert.Fail throws
+ throw new UnreachableException();
}
return new PostgresBrokerEndpoint(name, PostgresConnectionString);
}
@@ -115,7 +116,7 @@ public static KafkaBrokerEndpoint CreateKafkaEndpoint(string name)
if (!IsAvailable || KafkaBootstrapServers is null)
{
Assert.Fail("Docker not available — skipping real Kafka broker test");
- return null!; // unreachable — Assert.Fail throws
+ throw new UnreachableException();
}
return new KafkaBrokerEndpoint(name, KafkaBootstrapServers);
}
@@ -129,7 +130,7 @@ public static PulsarBrokerEndpoint CreatePulsarEndpoint(string name)
if (!IsAvailable || PulsarServiceUrl is null)
{
Assert.Fail("Docker not available — skipping real Pulsar broker test");
- return null!; // unreachable — Assert.Fail throws
+ throw new UnreachableException();
}
return new PulsarBrokerEndpoint(name, PulsarServiceUrl);
}
From 1664266afed88d6e9013a9ff076c194b18fbae13 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 9 Apr 2026 13:15:40 +0000
Subject: [PATCH 3/4] Fix Kafka CI failures: bypass Aspire TCP proxy with
isProxied: false
Root cause: Aspire's TCP proxy (randomly assigned port 35461) doesn't
properly relay Kafka's binary protocol. ApiVersionRequest consistently
times out through the proxy. Additionally, KAFKA_CFG_ADVERTISED_LISTENERS
hardcodes localhost:29094, which doesn't match the proxy's dynamic port.
Fix: Set isProxied: false on the Kafka endpoint so Docker maps port
29094 directly to container port 9094, bypassing the proxy entirely.
This ensures the advertised listener address matches the actual host port.
Agent-Logs-Url: https://github.com/devstress/My3DLearning/sessions/698b16d3-177d-4c3f-b6b3-e24a19e34316
Co-authored-by: devstress <30769729+devstress@users.noreply.github.com>
---
.../tests/TestAppHost/Program.cs | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/EnterpriseIntegrationPlatform/tests/TestAppHost/Program.cs b/EnterpriseIntegrationPlatform/tests/TestAppHost/Program.cs
index 171a773..c55b981 100644
--- a/EnterpriseIntegrationPlatform/tests/TestAppHost/Program.cs
+++ b/EnterpriseIntegrationPlatform/tests/TestAppHost/Program.cs
@@ -42,10 +42,11 @@
// ── Apache Kafka (via Bitnami image) — high-throughput event streaming ───────
// Uses KRaft mode (no ZooKeeper) for minimal resource footprint in tests.
-// IMPORTANT: The EXTERNAL listener port MUST be pinned (port: 29094) and the
-// KAFKA_CFG_ADVERTISED_LISTENERS EXTERNAL address MUST match, otherwise Kafka
-// advertises the container-internal port in metadata and the Confluent.Kafka
-// producer reconnects to the wrong address after the initial bootstrap.
+// IMPORTANT: isProxied: false is REQUIRED for Kafka. Aspire's TCP proxy does
+// not correctly relay Kafka's binary protocol (ApiVersionRequest times out).
+// With isProxied: false, Docker maps host:29094 → container:9094 directly,
+// and the KAFKA_CFG_ADVERTISED_LISTENERS EXTERNAL address matches the actual
+// host port so that Kafka metadata reconnections work correctly.
var kafka = builder.AddContainer("kafka", "bitnami/kafka", "3.9.0")
.WithEnvironment("KAFKA_CFG_NODE_ID", "0")
.WithEnvironment("KAFKA_CFG_PROCESS_ROLES", "controller,broker")
@@ -56,7 +57,7 @@
.WithEnvironment("KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP", "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT")
.WithEnvironment("KAFKA_CFG_INTER_BROKER_LISTENER_NAME", "PLAINTEXT")
.WithEnvironment("KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE", "true")
- .WithEndpoint(port: 29094, targetPort: 9094, name: "kafka-tcp", scheme: "tcp");
+ .WithEndpoint(port: 29094, targetPort: 9094, name: "kafka-tcp", scheme: "tcp", isProxied: false);
// ── Apache Pulsar — Key_Shared subscription for recipient-keyed distribution ─
// Standalone mode includes broker + bookie + ZooKeeper in a single container.
From 33912e04059f66ec591d85f70d64b1c109714fe8 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 10 Apr 2026 00:40:34 +0000
Subject: [PATCH 4/4] Fix Kafka CI: switch from nonexistent bitnami/kafka:3.9.0
to apache/kafka:3.9.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Root causes:
1. bitnami/kafka:3.9.0 image no longer exists on Docker Hub, so the
container was never created by Aspire DCP
2. Apache Kafka image requires ://:PORT format (not ://0.0.0.0:PORT)
in listeners, otherwise it rejects the config at startup
3. isProxied: false with direct port mapping (host:29092 → container:9092)
is needed because Aspire's ReferenceExpression doesn't resolve the
proxy port in container env vars
Agent-Logs-Url: https://github.com/devstress/My3DLearning/sessions/d337bf73-153e-47f0-b884-a0b2eb881ec6
Co-authored-by: devstress <30769729+devstress@users.noreply.github.com>
---
.../tests/TestAppHost/Program.cs | 40 ++++++++++---------
1 file changed, 22 insertions(+), 18 deletions(-)
diff --git a/EnterpriseIntegrationPlatform/tests/TestAppHost/Program.cs b/EnterpriseIntegrationPlatform/tests/TestAppHost/Program.cs
index c55b981..9cd53bc 100644
--- a/EnterpriseIntegrationPlatform/tests/TestAppHost/Program.cs
+++ b/EnterpriseIntegrationPlatform/tests/TestAppHost/Program.cs
@@ -40,24 +40,28 @@
.WithEnvironment("POSTGRES_PASSWORD", "eip")
.WithEndpoint(targetPort: 5432, name: "postgres-tcp", scheme: "tcp");
-// ── Apache Kafka (via Bitnami image) — high-throughput event streaming ───────
-// Uses KRaft mode (no ZooKeeper) for minimal resource footprint in tests.
-// IMPORTANT: isProxied: false is REQUIRED for Kafka. Aspire's TCP proxy does
-// not correctly relay Kafka's binary protocol (ApiVersionRequest times out).
-// With isProxied: false, Docker maps host:29094 → container:9094 directly,
-// and the KAFKA_CFG_ADVERTISED_LISTENERS EXTERNAL address matches the actual
-// host port so that Kafka metadata reconnections work correctly.
-var kafka = builder.AddContainer("kafka", "bitnami/kafka", "3.9.0")
- .WithEnvironment("KAFKA_CFG_NODE_ID", "0")
- .WithEnvironment("KAFKA_CFG_PROCESS_ROLES", "controller,broker")
- .WithEnvironment("KAFKA_CFG_CONTROLLER_QUORUM_VOTERS", "0@localhost:9093")
- .WithEnvironment("KAFKA_CFG_CONTROLLER_LISTENER_NAMES", "CONTROLLER")
- .WithEnvironment("KAFKA_CFG_LISTENERS", "PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094")
- .WithEnvironment("KAFKA_CFG_ADVERTISED_LISTENERS", "PLAINTEXT://localhost:9092,EXTERNAL://localhost:29094")
- .WithEnvironment("KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP", "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT")
- .WithEnvironment("KAFKA_CFG_INTER_BROKER_LISTENER_NAME", "PLAINTEXT")
- .WithEnvironment("KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE", "true")
- .WithEndpoint(port: 29094, targetPort: 9094, name: "kafka-tcp", scheme: "tcp", isProxied: false);
+// ── Apache Kafka — high-throughput event streaming for T05 and broker tests ──
+// Uses official Apache Kafka image in KRaft mode (no ZooKeeper).
+// IMPORTANT: isProxied: false is required because Kafka's binary protocol needs
+// the advertised listener port to match the actual host port. With Aspire's TCP
+// proxy, the proxy port is dynamic and cannot be injected into the container's
+// KAFKA_ADVERTISED_LISTENERS before startup. Direct Docker port mapping solves
+// this: host:29092 → container:9092, and Kafka advertises localhost:29092.
+// NOTE: Listeners MUST use ://:PORT format (not ://0.0.0.0:PORT) because
+// the Apache Kafka Docker image rejects 0.0.0.0 in advertised.listeners.
+var kafka = builder.AddContainer("kafka", "apache/kafka", "3.9.0")
+ .WithEnvironment("KAFKA_NODE_ID", "1")
+ .WithEnvironment("KAFKA_PROCESS_ROLES", "broker,controller")
+ .WithEnvironment("KAFKA_CONTROLLER_QUORUM_VOTERS", "1@localhost:9093")
+ .WithEnvironment("KAFKA_CONTROLLER_LISTENER_NAMES", "CONTROLLER")
+ .WithEnvironment("KAFKA_LISTENERS", "PLAINTEXT://:9092,CONTROLLER://:9093")
+ .WithEnvironment("KAFKA_ADVERTISED_LISTENERS", "PLAINTEXT://localhost:29092")
+ .WithEnvironment("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP", "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT")
+ .WithEnvironment("KAFKA_INTER_BROKER_LISTENER_NAME", "PLAINTEXT")
+ .WithEnvironment("KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR", "1")
+ .WithEnvironment("KAFKA_AUTO_CREATE_TOPICS_ENABLE", "true")
+ .WithEnvironment("CLUSTER_ID", "eip-test-cluster-001")
+ .WithEndpoint(port: 29092, targetPort: 9092, name: "kafka-tcp", scheme: "tcp", isProxied: false);
// ── Apache Pulsar — Key_Shared subscription for recipient-keyed distribution ─
// Standalone mode includes broker + bookie + ZooKeeper in a single container.