This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch investigate-and-fix-flaky-aws-eventbridge-localsta in repository https://gitbox.apache.org/repos/asf/camel.git
commit a91f8bf26b05bbd4b5ba997576b1d087bca770d9 Author: Guillaume Nodet <[email protected]> AuthorDate: Mon Jul 27 11:07:04 2026 +0200 CAMEL-22000: Fix flaky EventBridge LocalStack integration tests All EventBridge IT tests share a singleton LocalStack container but use the same hardcoded rule name "firstrule" without cleanup. When test classes run in an unfavorable order, leftover rules from a prior test cause assertions on rule counts or rule state to fail with NPEs or unexpected counts. Add @BeforeEach cleanup in the base test class that removes all rules (and their targets) from the default event bus before each test, ensuring a clean slate regardless of test ordering. This matches the isolation patterns used by stable AWS IT tests (SQS, SNS, S3). Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../localstack/Aws2EventbridgeBase.java | 71 +++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/components/camel-aws/camel-aws2-eventbridge/src/test/java/org/apache/camel/component/aws2/eventbridge/localstack/Aws2EventbridgeBase.java b/components/camel-aws/camel-aws2-eventbridge/src/test/java/org/apache/camel/component/aws2/eventbridge/localstack/Aws2EventbridgeBase.java index 8e19e2ca52e4..e12f55b9cdd4 100644 --- a/components/camel-aws/camel-aws2-eventbridge/src/test/java/org/apache/camel/component/aws2/eventbridge/localstack/Aws2EventbridgeBase.java +++ b/components/camel-aws/camel-aws2-eventbridge/src/test/java/org/apache/camel/component/aws2/eventbridge/localstack/Aws2EventbridgeBase.java @@ -22,20 +22,89 @@ import org.apache.camel.test.infra.aws.common.services.AWSService; import org.apache.camel.test.infra.aws2.clients.AWSSDKClientUtils; import org.apache.camel.test.infra.aws2.services.AWSServiceFactory; import org.apache.camel.test.junit6.CamelTestSupport; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.services.eventbridge.EventBridgeClient; +import software.amazon.awssdk.services.eventbridge.model.DeleteRuleRequest; +import software.amazon.awssdk.services.eventbridge.model.ListRulesRequest; +import software.amazon.awssdk.services.eventbridge.model.ListRulesResponse; +import software.amazon.awssdk.services.eventbridge.model.ListTargetsByRuleRequest; +import software.amazon.awssdk.services.eventbridge.model.ListTargetsByRuleResponse; +import software.amazon.awssdk.services.eventbridge.model.RemoveTargetsRequest; +import software.amazon.awssdk.services.eventbridge.model.Target; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class Aws2EventbridgeBase extends CamelTestSupport { + private static final Logger LOG = LoggerFactory.getLogger(Aws2EventbridgeBase.class); + private static final String DEFAULT_EVENT_BUS = "default"; + @RegisterExtension public static AWSService service = AWSServiceFactory.createSingletonEventBridgeService(); + protected EventBridgeClient eventBridgeClient; + @Override protected CamelContext createCamelContext() throws Exception { CamelContext context = super.createCamelContext(); + eventBridgeClient = AWSSDKClientUtils.newEventBridgeClient(); EventbridgeComponent eventbridgeComponent = context.getComponent("aws2-eventbridge", EventbridgeComponent.class); - eventbridgeComponent.getConfiguration().setEventbridgeClient(AWSSDKClientUtils.newEventBridgeClient()); + eventbridgeComponent.getConfiguration().setEventbridgeClient(eventBridgeClient); return context; } + + /** + * Cleans up all EventBridge rules on the default event bus before each test. This prevents test interference when + * using a singleton LocalStack container shared across test classes — leftover rules from a prior test could cause + * assertions on rule counts or rule state to fail. + */ + @BeforeEach + void cleanUpEventBridgeRules() { + if (eventBridgeClient == null) { + return; + } + try { + ListRulesResponse listResponse = eventBridgeClient.listRules( + ListRulesRequest.builder().eventBusName(DEFAULT_EVENT_BUS).build()); + if (listResponse.hasRules()) { + for (var rule : listResponse.rules()) { + removeTargetsAndDeleteRule(rule.name()); + } + } + } catch (Exception e) { + LOG.warn("Failed to clean up EventBridge rules before test: {}", e.getMessage()); + } + } + + private void removeTargetsAndDeleteRule(String ruleName) { + try { + ListTargetsByRuleResponse targets = eventBridgeClient.listTargetsByRule( + ListTargetsByRuleRequest.builder() + .rule(ruleName) + .eventBusName(DEFAULT_EVENT_BUS) + .build()); + if (targets.hasTargets() && !targets.targets().isEmpty()) { + eventBridgeClient.removeTargets( + RemoveTargetsRequest.builder() + .rule(ruleName) + .ids(targets.targets().stream().map(Target::id).toList()) + .eventBusName(DEFAULT_EVENT_BUS) + .build()); + } + } catch (Exception e) { + LOG.warn("Failed to remove targets for rule {}: {}", ruleName, e.getMessage()); + } + try { + eventBridgeClient.deleteRule( + DeleteRuleRequest.builder() + .name(ruleName) + .eventBusName(DEFAULT_EVENT_BUS) + .build()); + } catch (Exception e) { + LOG.warn("Failed to delete rule {}: {}", ruleName, e.getMessage()); + } + } }
