Skip to content

Fix #2984: run the Citrus itests against the Kamelets in this working tree - #2987

Merged
oscerd merged 2 commits into
apache:mainfrom
oscerd:ci-issue-2984
Aug 30, 2026
Merged

Fix #2984: run the Citrus itests against the Kamelets in this working tree#2987
oscerd merged 2 commits into
apache:mainfrom
oscerd:ci-issue-2984

Conversation

@oscerd

@oscerd oscerd commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes #2984.

The Citrus integration tests were resolving the Kamelet catalog from a released camel-kamelets artifact rather than the one built in the same run. The suite is the catalog's only behavioural gate, so in practice every Kamelet change was going through CI untested.

How it was found

PR #2978 briefly added this to kafka-sink:

- removeHeader:
    name: CamelKafkaOverrideTopic

tests/.../kafka/kafka-router-pipe.yaml is a Pipe of the shape webhook-source → timestamp-router-action → … → kafka-sink (topic: dummy), and its Citrus assertion consumes from the overridden topic — dummy is deliberately a throwaway. That strip nullifies the override, so the test should have failed. It reported TEST SUCCESS.

Cause

citrus-application.properties sets:

citrus.camel.cli.kamelets.version=${project.version}

Citrus turns that into the JBang system property camel-kamelets.version via ProcessLauncher.withSystemProperty. But Camel JBang consumes that name as a JBang script property, substituted into the //DEPS line of CamelJBang.java:

//DEPS org.apache.camel.kamelets:camel-kamelets:${camel-kamelets.version:4.21.0}

A JVM system property never reaches that substitution, so the declared version had no effect and the run fell back to the released catalog. The integration dumps show it plainly — Kamelets loading from classpath:kamelets/<name>.kamelet.yaml.

Fix

Citrus 5.0.0 also exposes citrus.camel.cli.kamelets.local.dir, which it turns into the Camel CLI's --local-kamelet-dir argument. Pointing that at the repository kamelets/ directory makes catalog resolution deterministic and independent of artifact resolution — Camel orders the file: location ahead of the classpath one, so the working tree always wins.

<kamelets.local.dir>${project.basedir}/../../kamelets</kamelets.local.dir>
citrus.camel.cli.kamelets.local.dir=${kamelets.local.dir}

citrus.camel.cli.kamelets.version is left in place — it still declares intent for the camel-kamelets artifact on the JBang classpath, it simply is not what selects the templates.

Verification

Ran KafkaIT locally, both ways.

1. The catalog under test is now the working tree. Integration dumps changed from classpath:kamelets/… to:

in: file:/…/camel-kamelets/kamelets/kafka-sink.kamelet.yaml
in: file:/…/camel-kamelets/kamelets/timestamp-router-action.kamelet.yaml
in: file:/…/camel-kamelets/kamelets/set-body-action.kamelet.yaml
in: file:/…/camel-kamelets/kamelets/webhook-source.kamelet.yaml

All 3 KafkaIT tests still pass against the working-tree catalog.

2. The gate now catches the regression it previously missed. Re-introducing the exact CamelKafkaOverrideTopic strip from #2978:

✔ SUCCESS (43475ms) kafka-sink-pipe-test
✔ SUCCESS (57647ms) kafka-source-pipe-test
✘ FAILED  (54935ms) kafka-router-pipe-test
TOTAL: 3   PASSED: 2 (66.7%)   FAILED: 1 (33.3%)
BUILD FAILURE

That is the same test, on the same Pipe, that reported success on #2978's head. Working tree restored afterwards — the diff here is two files, 9 added lines.

mvn clean install passes from the repository root.

Note for reviewers

This makes CI stricter, so the first run may surface pre-existing Kamelet/test drift that the released catalog was masking. That would be a real finding rather than a fault of this change, but worth watching on the first green.


Claude Code on behalf of Andrea Cosentino

…orking tree

The integration tests were resolving the Kamelet catalog from a released
camel-kamelets artifact instead of the one built in the same run, so a
Kamelet change could not be caught by its own test.

citrus.camel.cli.kamelets.version maps to the JBang system property
camel-kamelets.version, which Camel JBang consumes as a *script* property
via the //DEPS line in CamelJBang.java:

    //DEPS org.apache.camel.kamelets:camel-kamelets:${camel-kamelets.version:4.21.0}

