Skip to content

CAMEL-24618: Fix flaky SpringFileAntPathMatcherRemoteFileFilterTest - #26120

Draft
gnodet wants to merge 1 commit into
apache:mainfrom
gnodet:fix/CAMEL-24618
Draft

CAMEL-24618: Fix flaky SpringFileAntPathMatcherRemoteFileFilterTest#26120
gnodet wants to merge 1 commit into
apache:mainfrom
gnodet:fix/CAMEL-24618

Conversation

@gnodet

@gnodet gnodet commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Problem

SpringFileAntPathMatcherRemoteFileFilterTest.testAntPatchMatcherFilter is flaky with two distinct failure modes:

  1. expected <1> but was <9> — state contamination: The FTP consumer URI had no delete option, so consumed files remained on the FTP server. Since FtpServiceExtension.afterAll() is a no-op (the FTP server is never stopped/reset), files accumulated across test retries, causing the mock to receive duplicate messages.

  2. expected <1> but was <0> — timing race: The consumer had initialDelay=2000ms, and with the default MockEndpoint.resultWaitTime (10s), the assertion could expire before the first poll completed on slow CI environments.

Fix

  • Add delete=true to the FTP consumer URI in the Spring XML context — files are deleted after processing, preventing accumulation across retries
  • Change initialDelay from 2000 to 0 — the consumer starts polling immediately
  • Set resultWaitTime(30000) on the mock endpoint — generous timeout for slow CI

@gnodet
gnodet marked this pull request as draft September 4, 2026 09:50

@utafrali utafrali left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three-line test/XML fix (delete=true, initialDelay=0, 30s wait) is correct and targeted, but the PR bundles unrelated CI/Sonar/Scalpel changes — several of which hardcode gnodet/camel and gnodet_camel project keys, drop a security hardening flag, and remove an INFRA-27808 gate. Split the PR and revert the fork-specific configuration before this can be merged.

Comment thread .github/workflows/sonar-scan.yml Outdated
-Dsonar.pullrequest.base=${{ env.pr_base_ref }}
-Dsonar.pullrequest.key=${{ env.pr_number }}
-Dsonar.pullrequest.github.repository=apache/camel
-Dsonar.pullrequest.github.repository=gnodet/camel

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hardcodes -Dsonar.pullrequest.github.repository=gnodet/camel, -Dsonar.projectKey=gnodet_camel, and -Dsonar.organization=gnodet. If this lands on apache/camel, sonar analysis will publish to your personal fork's Sonar project instead of apache_camel. These look like local testing overrides that shouldn't be committed. Revert to apache/camel, apache_camel, apache.

jobs:
sonar:
# Temporarily disabled until SonarCloud quality gate is adjusted (INFRA-27808)
if: >

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing github.repository == 'apache/camel' from the condition means the workflow will now attempt to run on every fork that receives a workflow_run event. Combined with the hardcoded gnodet/camel project key below, this looks like fork-local plumbing rather than an intentional main-branch change. Keep the repository guard.

Comment thread .github/workflows/pr-build-main.yml Outdated
jobs:
build:
if: github.repository == 'apache/camel'
if: github.repository == 'apache/camel' || github.repository == 'gnodet/camel'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github.repository == 'apache/camel' || github.repository == 'gnodet/camel' hardcodes a personal fork into an apache/camel workflow. If you need to test on your fork, use github.repository_owner != '' or drop the guard locally, not in the upstream file.

Comment thread .github/workflows/sonar-build.yml Outdated

- name: Fetch base branch for Scalpel change detection
run: |
git fetch --deepen=200 2>/dev/null || true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The persist-credentials: false line was removed from the checkout step. This is a hardening default (OSSF Scorecard recommends it for actions/checkout) — restore it unless there is a specific reason later steps need the credential helper.

Comment thread .github/workflows/sonar-build.yml Outdated
build:
# Temporarily disabled until SonarCloud quality gate is adjusted (INFRA-27808)
if: false && github.repository == 'apache/camel'
if: true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if: false && ... was replaced by if: true, dropping both the INFRA-27808 gate and the github.repository == 'apache/camel' guard in one go. The linked ticket comment above still says the workflow is disabled pending SonarCloud quality-gate work — either update/remove the comment and confirm INFRA-27808 is resolved, or keep the repo guard.


