This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new aa8e54ee7833 CAMEL-24742: camel-opa - fix OpaSecurityPolicyWasmTest
after the committed WASM fixtures were removed (#26987)
aa8e54ee7833 is described below
commit aa8e54ee783320553f81c19bbaf4e0d05d3c757b
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Sep 28 14:46:30 2026 +0200
CAMEL-24742: camel-opa - fix OpaSecurityPolicyWasmTest after the committed
WASM fixtures were removed (#26987)
OpaSecurityPolicyWasmTest (CAMEL-24830) was written against the committed
authz.wasm and merged a minute before CAMEL-24742 deleted it, so every test
in the class failed at context startup once both were on main.
Split it the way CAMEL-24742 split the producer tests: the three tests that
evaluate a policy move to OpaSecurityPolicyWasmIT, which compiles authz.rego
with OpaWasmBundleBuilder, and the five startup-failure tests stay unit
tests. Also drop the dead classpath:authz.wasm reference in
OpaBatchEvaluationTest, and point two comments at OpaWasmIT instead of the
removed OpaWasmEvaluatorTest. Test code only.
Closes #26987
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Andrea Cosentino <[email protected]>
---
.../component/opa/OpaBatchEvaluationTest.java | 3 +-
.../java/org/apache/camel/component/opa/OpaIT.java | 2 +-
...yWasmTest.java => OpaSecurityPolicyWasmIT.java} | 113 ++++++---------------
.../opa/security/OpaSecurityPolicyWasmTest.java | 79 ++------------
.../src/test/resources/wasm-data/roles.rego | 2 +-
5 files changed, 44 insertions(+), 155 deletions(-)
diff --git
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaBatchEvaluationTest.java
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaBatchEvaluationTest.java
index ab5e51c91135..f2be6321d2f4 100644
---
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaBatchEvaluationTest.java
+++
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaBatchEvaluationTest.java
@@ -125,8 +125,9 @@ public class OpaBatchEvaluationTest extends
CamelTestSupport {
@Test
void rejectsBatchInWasmModeAtStartup() {
+ // rejected before the bundle is resolved, so the location here is
never opened
assertThatThrownBy(() -> context.getEndpoint(
- "opa:" + PATH +
"?evaluationMode=wasm&policyBundle=classpath:authz.wasm&batch=true").start())
+ "opa:" + PATH +
"?evaluationMode=wasm&policyBundle=file:unused.wasm&batch=true").start())
.isInstanceOf(Exception.class)
.hasMessageContaining("batch");
}
diff --git
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaIT.java
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaIT.java
index c2f7b1dc3720..1e5da310a5cd 100644
---
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaIT.java
+++
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaIT.java
@@ -126,7 +126,7 @@ public class OpaIT extends CamelTestSupport {
@Test
void failsClosedOnAnUndefinedDecision() {
// authz/strict_allow has no default, so OPA answers with an empty
result rather than false. The WASM
- // engine sees the same rule as an empty result array -
OpaWasmEvaluatorTest asserts the same outcome.
+ // engine sees the same rule as an empty result array - OpaWasmIT
asserts the same outcome.
Exchange out = template.request(opa("authz/strict_allow"), e ->
e.getMessage().setHeader("user", "mallory"));
assertThat(out.getException()).isInstanceOf(OpaPolicyEvaluationException.class);
diff --git
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/security/OpaSecurityPolicyWasmTest.java
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/security/OpaSecurityPolicyWasmIT.java
similarity index 52%
copy from
components/camel-opa/src/test/java/org/apache/camel/component/opa/security/OpaSecurityPolicyWasmTest.java
copy to
components/camel-opa/src/test/java/org/apache/camel/component/opa/security/OpaSecurityPolicyWasmIT.java
index 2de9e8da8c09..38f0d5aa5b0b 100644
---
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/security/OpaSecurityPolicyWasmTest.java
+++
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/security/OpaSecurityPolicyWasmIT.java
@@ -16,17 +16,23 @@
*/
package org.apache.camel.component.opa.security;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.List;
import org.apache.camel.CamelAuthorizationException;
import org.apache.camel.CamelExecutionException;
-import org.apache.camel.RuntimeCamelException;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.health.HealthCheck;
import org.apache.camel.health.HealthCheckRegistry;
+import org.apache.camel.test.infra.opa.services.OpaWasmBundleBuilder;
import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -35,16 +41,40 @@ import static
org.assertj.core.api.Assertions.assertThatThrownBy;
* {@link OpaSecurityPolicy} enforcing a route with an in-process WebAssembly
bundle. The decision contract is the same
* as the REST engine - a match proceeds, a non-match throws {@code
CamelAuthorizationException} - and no server
* readiness check is registered, because the policy is evaluated in-process
(CAMEL-24830).
+ * <p/>
+ * The bundle is compiled from the same {@code authz.rego} that {@code OpaIT}
uploads to a real OPA server rather than
+ * committed beside it (CAMEL-24742). The startup failures that never need a
bundle to evaluate stay unit tests in
+ * {@link OpaSecurityPolicyWasmTest}.
*/
-public class OpaSecurityPolicyWasmTest extends CamelTestSupport {
+public class OpaSecurityPolicyWasmIT extends CamelTestSupport {
+
+ @TempDir
+ static Path bundles;
+
+ private static String bundle;
private final OpaSecurityPolicy wasmPolicy = new OpaSecurityPolicy();
private final OpaSecurityPolicy restPolicy = new OpaSecurityPolicy();
+ @BeforeAll
+ static void compileBundle() throws Exception {
+ String rego;
+ try (InputStream in =
OpaSecurityPolicyWasmIT.class.getResourceAsStream("/authz.rego")) {
+ if (in == null) {
+ throw new IllegalStateException("Test resource not found on
the classpath: /authz.rego");
+ }
+ rego = new String(in.readAllBytes(), StandardCharsets.UTF_8);
+ }
+ // the policy's entrypoint defaults to its policy path, so that is the
one rule the bundle has to expose
+ Path path = bundles.resolve("authz-bundle.tar.gz");
+ Files.write(path, OpaWasmBundleBuilder.build("authz.rego", rego,
"authz/allow"));
+ bundle = "file:" + path.toAbsolutePath();
+ }
+
@Override
protected RouteBuilder createRouteBuilder() {
wasmPolicy.setEvaluationMode("wasm");
- wasmPolicy.setPolicyBundle("classpath:authz.wasm");
+ wasmPolicy.setPolicyBundle(bundle);
wasmPolicy.setPolicyPath("authz/allow");
// a rest-mode policy is the positive control for the readiness-check
assertion: it registers a check, the
@@ -97,81 +127,4 @@ public class OpaSecurityPolicyWasmTest extends
CamelTestSupport {
assertThat(checks).hasSize(1);
assertThat(checks.get(0).getId()).startsWith("security-policy:opa-").contains("opa-rest");
}
-
- @Test
- void failsRouteStartOnABundleThatLoadsButIsNotAValidModule() {
- // OpaWasmEvaluator borrows an instance at startup so a broken bundle
fails fast, and beforeWrap - which cannot
- // throw a checked exception - must surface that rather than swallow
it, or a route would start and then
- // authorize nothing. authz.rego is the Rego source: it loads as bytes
but is not a compiled wasm module.
- OpaSecurityPolicy corrupt = new OpaSecurityPolicy();
- corrupt.setEvaluationMode("wasm");
- corrupt.setPolicyBundle("classpath:authz.rego");
- corrupt.setPolicyPath("authz/allow");
-
- // OpaPolicy rejects the module during the warmup borrow with an
unchecked exception, which buildEvaluator's
- // catch(RuntimeException) rethrows as-is; reaching the caller of
addRoutes is what proves the route did not
- // start (a first-exchange failure would not surface here).
- assertThatThrownBy(() -> context.addRoutes(routeWith(corrupt)))
- .isInstanceOf(RuntimeException.class);
- }
-
- @Test
- void failsRouteStartWhenTheBundleResourceCannotBeLoaded() {
- // a bundle location that resolves to nothing is a checked failure in
loadPolicy, which beforeWrap wraps; the
- // wrapper message is what proves the failure surfaced at startup
rather than being swallowed
- OpaSecurityPolicy missing = new OpaSecurityPolicy();
- missing.setEvaluationMode("wasm");
- missing.setPolicyBundle("classpath:does-not-exist.wasm");
- missing.setPolicyPath("authz/allow");
-
- assertThatThrownBy(() -> context.addRoutes(routeWith(missing)))
- .isInstanceOf(RuntimeCamelException.class)
- .hasMessageContaining("Could not load the wasm policy bundle");
- }
-
- @Test
- void failsRouteStartOnAnUnknownEvaluationMode() {
- // this path lives entirely in the policy's buildEvaluator and is
covered by no endpoint test
- OpaSecurityPolicy bogus = new OpaSecurityPolicy();
- bogus.setEvaluationMode("bogus");
- bogus.setPolicyPath("authz/allow");
-
- assertThatThrownBy(() -> context.addRoutes(routeWith(bogus)))
- .hasRootCauseInstanceOf(IllegalArgumentException.class)
- .hasMessageContaining("Unknown evaluationMode");
- }
-
- @Test
- void failsRouteStartWhenNoWasmBundleIsConfigured() {
- // buildEvaluator forwards to OpaWasmEvaluator.create, so its
validation applies through the policy too
- OpaSecurityPolicy noBundle = new OpaSecurityPolicy();
- noBundle.setEvaluationMode("wasm");
- noBundle.setPolicyPath("authz/allow");
-
- assertThatThrownBy(() -> context.addRoutes(routeWith(noBundle)))
- .hasRootCauseInstanceOf(IllegalArgumentException.class)
- .hasMessageContaining("policyBundle is required");
- }
-
- @Test
- void failsRouteStartOnAPoolSizeBelowOne() {
- OpaSecurityPolicy badPool = new OpaSecurityPolicy();
- badPool.setEvaluationMode("wasm");
- badPool.setPolicyBundle("classpath:authz.wasm");
- badPool.setPolicyPath("authz/allow");
- badPool.setPoolSize(0);
-
- assertThatThrownBy(() -> context.addRoutes(routeWith(badPool)))
- .hasRootCauseInstanceOf(IllegalArgumentException.class)
- .hasMessageContaining("poolSize must be at least 1");
- }
-
- private static RouteBuilder routeWith(OpaSecurityPolicy policy) {
- return new RouteBuilder() {
- @Override
- public void configure() {
- from("direct:probe").policy(policy).to("mock:never");
- }
- };
- }
}
diff --git
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/security/OpaSecurityPolicyWasmTest.java
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/security/OpaSecurityPolicyWasmTest.java
index 2de9e8da8c09..43bcdb637cb9 100644
---
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/security/OpaSecurityPolicyWasmTest.java
+++
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/security/OpaSecurityPolicyWasmTest.java
@@ -16,88 +16,22 @@
*/
package org.apache.camel.component.opa.security;
-import java.util.List;
-
-import org.apache.camel.CamelAuthorizationException;
-import org.apache.camel.CamelExecutionException;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.mock.MockEndpoint;
-import org.apache.camel.health.HealthCheck;
-import org.apache.camel.health.HealthCheckRegistry;
import org.apache.camel.test.junit6.CamelTestSupport;
import org.junit.jupiter.api.Test;
-import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
- * {@link OpaSecurityPolicy} enforcing a route with an in-process WebAssembly
bundle. The decision contract is the same
- * as the REST engine - a match proceeds, a non-match throws {@code
CamelAuthorizationException} - and no server
- * readiness check is registered, because the policy is evaluated in-process
(CAMEL-24830).
+ * The {@code wasm} misconfigurations that fail an {@link OpaSecurityPolicy}
route at startup.
+ * <p/>
+ * These stay unit tests deliberately: none of them needs a bundle that loads
and evaluates, so they need no compiled
+ * artifact and no container. Everything that evaluates a policy lives in
{@link OpaSecurityPolicyWasmIT}, where the
+ * bundle is compiled from the Rego under test rather than committed beside it
(CAMEL-24742).
*/
public class OpaSecurityPolicyWasmTest extends CamelTestSupport {
- private final OpaSecurityPolicy wasmPolicy = new OpaSecurityPolicy();
- private final OpaSecurityPolicy restPolicy = new OpaSecurityPolicy();
-
- @Override
- protected RouteBuilder createRouteBuilder() {
- wasmPolicy.setEvaluationMode("wasm");
- wasmPolicy.setPolicyBundle("classpath:authz.wasm");
- wasmPolicy.setPolicyPath("authz/allow");
-
- // a rest-mode policy is the positive control for the readiness-check
assertion: it registers a check, the
- // wasm one must not
- restPolicy.setServerUrl("http://opa-rest:8181");
- restPolicy.setPolicyPath("authz/allow");
-
- return new RouteBuilder() {
- @Override
- public void configure() {
- from("direct:wasm").policy(wasmPolicy).to("mock:allowed");
- from("direct:rest").policy(restPolicy).to("mock:rest");
- }
- };
- }
-
- @Test
- void allowsWhenTheWasmPolicyMatches() throws Exception {
- MockEndpoint allowed = getMockEndpoint("mock:allowed");
- allowed.expectedMessageCount(1);
-
- template.sendBodyAndHeader("direct:wasm", "payload", "user", "alice");
-
- allowed.assertIsSatisfied();
- }
-
- @Test
- void deniesWhenTheWasmPolicyDoesNotMatch() throws Exception {
- MockEndpoint allowed = getMockEndpoint("mock:allowed");
- allowed.expectedMessageCount(0);
-
- assertThatThrownBy(() -> template.sendBodyAndHeader("direct:wasm",
"payload", "user", "mallory"))
- .isInstanceOf(CamelExecutionException.class)
- .hasCauseInstanceOf(CamelAuthorizationException.class);
-
- allowed.assertIsSatisfied();
- }
-
- @Test
- void registersOnlyTheRestPolicysReadinessCheck() {
- HealthCheckRegistry registry = HealthCheckRegistry.get(context);
- assertThat(registry).isNotNull();
- List<HealthCheck> checks = registry.stream()
- .filter(hc -> hc.getId().startsWith("security-policy:opa-"))
- .toList();
-
- // exactly one, and it is the rest policy's - the wasm policy
evaluates in-process with no server to probe.
- // Assert the full ID contract, not just the hostname, so a refactor
of the ID-building logic cannot pass here
- // silently: OpaSecurityPolicyHealthCheck builds it as
"security-policy:opa-" + sanitized serverUrl/policyPath.
- assertThat(checks).hasSize(1);
-
assertThat(checks.get(0).getId()).startsWith("security-policy:opa-").contains("opa-rest");
- }
-
@Test
void failsRouteStartOnABundleThatLoadsButIsNotAValidModule() {
// OpaWasmEvaluator borrows an instance at startup so a broken bundle
fails fast, and beforeWrap - which cannot
@@ -155,9 +89,10 @@ public class OpaSecurityPolicyWasmTest extends
CamelTestSupport {
@Test
void failsRouteStartOnAPoolSizeBelowOne() {
+ // rejected before the bundle is resolved, so the location here is
never opened
OpaSecurityPolicy badPool = new OpaSecurityPolicy();
badPool.setEvaluationMode("wasm");
- badPool.setPolicyBundle("classpath:authz.wasm");
+ badPool.setPolicyBundle("file:unused.wasm");
badPool.setPolicyPath("authz/allow");
badPool.setPoolSize(0);
diff --git a/components/camel-opa/src/test/resources/wasm-data/roles.rego
b/components/camel-opa/src/test/resources/wasm-data/roles.rego
index b7f2f6e63e52..72f9bf8a9320 100644
--- a/components/camel-opa/src/test/resources/wasm-data/roles.rego
+++ b/components/camel-opa/src/test/resources/wasm-data/roles.rego
@@ -20,7 +20,7 @@
# `opa build` packs the data.json sitting beside this file into the bundle,
and a server that
# loads the bundle answers against it. The WebAssembly module carries no data
of its own, so
# camel-opa has to apply the bundle's data to every evaluation for the two to
agree - which is
-# what OpaWasmEvaluatorTest.appliesTheDataDocumentPackedInTheBundle asserts.
+# what OpaWasmIT.appliesTheDataDocumentPackedInTheBundle asserts.
package roles