From 712d4f8eaccc9ac95384a900b3229e0323c8723f Mon Sep 17 00:00:00 2001 From: Jonathan Jamroga Date: Thu, 29 Oct 2026 21:20:08 -0400 Subject: [PATCH 1/4] test(a2a): name HTTP access cases as behavior scenarios Signed-off-by: Jonathan Jamroga --- go/core/internal/a2agateway/http_test.go | 48 ++++++++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/go/core/internal/a2agateway/http_test.go b/go/core/internal/a2agateway/http_test.go index 297c3a97c9..bb10d034f5 100644 --- a/go/core/internal/a2agateway/http_test.go +++ b/go/core/internal/a2agateway/http_test.go @@ -354,14 +354,46 @@ func TestHTTPGatewayRejectsInvalidAccess(t *testing.T) { authzErr error want int }{ - {name: "card needs authentication", method: "GET", path: gatewayTestAgent + a2asrv.WellKnownAgentCardPath, want: 401}, - {name: "RPC needs authentication", method: "POST", path: gatewayTestAgent, want: 401}, - {name: "card needs authorization", method: "GET", path: gatewayTestAgent + a2asrv.WellKnownAgentCardPath, authorized: true, authzErr: errors.New("denied"), want: 403}, - {name: "missing Agent", method: "GET", path: gatewayTestAgent + a2asrv.WellKnownAgentCardPath, authorized: true, storeErr: database.ErrNotFound, want: 404}, - {name: "internal errors are hidden", method: "GET", path: gatewayTestAgent + a2asrv.WellKnownAgentCardPath, authorized: true, storeErr: errors.New("private connection details"), want: 500}, - {name: "invalid session ID", method: "GET", path: "invalid!/assistant" + a2asrv.WellKnownAgentCardPath, authorized: true, want: 400}, - {name: "unknown route", method: "GET", path: gatewayTestAgent + "/unknown", authorized: true, want: 404}, - {name: "card only supports reads", method: "POST", path: gatewayTestAgent + a2asrv.WellKnownAgentCardPath, authorized: true, want: 405}, + { + name: "Given a protected agent card, when an unauthenticated caller requests it, assert that the response is unauthorized", + method: http.MethodGet, path: gatewayTestAgent + a2asrv.WellKnownAgentCardPath, + want: http.StatusUnauthorized, + }, + { + name: "Given an agent RPC, when an unauthenticated caller posts to it, assert that the response is unauthorized", + method: http.MethodPost, path: gatewayTestAgent, + want: http.StatusUnauthorized, + }, + { + name: "Given denied agent access, when an authenticated caller requests the card, assert that the response is forbidden", + method: http.MethodGet, path: gatewayTestAgent + a2asrv.WellKnownAgentCardPath, + authorized: true, authzErr: errors.New("denied"), want: http.StatusForbidden, + }, + { + name: "Given a missing Agent, when an authenticated caller requests its card, assert that the response is not found", + method: http.MethodGet, path: gatewayTestAgent + a2asrv.WellKnownAgentCardPath, + authorized: true, storeErr: database.ErrNotFound, want: http.StatusNotFound, + }, + { + name: "Given a failed Agent lookup, when an authenticated caller requests a card, assert that the response hides private details", + method: http.MethodGet, path: gatewayTestAgent + a2asrv.WellKnownAgentCardPath, + authorized: true, storeErr: errors.New("private connection details"), want: http.StatusInternalServerError, + }, + { + name: "Given an invalid Agent path, when an authenticated caller requests the card, assert that the response is bad request", + method: http.MethodGet, path: "invalid!/assistant" + a2asrv.WellKnownAgentCardPath, + authorized: true, want: http.StatusBadRequest, + }, + { + name: "Given an unknown route, when an authenticated caller requests it, assert that the response is not found", + method: http.MethodGet, path: gatewayTestAgent + "/unknown", + authorized: true, want: http.StatusNotFound, + }, + { + name: "Given a protected agent card, when an authenticated caller posts to it, assert that the method is not allowed", + method: http.MethodPost, path: gatewayTestAgent + a2asrv.WellKnownAgentCardPath, + authorized: true, want: http.StatusMethodNotAllowed, + }, } { t.Run(test.name, func(t *testing.T) { store := &gatewayTestStore{session: gatewayTestSession(), err: test.storeErr} From 1d94b0df3d2d66524d493fd49450546154705b45 Mon Sep 17 00:00:00 2001 From: Jonathan Jamroga Date: Thu, 29 Oct 2026 21:23:24 -0400 Subject: [PATCH 2/4] test(a2a): name remaining HTTP gateway scenarios Signed-off-by: Jonathan Jamroga --- go/core/internal/a2agateway/http_test.go | 42 +++++++++++++----------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/go/core/internal/a2agateway/http_test.go b/go/core/internal/a2agateway/http_test.go index bb10d034f5..ce6704e98c 100644 --- a/go/core/internal/a2agateway/http_test.go +++ b/go/core/internal/a2agateway/http_test.go @@ -92,7 +92,7 @@ func newGatewayTestTransport(t *testing.T, address string, protocol a2atype.Tran func TestGatewayCreatesConversationAndReusesInitialMessage(t *testing.T) { for _, protocol := range []a2atype.TransportProtocol{a2atype.TransportProtocolJSONRPC, a2atype.TransportProtocolGRPC} { - t.Run(string(protocol), func(t *testing.T) { + t.Run(fmt.Sprintf("Given %s transport, when an initial message is retried and a new one is sent, assert that only the retry reuses its conversation", protocol), func(t *testing.T) { store := &gatewayTestStore{created: map[string]*apiv1.Session{}} runtime := &gatewayTestRuntime{} authorizer := &gatewayTestAuthorizer{} @@ -145,12 +145,12 @@ func TestGatewayMissingConversationAcrossTransports(t *testing.T) { authErr error want error }{ - {name: "missing context", storeErr: database.ErrNotFound, want: a2atype.ErrTaskNotFound}, - {name: "expired task", taskErr: database.ErrNotFound, storeErr: database.ErrNotFound, want: a2atype.ErrTaskNotFound}, - {name: "permission denied", authErr: errors.New("denied"), want: a2atype.ErrUnauthorized}, - {name: "store failure", storeErr: errors.New("private database details"), want: a2atype.ErrInternalError}, + {name: "Given a missing context over %s, when task methods are called, assert that the task is not found", storeErr: database.ErrNotFound, want: a2atype.ErrTaskNotFound}, + {name: "Given an expired task over %s, when task methods are called, assert that the task is not found", taskErr: database.ErrNotFound, storeErr: database.ErrNotFound, want: a2atype.ErrTaskNotFound}, + {name: "Given denied access over %s, when task methods are called, assert that the caller is unauthorized", authErr: errors.New("denied"), want: a2atype.ErrUnauthorized}, + {name: "Given a failed store over %s, when task methods are called, assert that an internal error is returned", storeErr: errors.New("private database details"), want: a2atype.ErrInternalError}, } { - t.Run(string(protocol)+"/"+test.name, func(t *testing.T) { + t.Run(fmt.Sprintf(test.name, protocol), func(t *testing.T) { store := &gatewayTestStore{session: gatewayTestSession(), err: test.storeErr, taskErr: test.taskErr} dialer := &gatewayTestDialer{} gateway := newTestGateway(store, &gatewayTestAuthorizer{err: test.authErr}, dialer, "") @@ -200,12 +200,12 @@ func TestHTTPAgentURLIsAuthoritative(t *testing.T) { name, path, tenant string want error }{ - {name: "URL only", path: gatewayTestAgent}, - {name: "trailing slash", path: gatewayTestAgent + "/"}, - {name: "matching tenant", path: gatewayTestAgent, tenant: gatewayTestAgent}, - {name: "conflicting tenant", path: gatewayTestAgent, tenant: "team-a/other", want: a2atype.ErrInvalidRequest}, - {name: "invalid URL", path: "INVALID/assistant", want: a2atype.ErrInvalidRequest}, - {name: "Session belongs to another Agent", path: "team-a/other", want: a2atype.ErrUnauthorized}, + {name: "Given an Agent URL without a tenant, when task methods are called, assert that the URL identifies the Agent", path: gatewayTestAgent}, + {name: "Given an Agent URL with a trailing slash, when task methods are called, assert that the URL identifies the Agent", path: gatewayTestAgent + "/"}, + {name: "Given a tenant matching the Agent URL, when task methods are called, assert that routing succeeds", path: gatewayTestAgent, tenant: gatewayTestAgent}, + {name: "Given a tenant conflicting with the Agent URL, when task methods are called, assert that the request is invalid", path: gatewayTestAgent, tenant: "team-a/other", want: a2atype.ErrInvalidRequest}, + {name: "Given an invalid Agent URL, when task methods are called, assert that the request is invalid", path: "INVALID/assistant", want: a2atype.ErrInvalidRequest}, + {name: "Given a URL for another Agent, when task methods are called, assert that the caller is unauthorized", path: "team-a/other", want: a2atype.ErrUnauthorized}, } { t.Run(tc.name, func(t *testing.T) { store := &gatewayTestStore{session: gatewayTestSession()} @@ -421,11 +421,11 @@ func TestGatewayReadOnlySharesAcrossTransports(t *testing.T) { canRead bool denied bool }{ - {name: "read only", sessionID: gatewayTestID, canRead: true}, - {name: "policy denied", sessionID: gatewayTestID, denied: true}, - {name: "other session", sessionID: "12345678-1234-4234-8234-123456789abc"}, - {name: "expired", storeErr: database.ErrNotFound, wantStatus: 403}, - {name: "store unavailable", storeErr: errors.New("database credentials"), wantStatus: 500}, + {name: "Given a valid read-only share, when task methods are called, assert that reads work and writes are rejected", sessionID: gatewayTestID, canRead: true}, + {name: "Given a share denied by policy, when task methods are called, assert that reads and writes are rejected", sessionID: gatewayTestID, denied: true}, + {name: "Given a share for another session, when task methods are called, assert that reads and writes are rejected", sessionID: "12345678-1234-4234-8234-123456789abc"}, + {name: "Given an expired share, when task methods are called, assert that reads and writes are rejected", storeErr: database.ErrNotFound, wantStatus: 403}, + {name: "Given a failed share store, when task methods are called, assert that reads and writes are rejected", storeErr: errors.New("database credentials"), wantStatus: 500}, } { t.Run(test.name, func(t *testing.T) { store := &gatewayTestStore{session: gatewayTestSession(), task: &a2atype.Task{ID: "task", ContextID: gatewayTestContextID, Status: a2atype.TaskStatus{State: a2atype.TaskStateCompleted}}} @@ -521,7 +521,11 @@ func startCoreTestServer(t *testing.T, gateway a2asrv.RequestHandler, shares ses func TestEmbeddedPushAcrossTransports(t *testing.T) { for _, protocol := range []a2atype.TransportProtocol{a2atype.TransportProtocolJSONRPC, a2atype.TransportProtocolGRPC} { for _, streaming := range []bool{false, true} { - t.Run(fmt.Sprintf("%s/stream=%t", protocol, streaming), func(t *testing.T) { + action := "a message is sent" + if streaming { + action = "a message is streamed" + } + t.Run(fmt.Sprintf("Given %s transport, when %s with a push config and retried, assert that push is registered without redispatch", protocol, action), func(t *testing.T) { store := &gatewayTestStore{created: map[string]*apiv1.Session{}} runtime := &gatewayTestRuntime{} runtime.onStream = func(req *a2atype.SendMessageRequest) { @@ -597,7 +601,7 @@ func (d *denyAgentGetPolicy) Check(_ context.Context, _ auth.Principal, verb aut func TestGatewayNewConversationRequiresAgentAccessAcrossTransports(t *testing.T) { for _, protocol := range []a2atype.TransportProtocol{a2atype.TransportProtocolJSONRPC, a2atype.TransportProtocolGRPC} { - t.Run(string(protocol), func(t *testing.T) { + t.Run(fmt.Sprintf("Given %s transport and denied Agent access, when a conversation is started, assert that no session or runtime work is reserved", protocol), func(t *testing.T) { store := &gatewayTestStore{created: map[string]*apiv1.Session{}} policy := &denyAgentGetPolicy{} dialer := &gatewayTestDialer{} From f1b35a9cf2052a1b9d29ca7fc4354f0787d5f5bf Mon Sep 17 00:00:00 2001 From: Jonathan Jamroga Date: Thu, 29 Oct 2026 21:35:04 -0400 Subject: [PATCH 3/4] test(a2a): name single-case HTTP gateway scenarios Signed-off-by: Jonathan Jamroga --- go/core/internal/a2agateway/http_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/go/core/internal/a2agateway/http_test.go b/go/core/internal/a2agateway/http_test.go index ce6704e98c..31a2905994 100644 --- a/go/core/internal/a2agateway/http_test.go +++ b/go/core/internal/a2agateway/http_test.go @@ -233,7 +233,7 @@ func TestHTTPAgentURLIsAuthoritative(t *testing.T) { } } -func TestHTTPAgentCardDiscoveryAndRouting(t *testing.T) { +func TestGivenAPinnedAgentCardWhenDiscoveredOverHTTPAssertThatItRoutesThroughThePublicURL(t *testing.T) { store := &gatewayTestStore{ session: gatewayTestSession(), revision: &database.RuntimeRevision{AgentCard: &a2apb.AgentCard{ @@ -296,7 +296,7 @@ func TestHTTPAgentCardDiscoveryAndRouting(t *testing.T) { require.Equal(t, gatewayTestID, store.id) } -func TestHTTPGatewayStreamingAndCancellation(t *testing.T) { +func TestGivenHTTPTasksWhenStreamedAndCanceledAssertThatSettledStateIsReadable(t *testing.T) { store := &gatewayTestStore{session: gatewayTestSession()} server, runtime := newHTTPTestServer(t, store, &gatewayTestAuthorizer{}, nil) client, err := a2aclient.NewFromEndpoints(t.Context(), []*a2atype.AgentInterface{ @@ -479,7 +479,7 @@ func TestGatewayReadOnlySharesAcrossTransports(t *testing.T) { } } -func TestHTTPGatewayMalformedJSONRPC(t *testing.T) { +func TestGivenMalformedJSONRPCWhenPostedToTheGatewayAssertThatItReturnsAParseErrorWithoutSessionLookup(t *testing.T) { store := &gatewayTestStore{} handler := NewHTTPHandler(newTestGateway(store, &gatewayTestAuthorizer{}, nil, gatewayTestURL), httpTestAuthenticator{}, nil) request := httptest.NewRequest(http.MethodPost, HTTPPathPrefix+gatewayTestAgent, strings.NewReader(`{"jsonrpc":`)) From ad2341c81cbe1908bfa521580b4a811c5737701d Mon Sep 17 00:00:00 2001 From: Jonathan Jamroga Date: Thu, 29 Oct 2026 21:38:15 -0400 Subject: [PATCH 4/4] test(a2a): separate scenario phases in single-case names Signed-off-by: Jonathan Jamroga --- go/core/internal/a2agateway/http_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/go/core/internal/a2agateway/http_test.go b/go/core/internal/a2agateway/http_test.go index 31a2905994..018ba31132 100644 --- a/go/core/internal/a2agateway/http_test.go +++ b/go/core/internal/a2agateway/http_test.go @@ -233,7 +233,7 @@ func TestHTTPAgentURLIsAuthoritative(t *testing.T) { } } -func TestGivenAPinnedAgentCardWhenDiscoveredOverHTTPAssertThatItRoutesThroughThePublicURL(t *testing.T) { +func TestGivenAPinnedAgentCard_WhenDiscoveredOverHTTP_AssertThatItRoutesThroughThePublicURL(t *testing.T) { store := &gatewayTestStore{ session: gatewayTestSession(), revision: &database.RuntimeRevision{AgentCard: &a2apb.AgentCard{ @@ -296,7 +296,7 @@ func TestGivenAPinnedAgentCardWhenDiscoveredOverHTTPAssertThatItRoutesThroughThe require.Equal(t, gatewayTestID, store.id) } -func TestGivenHTTPTasksWhenStreamedAndCanceledAssertThatSettledStateIsReadable(t *testing.T) { +func TestGivenHTTPTasks_WhenStreamedAndCanceled_AssertThatSettledStateIsReadable(t *testing.T) { store := &gatewayTestStore{session: gatewayTestSession()} server, runtime := newHTTPTestServer(t, store, &gatewayTestAuthorizer{}, nil) client, err := a2aclient.NewFromEndpoints(t.Context(), []*a2atype.AgentInterface{ @@ -479,7 +479,7 @@ func TestGatewayReadOnlySharesAcrossTransports(t *testing.T) { } } -func TestGivenMalformedJSONRPCWhenPostedToTheGatewayAssertThatItReturnsAParseErrorWithoutSessionLookup(t *testing.T) { +func TestGivenMalformedJSONRPC_WhenPosted_AssertThatParseErrorSkipsSessionLookup(t *testing.T) { store := &gatewayTestStore{} handler := NewHTTPHandler(newTestGateway(store, &gatewayTestAuthorizer{}, nil, gatewayTestURL), httpTestAuthenticator{}, nil) request := httptest.NewRequest(http.MethodPost, HTTPPathPrefix+gatewayTestAgent, strings.NewReader(`{"jsonrpc":`))