Citrus passes it with ProcessLauncher.withSystemProperty, which never
reaches that substitution, so the run silently fell back to the released
catalog and the integration dumps showed classpath:kamelets/*.kamelet.yaml.

Citrus 5.0.0 also exposes citrus.camel.cli.kamelets.local.dir, which it
turns into the Camel CLI's --local-kamelet-dir argument. Setting it to the
repository kamelets/ directory makes catalog resolution deterministic and
independent of artifact resolution: Camel puts the file: location ahead of
the classpath one, so the working tree always wins.

Verified locally with KafkaIT:
- dumps now report file:<repo>/kamelets/<name>.kamelet.yaml instead of
  classpath:kamelets/<name>.kamelet.yaml
- re-introducing the CamelKafkaOverrideTopic strip that apache#2978 originally
  carried now makes kafka-router-pipe-test FAIL, where it passed before
  this change

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@oscerd
oscerd requested a review from christophd August 27, 2026 04:50
First regression the restored gate catches, and it was already on main.

apache#2956 changed mail-sink to default protocol: smtps (implicit TLS), which
is right for real deployments. The Citrus mail server this test starts is
plaintext, so the sink can no longer complete a handshake against it and
the test times out waiting on mailServer.inbound.

It went unnoticed because the itests were resolving the released catalog,
where mail-sink still defaulted to smtp -- exactly the blind spot this
branch removes.

Pinning protocol: smtp in the Pipe keeps the secure default for users and
makes the test's plaintext assumption explicit rather than accidental.

MailIT passes locally with this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@oscerd

oscerd commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

The stricter gate immediately caught a live regression on main

The test check on the first push failed — 1 of 62, mail-sink-pipe-test:

✘ TEST FAILED: mail-sink-pipe-test
    MessageTimeoutException: Action timeout after 5000 milliseconds.
    Failed to receive message on endpoint: 'mailServer.inbound'
PASSED: 61 (98.4%)   FAILED: 1 (1.6%)

This is not breakage from this branch. It is a pre-existing defect on main that the stale catalog was hiding — the exact failure mode this PR exists to remove, showing up on its first run.

What happened: #2968 (Fix #2956) changed mail-sink to default protocol: smtps — implicit TLS, and the right default for real deployments. But mail/mailServer.groovy starts a plaintext Citrus mail server:

mail("mailServer")
  .server()
  .port(22222)
  .knownUsers([...])

So since that merge the sink has been unable to complete a handshake against the mock and the test times out. Nobody saw it, because the itests were resolving the released catalog where mail-sink still defaulted to smtp.

Fix: pin the protocol in the test Pipe, with a comment recording why:

      # The Citrus mail server this test starts is a plaintext SMTP server, so
      # the protocol has to be pinned. mail-sink defaults to smtps (implicit
      # TLS) since #2956, which is the right default for real deployments but
      # cannot complete a handshake against the mock.
      protocol: "smtp"

The secure default is untouched for users; the test's plaintext assumption is now explicit instead of accidental. MailIT passes locally:

✔ SUCCESS (16888ms) mail-sink-pipe-test
TOTAL: 1   PASSED: 1 (100.0%)   FAILED: 0 (0.0%)

Two options for the alternative, if reviewers prefer: give the Citrus mail server TLS so the test exercises the shipped smtps default end-to-end. That is the stronger test but a bigger change, and it is a separate concern from restoring the gate — happy to do it as a follow-up.

The PR description already warned this branch might surface masked drift. One in 62 is a better outcome than I expected.


Claude Code on behalf of Andrea Cosentino

@oscerd

oscerd commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Correction, and this PR is more urgent than I said

In my previous comment I described mail-sink-pipe-test as a regression that the stale catalog had been hiding, surfaced by this branch. That is not accurate, and the real picture matters for scheduling this PR.

main is currently red, and has been since 24 Aug. The last four Integration Tests runs on main:

2026-08-25T14:35:59Z  failure  2015bcf8
2026-08-25T09:52:53Z  failure  d6ad23f9
2026-08-25T06:39:29Z  failure  957fd47d
2026-08-24T17:00:37Z  failure  3969f06e

and the failure there is the same one, with the same shape:

TEST FAILED: mail-sink-pipe-test
PASSED: 61 (98.4%)   FAILED: 1 (1.6%)

The first failure follows ff0ac602b (#2968, mail-sink to smtps) merging on 24 Aug. So the breakage was never hidden - it went red on main immediately and stayed red.

What the gap actually hides

Refining the #2984 diagnosis rather than retracting it. The evidence together:

So the catalog under test tracks main's published snapshot rather than the build produced in the same run. The blind spot is precisely PR-level changes: every PR is tested against a catalog that does not contain it, and defects only become visible after merge, on main, where they are most expensive.

That is still exactly what this PR fixes, and it is why --local-kamelet-dir is the right lever - it removes artifact resolution from the question entirely.

Practical consequence

Every open PR is currently red through no fault of its own - #2988, #2989 and #2990 all fail on mail-sink-pipe-test alone, 61/62 passing. This branch is the only green one, because it carries the one-line test fix.

Merging this unblocks main and every open PR at once. If it would be preferable to land the mail-sink-pipe.yaml pin as its own tiny PR first and rebase this one behind it, say the word and I will split it - I did not want to fragment the change without asking.


Claude Code on behalf of Andrea Cosentino

@oscerd
oscerd merged commit 91e877b into apache:main Aug 30, 2026
5 checks passed
@oscerd oscerd mentioned this pull request Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Citrus integration tests do not exercise the Kamelets built in the same run

1 participant