consumer = (event, card) -> {
+ Task task = null;
+ if (event instanceof TaskEvent te) {
+ task = te.getTask();
+ } else if (event instanceof TaskUpdateEvent tue) {
+ task = tue.getTask();
+ }
+ if (task != null && task.artifacts() != null && !task.artifacts().isEmpty()) {
+ Artifact artifact = task.artifacts().get(0);
+ if (!artifact.parts().isEmpty() && artifact.parts().get(0) instanceof TextPart tp) {
+ artifactTexts.add(tp.text());
+ }
+ }
+ if (task != null && task.status() != null && task.status().state().isFinal()) {
+ done.countDown();
+ }
+ };
+
+ Client client = streaming ? getStreamingClient() : getNonStreamingClient();
+ client.sendMessage(params.build(), List.of(consumer), null, null);
+ if (streaming) {
+ // The streaming client returns as soon as the SSE connection is initiated; wait for
+ // completion. The non-streaming client blocks in sendMessage() until the task is done,
+ // so no latch is needed there.
+ boolean completed = done.await(30, TimeUnit.SECONDS);
+ assertTrue(completed, "Timed out waiting for streaming task to complete");
+ }
+
+ assertFalse(artifactTexts.isEmpty(), "Expected at least one artifact text event");
+ return artifactTexts.get(artifactTexts.size() - 1);
+ }
+
+ private String getWellKnownCardName(String path) {
+ String body = RestAssured.given().when().get(wellKnownUrl(path))
+ .then().statusCode(200).extract().asString();
+ String name = JsonPath.from(body).getString("name");
+ assertNotNull(name, "Expected a card name in: " + body);
+ return name;
+ }
+
+ private String wellKnownUrl(String path) {
+ return "http://localhost:" + serverPort + path;
+ }
+
+ protected Client getStreamingClient() {
+ if (streamingClient == null) {
+ streamingClient = buildClient(true);
+ }
+ return streamingClient;
+ }
+
+ protected Client getNonStreamingClient() {
+ if (nonStreamingClient == null) {
+ nonStreamingClient = buildClient(false);
+ }
+ return nonStreamingClient;
+ }
+
+ private Client buildClient(boolean streaming) {
+ AgentCard card = AgentCard.builder()
+ .name("multitenant-client-card")
+ .description("client-side card")
+ .version("1.0.0")
+ .defaultInputModes(List.of("text"))
+ .defaultOutputModes(List.of("text"))
+ .capabilities(AgentCapabilities.builder().streaming(true).extendedAgentCard(true).build())
+ .skills(List.of())
+ .supportedInterfaces(List.of(new AgentInterface(getTransportProtocol(), getTransportUrl())))
+ .build();
+ ClientBuilder builder = Client.builder(card)
+ .clientConfig(new ClientConfig.Builder().setStreaming(streaming).build());
+ configureTransport(builder);
+ try {
+ return builder.build();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/MultiTenantAgentCardProducer.java b/extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/MultiTenantAgentCardProducer.java
new file mode 100644
index 000000000..f4d57be44
--- /dev/null
+++ b/extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/MultiTenantAgentCardProducer.java
@@ -0,0 +1,115 @@
+package org.a2aproject.sdk.extras.multitenancy.tests;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+import java.util.Properties;
+
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Singleton;
+
+import org.a2aproject.sdk.extras.multitenancy.Tenant;
+import org.a2aproject.sdk.server.ExtendedAgentCard;
+import org.a2aproject.sdk.server.PublicAgentCard;
+import org.a2aproject.sdk.spec.AgentCapabilities;
+import org.a2aproject.sdk.spec.AgentCard;
+import org.a2aproject.sdk.spec.AgentInterface;
+import org.a2aproject.sdk.spec.TransportProtocol;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+/**
+ * Single CDI producer for all six multi-tenant agent cards (default/acme/beta × public/extended).
+ * The transport interface stamped onto every card is chosen from the {@code preferred-transport}
+ * key in {@code /a2a-requesthandler-test.properties}, so one producer serves every transport module.
+ *
+ * Router contract: per-tenant public cards carry {@code @Tenant} only (never {@code @PublicAgentCard}),
+ * per-tenant extended cards carry {@code @Tenant @ExtendedAgentCard}.
+ */
+@Singleton
+public class MultiTenantAgentCardProducer {
+
+ @ConfigProperty(name = "quarkus.http.port", defaultValue = "" + AbstractMultiTenantServerTest.TEST_PORT)
+ int serverPort;
+
+ private final String preferredTransport = loadPreferredTransport();
+
+ @Produces
+ @Singleton
+ @PublicAgentCard
+ public AgentCard publicCard() {
+ return card("Default Agent");
+ }
+
+ @Produces
+ @Singleton
+ @ExtendedAgentCard
+ public AgentCard defaultExtendedCard() {
+ return card("default-extended");
+ }
+
+ @Produces
+ @Singleton
+ @Tenant("acme")
+ public AgentCard acmePublicCard() {
+ return card("Acme Agent");
+ }
+
+ @Produces
+ @Singleton
+ @Tenant("acme")
+ @ExtendedAgentCard
+ public AgentCard acmeExtendedCard() {
+ return card("acme-extended");
+ }
+
+ @Produces
+ @Singleton
+ @Tenant("beta")
+ public AgentCard betaPublicCard() {
+ return card("Beta Agent");
+ }
+
+ @Produces
+ @Singleton
+ @Tenant("beta")
+ @ExtendedAgentCard
+ public AgentCard betaExtendedCard() {
+ return card("beta-extended");
+ }
+
+ private AgentCard card(String name) {
+ String url = TransportProtocol.GRPC.asString().equals(preferredTransport)
+ ? "localhost:" + serverPort
+ : "http://localhost:" + serverPort;
+ List interfaces = List.of(new AgentInterface(preferredTransport, url));
+ return AgentCard.builder()
+ .name(name)
+ .description(name)
+ .version("1.0.0")
+ .defaultInputModes(List.of("text"))
+ .defaultOutputModes(List.of("text"))
+ .capabilities(AgentCapabilities.builder().streaming(true).extendedAgentCard(true).build())
+ .skills(List.of())
+ .supportedInterfaces(interfaces)
+ .build();
+ }
+
+ private static String loadPreferredTransport() {
+ URL url = MultiTenantAgentCardProducer.class.getResource("/a2a-requesthandler-test.properties");
+ if (url == null) {
+ throw new IllegalStateException("Missing /a2a-requesthandler-test.properties on the test classpath");
+ }
+ Properties properties = new Properties();
+ try (InputStream in = url.openStream()) {
+ properties.load(in);
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to read /a2a-requesthandler-test.properties", e);
+ }
+ String preferredTransport = properties.getProperty("preferred-transport");
+ if (preferredTransport == null || preferredTransport.isBlank()) {
+ throw new IllegalStateException("preferred-transport not set in /a2a-requesthandler-test.properties");
+ }
+ return preferredTransport;
+ }
+}
diff --git a/tests/multitenancy/test-common/src/main/java/org/a2aproject/sdk/tests/multitenancy/MultiTenantAgentExecutorProducer.java b/extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/MultiTenantAgentExecutorProducer.java
similarity index 73%
rename from tests/multitenancy/test-common/src/main/java/org/a2aproject/sdk/tests/multitenancy/MultiTenantAgentExecutorProducer.java
rename to extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/MultiTenantAgentExecutorProducer.java
index 3872426b6..4e17bb76d 100644
--- a/tests/multitenancy/test-common/src/main/java/org/a2aproject/sdk/tests/multitenancy/MultiTenantAgentExecutorProducer.java
+++ b/extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/MultiTenantAgentExecutorProducer.java
@@ -1,4 +1,4 @@
-package org.a2aproject.sdk.tests.multitenancy;
+package org.a2aproject.sdk.extras.multitenancy.tests;
import java.util.List;
@@ -10,8 +10,8 @@
import org.a2aproject.sdk.spec.TextPart;
/**
- * Shared CDI producer for multi-tenant agent executors used across integration tests.
- * Each executor echoes the tenant name as artifact text, enabling tenant routing verification.
+ * Shared CDI producer for multi-tenant agent executors used across multitenancy integration tests.
+ * Each executor echoes its tenant label as artifact text, enabling tenant-routing verification.
*/
@ApplicationScoped
public class MultiTenantAgentExecutorProducer {
@@ -19,21 +19,21 @@ public class MultiTenantAgentExecutorProducer {
@Produces
@ApplicationScoped
public AgentExecutor defaultExecutor() {
- return tenantEchoExecutor("default");
+ return tenantEchoExecutor(Tenants.DEFAULT_LABEL);
}
@Produces
@ApplicationScoped
@Tenant("acme")
public AgentExecutor acmeExecutor() {
- return tenantEchoExecutor("acme");
+ return tenantEchoExecutor(Tenants.ACME);
}
@Produces
@ApplicationScoped
@Tenant("beta")
public AgentExecutor betaExecutor() {
- return tenantEchoExecutor("beta");
+ return tenantEchoExecutor(Tenants.BETA);
}
private static AgentExecutor tenantEchoExecutor(String label) {
diff --git a/extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/Tenants.java b/extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/Tenants.java
new file mode 100644
index 000000000..b6af7d3f2
--- /dev/null
+++ b/extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/Tenants.java
@@ -0,0 +1,13 @@
+package org.a2aproject.sdk.extras.multitenancy.tests;
+
+/** Shared tenant identifiers and expected executor labels for multitenancy tests. */
+public final class Tenants {
+
+ public static final String ACME = "acme";
+ public static final String BETA = "beta";
+ public static final String UNKNOWN = "unknown-corp";
+ public static final String DEFAULT_LABEL = "default";
+
+ private Tenants() {
+ }
+}
diff --git a/tests/multitenancy/grpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/grpc/TestAuthorizationController.java b/extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/TestAuthorizationController.java
similarity index 60%
rename from tests/multitenancy/grpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/grpc/TestAuthorizationController.java
rename to extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/TestAuthorizationController.java
index c7125d6ce..35bae7f4e 100644
--- a/tests/multitenancy/grpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/grpc/TestAuthorizationController.java
+++ b/extras/multitenancy/tests/test-common/src/main/java/org/a2aproject/sdk/extras/multitenancy/tests/TestAuthorizationController.java
@@ -1,4 +1,4 @@
-package org.a2aproject.sdk.tests.multitenancy.grpc;
+package org.a2aproject.sdk.extras.multitenancy.tests;
import jakarta.annotation.Priority;
import jakarta.enterprise.inject.Alternative;
@@ -10,13 +10,14 @@
import io.quarkus.security.spi.runtime.AuthorizationController;
/**
- * Disables authorization for gRPC multitenancy tests.
+ * Disables authorization for multitenancy tests, across every transport.
*
* The {@code @Authenticated} CDI interceptor checks {@link AuthorizationController#isAuthorizationEnabled()}
- * before enforcing. When disabled, {@code @Authenticated} becomes a no-op, allowing
- * tests to call gRPC endpoints without credentials.
+ * before enforcing. CDI resolves at most one {@code AuthorizationController} bean; declaring this
+ * {@code @Alternative} with a priority above the built-in controller makes CDI pick it instead, so
+ * when disabled, {@code @Authenticated} becomes a no-op and tests can call endpoints without credentials.
*
- * The default is {@code false}. To enforce real authentication in a specific test profile,
+ * The default is {@code false}. To exercise real authentication in a specific test profile,
* set {@code test.authorization.enabled=true}.
*/
@Alternative
diff --git a/extras/multitenancy/tests/test-common/src/main/resources/META-INF/beans.xml b/extras/multitenancy/tests/test-common/src/main/resources/META-INF/beans.xml
new file mode 100644
index 000000000..548d44b72
--- /dev/null
+++ b/extras/multitenancy/tests/test-common/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,6 @@
+
+
+
diff --git a/pom.xml b/pom.xml
index b480303f4..d2408b0b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -635,17 +635,20 @@
reference/multiversion-jsonrpc
reference/multiversion-rest
-
- tests/multitenancy
+
+ extras/multitenancy/tests/test-common
-
- tests/multitenancy/test-common
+
+ extras/multitenancy/tests/rest
- tests/multitenancy/jsonrpc
+ extras/multitenancy/tests/jsonrpc
-
- tests/multitenancy/grpc
+
+ extras/multitenancy/tests/grpc
+
+
+ extras/multitenancy/tests/router-unit
tests/multiversion/jsonrpc
diff --git a/tests/multitenancy/grpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/grpc/MultiTenantAgentCardProducer.java b/tests/multitenancy/grpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/grpc/MultiTenantAgentCardProducer.java
deleted file mode 100644
index cf25c9789..000000000
--- a/tests/multitenancy/grpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/grpc/MultiTenantAgentCardProducer.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.a2aproject.sdk.tests.multitenancy.grpc;
-
-import java.util.List;
-
-import jakarta.enterprise.inject.Produces;
-import jakarta.inject.Singleton;
-
-import org.a2aproject.sdk.extras.multitenancy.Tenant;
-import org.a2aproject.sdk.server.ExtendedAgentCard;
-import org.a2aproject.sdk.server.PublicAgentCard;
-import org.a2aproject.sdk.spec.AgentCard;
-import org.a2aproject.sdk.spec.AgentInterface;
-import org.a2aproject.sdk.spec.TransportProtocol;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-
-@Singleton
-public class MultiTenantAgentCardProducer extends org.a2aproject.sdk.tests.multitenancy.MultiTenantAgentCardProducer {
-
- @ConfigProperty(name = "quarkus.http.port", defaultValue = "8081")
- int serverPort;
-
- @Produces
- @Singleton
- @PublicAgentCard
- public AgentCard publicCard() {
- return card("Default Agent", grpcInterfaces());
- }
-
- @Produces
- @Singleton
- @ExtendedAgentCard
- public AgentCard defaultExtendedCard() {
- return card("Default Agent (extended)", grpcInterfaces());
- }
-
- @Produces
- @Singleton
- @Tenant("acme")
- public AgentCard acmePublicCard() {
- return card("Acme Agent", grpcInterfaces());
- }
-
- @Produces
- @Singleton
- @Tenant("acme")
- @ExtendedAgentCard
- public AgentCard acmeExtendedCard() {
- return card("Acme Agent (extended)", grpcInterfaces());
- }
-
- @Produces
- @Singleton
- @Tenant("beta")
- public AgentCard betaPublicCard() {
- return card("Beta Agent", grpcInterfaces());
- }
-
- @Produces
- @Singleton
- @Tenant("beta")
- @ExtendedAgentCard
- public AgentCard betaExtendedCard() {
- return card("Beta Agent (extended)", grpcInterfaces());
- }
-
- private List grpcInterfaces() {
- return List.of(new AgentInterface(TransportProtocol.GRPC.asString(), "localhost:" + serverPort));
- }
-}
diff --git a/tests/multitenancy/grpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/grpc/MultiTenantGrpcTest.java b/tests/multitenancy/grpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/grpc/MultiTenantGrpcTest.java
deleted file mode 100644
index fb9a41fa7..000000000
--- a/tests/multitenancy/grpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/grpc/MultiTenantGrpcTest.java
+++ /dev/null
@@ -1,147 +0,0 @@
-package org.a2aproject.sdk.tests.multitenancy.grpc;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-import java.util.function.BiConsumer;
-
-import jakarta.inject.Inject;
-
-import org.a2aproject.sdk.client.Client;
-import org.a2aproject.sdk.client.ClientEvent;
-import org.a2aproject.sdk.client.TaskEvent;
-import org.a2aproject.sdk.client.transport.grpc.GrpcTransport;
-import org.a2aproject.sdk.client.transport.grpc.GrpcTransportConfigBuilder;
-import org.a2aproject.sdk.spec.AgentCapabilities;
-import org.a2aproject.sdk.spec.AgentCard;
-import org.a2aproject.sdk.spec.AgentInterface;
-import org.a2aproject.sdk.spec.Message;
-import org.a2aproject.sdk.spec.MessageSendParams;
-import org.a2aproject.sdk.spec.Task;
-import org.a2aproject.sdk.spec.TextPart;
-import org.a2aproject.sdk.spec.TransportProtocol;
-import io.grpc.ManagedChannel;
-import io.grpc.ManagedChannelBuilder;
-import io.quarkus.security.spi.runtime.AuthorizationController;
-import io.quarkus.test.junit.QuarkusTest;
-import org.eclipse.microprofile.config.ConfigProvider;
-import org.jspecify.annotations.Nullable;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-/**
- * Integration tests for multi-tenant executor routing on a gRPC server.
- * Verifies that gRPC SendMessage requests with a {@code tenant} field are routed
- * to the correct {@code AgentExecutor} bean qualified with {@code @Tenant}.
- */
-@QuarkusTest
-public class MultiTenantGrpcTest {
-
- @Inject
- AuthorizationController authorizationController;
-
- private static ManagedChannel channel;
- private static String grpcTarget;
-
- @BeforeAll
- public static void setupChannel() {
- int port = ConfigProvider.getConfig().getValue("quarkus.http.port", Integer.class);
- grpcTarget = "localhost:" + port;
- channel = ManagedChannelBuilder.forTarget(grpcTarget).usePlaintext().build();
- }
-
- @AfterAll
- public static void closeChannel() {
- channel.shutdownNow();
- try {
- channel.awaitTermination(10, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
-
- @Test
- public void authorizationControllerIsTestAlternative() {
- assertInstanceOf(TestAuthorizationController.class, authorizationController,
- "TestAuthorizationController CDI alternative must be active");
- assertFalse(authorizationController.isAuthorizationEnabled(),
- "Authorization must be disabled for multitenancy tests");
- }
-
- @Test
- public void knownTenantRoutesToAcmeExecutor() throws Exception {
- assertEquals("acme", sendMessageAndGetArtifactText("acme"));
- }
-
- @Test
- public void secondTenantRoutesToBetaExecutor() throws Exception {
- assertEquals("beta", sendMessageAndGetArtifactText("beta"));
- }
-
- @Test
- public void unknownTenantFallsBackToDefault() throws Exception {
- assertEquals("default", sendMessageAndGetArtifactText("unknown-corp"));
- }
-
- @Test
- public void nullTenantUsesDefault() throws Exception {
- assertEquals("default", sendMessageAndGetArtifactText(null));
- }
-
- private String sendMessageAndGetArtifactText(@Nullable String tenant) throws Exception {
- AgentCard card = buildGrpcAgentCard();
- MessageSendParams params = buildParams(tenant);
-
- List tasks = new ArrayList<>();
- List> consumers = List.of((event, agentCard) -> {
- if (event instanceof TaskEvent te) {
- tasks.add(te.getTask());
- }
- });
- try (Client client = Client.builder(card)
- .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder()
- .channelFactory(target -> channel))
- .build()) {
- client.sendMessage(params, consumers, null, null);
- }
-
- assertEquals(1, tasks.size(), "Expected exactly one Task in the response");
- Task task = tasks.get(0);
- assertNotNull(task.artifacts(), "Expected artifacts in task");
- assertFalse(task.artifacts().isEmpty(), "Expected at least one artifact");
- return ((TextPart) task.artifacts().get(0).parts().get(0)).text();
- }
-
- private static MessageSendParams buildParams(@Nullable String tenant) {
- Message message = Message.builder()
- .messageId(UUID.randomUUID().toString())
- .role(Message.Role.ROLE_USER)
- .parts(List.of(new TextPart("hello")))
- .build();
- MessageSendParams.Builder builder = MessageSendParams.builder().message(message);
- if (tenant != null) {
- builder.tenant(tenant);
- }
- return builder.build();
- }
-
- private static AgentCard buildGrpcAgentCard() {
- return AgentCard.builder()
- .name("test-agent")
- .description("test")
- .version("1.0.0")
- .defaultInputModes(List.of("text"))
- .defaultOutputModes(List.of("text"))
- .capabilities(AgentCapabilities.builder().streaming(false).build())
- .skills(List.of())
- .supportedInterfaces(List.of(new AgentInterface(TransportProtocol.GRPC.asString(), grpcTarget)))
- .build();
- }
-}
diff --git a/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/MultiTenantAgentCardProducer.java b/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/MultiTenantAgentCardProducer.java
deleted file mode 100644
index c475106af..000000000
--- a/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/MultiTenantAgentCardProducer.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.a2aproject.sdk.tests.multitenancy.jsonrpc;
-
-import java.util.List;
-
-import jakarta.enterprise.inject.Produces;
-import jakarta.inject.Singleton;
-
-import org.a2aproject.sdk.extras.multitenancy.Tenant;
-import org.a2aproject.sdk.server.ExtendedAgentCard;
-import org.a2aproject.sdk.server.PublicAgentCard;
-import org.a2aproject.sdk.spec.AgentCard;
-import org.a2aproject.sdk.spec.AgentInterface;
-import org.a2aproject.sdk.spec.TransportProtocol;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-
-@Singleton
-public class MultiTenantAgentCardProducer extends org.a2aproject.sdk.tests.multitenancy.MultiTenantAgentCardProducer {
-
- @ConfigProperty(name = "quarkus.http.port", defaultValue = "8081")
- int serverPort;
-
- @Produces
- @Singleton
- @PublicAgentCard
- public AgentCard publicCard() {
- return card("Default Agent", jsonrpcInterfaces());
- }
-
- @Produces
- @Singleton
- @ExtendedAgentCard
- public AgentCard defaultExtendedCard() {
- return card("Default Agent (extended)", jsonrpcInterfaces());
- }
-
- @Produces
- @Singleton
- @Tenant("acme")
- public AgentCard acmePublicCard() {
- return card("Acme Agent", jsonrpcInterfaces());
- }
-
- @Produces
- @Singleton
- @Tenant("acme")
- @ExtendedAgentCard
- public AgentCard acmeExtendedCard() {
- return card("Acme Agent (extended)", jsonrpcInterfaces());
- }
-
- @Produces
- @Singleton
- @Tenant("beta")
- public AgentCard betaPublicCard() {
- return card("Beta Agent", jsonrpcInterfaces());
- }
-
- @Produces
- @Singleton
- @Tenant("beta")
- @ExtendedAgentCard
- public AgentCard betaExtendedCard() {
- return card("Beta Agent (extended)", jsonrpcInterfaces());
- }
-
- private List jsonrpcInterfaces() {
- return List.of(new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:" + serverPort));
- }
-}
diff --git a/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/MultiTenantJSONRPCPublicCardTest.java b/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/MultiTenantJSONRPCPublicCardTest.java
deleted file mode 100644
index e795d4cf9..000000000
--- a/tests/multitenancy/jsonrpc/src/test/java/org/a2aproject/sdk/tests/multitenancy/jsonrpc/MultiTenantJSONRPCPublicCardTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.a2aproject.sdk.tests.multitenancy.jsonrpc;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import io.quarkus.test.junit.QuarkusTest;
-import io.restassured.RestAssured;
-import io.restassured.path.json.JsonPath;
-import org.junit.jupiter.api.Test;
-
-/**
- * Verifies that the JSON-RPC transport serves the public agent card endpoints
- * (/.well-known/agent-card.json and /.well-known/{tenant}/agent-card.json)
- * when the REST transport is NOT on the classpath.
- */
-@QuarkusTest
-public class MultiTenantJSONRPCPublicCardTest {
-
- @Test
- public void defaultPublicCardIsServed() {
- String response = RestAssured.given()
- .when().get("/.well-known/agent-card.json")
- .then().statusCode(200)
- .extract().asString();
- assertEquals("Default Agent", JsonPath.from(response).getString("name"));
- }
-
- @Test
- public void acmeTenantPublicCardIsServed() {
- String response = RestAssured.given()
- .when().get("/.well-known/acme/agent-card.json")
- .then().statusCode(200)
- .extract().asString();
- assertEquals("Acme Agent", JsonPath.from(response).getString("name"));
- }
-
- @Test
- public void betaTenantPublicCardIsServed() {
- String response = RestAssured.given()
- .when().get("/.well-known/beta/agent-card.json")
- .then().statusCode(200)
- .extract().asString();
- assertEquals("Beta Agent", JsonPath.from(response).getString("name"));
- }
-
- @Test
- public void unknownTenantReturns404() {
- RestAssured.given()
- .when().get("/.well-known/unknown-corp/agent-card.json")
- .then().statusCode(404);
- }
-}
diff --git a/tests/multitenancy/test-common/pom.xml b/tests/multitenancy/test-common/pom.xml
deleted file mode 100644
index cbded74bc..000000000
--- a/tests/multitenancy/test-common/pom.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
- 4.0.0
-
-
- org.a2aproject.sdk
- a2a-java-sdk-parent
- 1.3.2.Final-SNAPSHOT
- ../../../pom.xml
-
-
- a2a-java-sdk-tests-multitenancy-test-common
-
- jar
-
- Java A2A SDK Tests Multitenancy Test Common
- Shared test infrastructure (CDI producers) for multitenancy integration tests
-
-
- true
-
-
-
-
- ${project.groupId}
- a2a-java-extras-multitenancy
-
-
- ${project.groupId}
- a2a-java-sdk-server-common
-
-
- ${project.groupId}
- a2a-java-sdk-spec
-
-
- jakarta.enterprise
- jakarta.enterprise.cdi-api
-
-
- jakarta.inject
- jakarta.inject-api
-
-
-
diff --git a/tests/multitenancy/test-common/src/main/java/org/a2aproject/sdk/tests/multitenancy/MultiTenantAgentCardProducer.java b/tests/multitenancy/test-common/src/main/java/org/a2aproject/sdk/tests/multitenancy/MultiTenantAgentCardProducer.java
deleted file mode 100644
index c921b161a..000000000
--- a/tests/multitenancy/test-common/src/main/java/org/a2aproject/sdk/tests/multitenancy/MultiTenantAgentCardProducer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.a2aproject.sdk.tests.multitenancy;
-
-import java.util.List;
-
-import jakarta.enterprise.inject.Vetoed;
-
-import org.a2aproject.sdk.spec.AgentCapabilities;
-import org.a2aproject.sdk.spec.AgentCard;
-import org.a2aproject.sdk.spec.AgentInterface;
-
-/**
- * Base helper for building multi-tenant agent cards in integration tests.
- * Not a CDI bean — consuming modules define their own transport-specific card producer.
- * Each module's card producer must declare only the interfaces whose transports are
- * deployed in that module (JSONRPC/HTTP for jsonrpc tests, GRPC for grpc tests).
- */
-@Vetoed
-public class MultiTenantAgentCardProducer {
-
- protected static AgentCard card(String name, List supportedInterfaces) {
- return AgentCard.builder()
- .name(name)
- .description(name)
- .version("1.0.0")
- .defaultInputModes(List.of("text"))
- .defaultOutputModes(List.of("text"))
- .capabilities(AgentCapabilities.builder().streaming(true).extendedAgentCard(true).build())
- .skills(List.of())
- .supportedInterfaces(supportedInterfaces)
- .build();
- }
-}