<!-- use myFilter as filter to allow setting ANT paths for which files to scan for -->
<endpoint id="myFTPEndpoint" uri="ftp://admin@localhost:${SpringFileAntPathMatcherRemoteFileFilterTest.ftpPort}/antpath?password=admin&amp;recursive=true&amp;delay=10000&amp;initialDelay=2000&amp;filter=#myAntFilter"/>
<endpoint id="myFTPEndpoint" uri="ftp://admin@localhost:${SpringFileAntPathMatcherRemoteFileFilterTest.ftpPort}/antpath?password=admin&amp;recursive=true&amp;delay=10000&amp;initialDelay=0&amp;delete=true&amp;filter=#myAntFilter"/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URI change itself is the right fix, but consider adding readLock=changed (or readLock=none with a comment explaining why it's safe here) alongside delete=true. Without a read lock, on a slow FTP handshake the consumer can begin reading a file that the producer is still writing — historically a source of a different flake class in the FTP itests. If you've already ruled this out for this test, leave it, but a one-line XML comment near delete=true explaining the state-contamination fix would help the next reader.

void testAntPatchMatherFilter() throws Exception {

result.expectedBodiesReceived(expectedBody);
result.setResultWaitTime(30000);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer the timed overload MockEndpoint.assertIsSatisfied(context, 30, TimeUnit.SECONDS) in the assertion below instead of calling setResultWaitTime(30000). It reads more clearly at the assertion site and keeps the wait budget next to the assertion it applies to. Per the project guidelines, mock-based waits should use MockEndpoint's native timed API rather than setter side-effects.

if [ -n "$prId" ]; then
echo "Fetching PR #${prId} diff..."
diff_body=$(fetchDiff "$prId" "$repository")
echo "Computing diff against origin/${GITHUB_BASE_REF:-main}..."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching PR diff computation from the GitHub API to a local git diff merge-base..HEAD is a meaningful behavior change (previously the diff matched exactly what GitHub shows on the PR; now it includes any local merge commits or force-push artifacts differently). Worth calling out in the PR description and testing on a PR that has been rebased/force-pushed — the two diffs can diverge when the base branch has moved forward and the PR hasn't been rebased.

Comment thread .mvn/extensions.xml Outdated
<groupId>eu.maveniverse.maven.scalpel</groupId>
<artifactId>extension3</artifactId>
<version>0.3.0</version>
<version>0.3.3</version>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bumping Scalpel from 0.3.0 to 0.3.3 is unrelated to the flaky-test fix advertised in the PR title. Please split: (1) the CAMEL-24618 test fix, (2) the Scalpel bump + shadow-comparison wiring, (3) the sonar/pr-build workflow changes. Right now this PR mixes three independent changes under a title that only names one.

Comment thread .github/workflows/full-test-suite.yml Outdated
@@ -0,0 +1,255 @@
#

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new 255-line workflow (full-test-suite.yml) is landing under a PR titled "Fix flaky SpringFileAntPathMatcherRemoteFileFilterTest". Split this out — it deserves its own PR, its own JIRA (or none if infra-only), and its own review. Bundling it here hides it from reviewers looking at the ticket scope.

@gnodet
gnodet force-pushed the fix/CAMEL-24618 branch 2 times, most recently from 91325e3 to cbefb7f Compare September 4, 2026 10:43
@github-actions github-actions Bot added the tests label Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • tests/camel-itest

🔬 Scalpel shadow comparison — Scalpel: 1 tested, 0 compile-only — current: 0 all tested

Maveniverse Scalpel detected 1 affected modules (current approach: 0).

⚠️ Modules only in Scalpel (1)
  • camel-itest

Skip-tests mode would test 1 modules (1 direct + 0 downstream), skip tests for 0 (generated code, meta-modules)

Modules Scalpel would test (1)
  • camel-itest

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more


⚙️ View full build and test results

Fix two flakiness root causes:

1. State contamination (expected <1> but was <9>): The FTP consumer had
   no delete option, so files accumulated across test retries since
   FtpServiceExtension.afterAll() is a no-op and the FTP server
   directories are never reset. Added delete=true to the FTP consumer
   URI so files are removed after processing.

2. Timing race (expected <1> but was <0>): The consumer had
   initialDelay=2000ms which, combined with the default
   resultWaitTime, could cause the mock assertion to expire before
   the first poll completed on slow CI. Changed initialDelay to 0
   and set an explicit resultWaitTime of 30 seconds.
@gnodet

gnodet commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all review comments:

  • Unrelated CI/Sonar/Scalpel changes: The PR was rebased onto upstream/main — the diff now contains only the 2 test files. The earlier review was on a stale base that included fork-local CI changes.
  • Use timed assertIsSatisfied: Replaced setResultWaitTime(30000) + result.assertIsSatisfied() with MockEndpoint.assertIsSatisfied(context, 30, TimeUnit.SECONDS) — clearer and keeps the timeout at the assertion site.
  • Add explanatory XML comment: Added a comment above the endpoint explaining why delete=true is needed (state contamination from FtpServiceExtension never resetting the FTP directory).
  • readLock=changed: Not needed here — the producer writes via template.sendBodyAndHeader() which completes synchronously before the consumer polls. The consumer's initialDelay=0 + delay=10000 means the first poll happens after context start, by which point all 5 files are fully written. No partial-read risk.

@apupier
apupier requested a review from utafrali September 4, 2026 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants