diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml
index f815ce86a..fbf6648c0 100644
--- a/client/transport/grpc/pom.xml
+++ b/client/transport/grpc/pom.xml
@@ -51,6 +51,11 @@
junit-jupiter-api
test
+
+ io.grpc
+ grpc-inprocess
+ test
+
org.mock-server
@@ -59,4 +64,4 @@
-
\ No newline at end of file
+
diff --git a/client/transport/grpc/src/main/java/org/a2aproject/sdk/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/org/a2aproject/sdk/client/transport/grpc/GrpcTransport.java
index 00c86b660..7609bc9d5 100644
--- a/client/transport/grpc/src/main/java/org/a2aproject/sdk/client/transport/grpc/GrpcTransport.java
+++ b/client/transport/grpc/src/main/java/org/a2aproject/sdk/client/transport/grpc/GrpcTransport.java
@@ -256,12 +256,12 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPu
@Nullable ClientCallContext context) throws A2AClientException {
checkNotNullParam("request", request);
checkNotNullParam("taskId", request.taskId());
- checkNotNullParam("id", request.id());
+ String configId = request.id();
org.a2aproject.sdk.grpc.GetTaskPushNotificationConfigRequest grpcRequest = org.a2aproject.sdk.grpc.GetTaskPushNotificationConfigRequest.newBuilder()
.setTaskId(request.taskId())
.setTenant(resolveTenant(request.tenant()))
- .setId(request.id())
+ .setId(configId == null ? "" : configId)
.build();
PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, grpcRequest, agentCard, context);
diff --git a/client/transport/grpc/src/test/java/org/a2aproject/sdk/client/transport/grpc/GrpcTransportTest.java b/client/transport/grpc/src/test/java/org/a2aproject/sdk/client/transport/grpc/GrpcTransportTest.java
new file mode 100644
index 000000000..5fd8363c2
--- /dev/null
+++ b/client/transport/grpc/src/test/java/org/a2aproject/sdk/client/transport/grpc/GrpcTransportTest.java
@@ -0,0 +1,119 @@
+package org.a2aproject.sdk.client.transport.grpc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import io.grpc.ManagedChannel;
+import io.grpc.Server;
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.stub.StreamObserver;
+import org.a2aproject.sdk.grpc.A2AServiceGrpc;
+import org.a2aproject.sdk.grpc.GetTaskPushNotificationConfigRequest;
+import org.a2aproject.sdk.spec.AgentCapabilities;
+import org.a2aproject.sdk.spec.AgentCard;
+import org.a2aproject.sdk.spec.AgentInterface;
+import org.a2aproject.sdk.spec.GetTaskPushNotificationConfigParams;
+import org.a2aproject.sdk.spec.TaskPushNotificationConfig;
+import org.jspecify.annotations.Nullable;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+@Timeout(5)
+class GrpcTransportTest {
+
+ private static final String TASK_ID = "task-1";
+ private static final String CALLBACK_URL = "https://example.com/callback";
+ private static final AgentCard CARD = AgentCard.builder()
+ .name("Test Agent")
+ .description("Agent for gRPC transport tests")
+ .version("1.0.0")
+ .supportedInterfaces(List.of(new AgentInterface("GRPC", "http://localhost")))
+ .capabilities(AgentCapabilities.builder().pushNotifications(true).build())
+ .defaultInputModes(List.of("text"))
+ .defaultOutputModes(List.of("text"))
+ .skills(List.of())
+ .build();
+
+ private final AtomicReference receivedRequest = new AtomicReference<>();
+ private Server server;
+ private ManagedChannel channel;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ String serverName = InProcessServerBuilder.generateName();
+ server = InProcessServerBuilder.forName(serverName)
+ .directExecutor()
+ .addService(new A2AServiceGrpc.A2AServiceImplBase() {
+ @Override
+ public void getTaskPushNotificationConfig(GetTaskPushNotificationConfigRequest request,
+ StreamObserver responseObserver) {
+ receivedRequest.set(request);
+ responseObserver.onNext(org.a2aproject.sdk.grpc.TaskPushNotificationConfig.newBuilder()
+ .setTaskId(request.getTaskId())
+ .setId(request.getId().isEmpty() ? request.getTaskId() : request.getId())
+ .setUrl(CALLBACK_URL)
+ .build());
+ responseObserver.onCompleted();
+ }
+ })
+ .build()
+ .start();
+ channel = InProcessChannelBuilder.forName(serverName).directExecutor().build();
+ }
+
+ @AfterEach
+ void tearDown() throws InterruptedException {
+ channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
+ server.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
+ }
+
+ @Test
+ void testGetPushNotificationConfigWithOmittedId() throws Exception {
+ assertGetPushNotificationConfig(new GetTaskPushNotificationConfigParams(TASK_ID), null, "", "");
+ }
+
+ @Test
+ void testGetPushNotificationConfigWithOmittedIdViaBuilder() throws Exception {
+ assertGetPushNotificationConfig(GetTaskPushNotificationConfigParams.builder().taskId(TASK_ID).build(),
+ null, "", "");
+ }
+
+ @Test
+ void testGetPushNotificationConfigWithEmptyId() throws Exception {
+ assertGetPushNotificationConfig(new GetTaskPushNotificationConfigParams(TASK_ID, ""), null, "", "");
+ }
+
+ @Test
+ void testGetPushNotificationConfigWithExplicitIdAndDefaultTenant() throws Exception {
+ assertGetPushNotificationConfig(new GetTaskPushNotificationConfigParams(TASK_ID, "config-1"),
+ "default-tenant", "config-1", "default-tenant");
+ }
+
+ @Test
+ void testGetPushNotificationConfigWithOmittedIdAndRequestTenant() throws Exception {
+ assertGetPushNotificationConfig(new GetTaskPushNotificationConfigParams(TASK_ID, null, "request-tenant"),
+ "default-tenant", "", "request-tenant");
+ }
+
+ private void assertGetPushNotificationConfig(GetTaskPushNotificationConfigParams params,
+ @Nullable String defaultTenant, String expectedId, String expectedTenant) throws Exception {
+ GrpcTransport transport = new GrpcTransport(channel, CARD, defaultTenant, null);
+ TaskPushNotificationConfig result = transport.getTaskPushNotificationConfiguration(params, null);
+
+ GetTaskPushNotificationConfigRequest request = receivedRequest.get();
+ assertNotNull(request);
+ assertEquals(TASK_ID, request.getTaskId());
+ assertEquals(expectedId, request.getId());
+ assertEquals(expectedTenant, request.getTenant());
+ assertEquals(expectedId.isEmpty() ? TASK_ID : expectedId, result.id());
+ assertEquals(TASK_ID, result.taskId());
+ assertEquals(CALLBACK_URL, result.url());
+ }
+}
diff --git a/client/transport/rest/src/test/java/org/a2aproject/sdk/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/org/a2aproject/sdk/client/transport/rest/RestTransportTest.java
index d0ba6da72..73e45a330 100644
--- a/client/transport/rest/src/test/java/org/a2aproject/sdk/client/transport/rest/RestTransportTest.java
+++ b/client/transport/rest/src/test/java/org/a2aproject/sdk/client/transport/rest/RestTransportTest.java
@@ -340,6 +340,33 @@ public void testGetTaskPushNotificationConfiguration() throws Exception {
assertEquals("jwt", authenticationInfo.scheme());
}
+ @Test
+ public void testGetTaskPushNotificationConfigurationWithOmittedId() throws Exception {
+ assertGetTaskPushNotificationConfigurationWithDefaultId(
+ new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64"));
+ }
+
+ @Test
+ public void testGetTaskPushNotificationConfigurationWithEmptyId() throws Exception {
+ assertGetTaskPushNotificationConfigurationWithDefaultId(
+ new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", ""));
+ }
+
+ private void assertGetTaskPushNotificationConfigurationWithDefaultId(
+ GetTaskPushNotificationConfigParams params) throws Exception {
+ this.server.when(request()
+ .withMethod("GET")
+ .withPath("/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/"))
+ .respond(response()
+ .withStatusCode(200)
+ .withBody(GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE));
+
+ RestTransport client = new RestTransport(CARD);
+ TaskPushNotificationConfig config = client.getTaskPushNotificationConfiguration(params, null);
+
+ assertEquals("https://example.com/callback", config.url());
+ }
+
/**
* Test of listTaskPushNotificationConfigurations method, of class JSONRestTransport.
*/