docs(a2asrv): add Example_* test functions for pkg.go.dev documentation#262
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly improves the discoverability and understanding of the Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request adds comprehensive example tests for the a2asrv and a2aclient packages. These examples serve as executable documentation on pkg.go.dev, which is a great improvement. The code is well-written and the examples clearly demonstrate the usage of the public APIs. I have one suggestion to merge two examples related to AuthInterceptor into a single, more descriptive one to better illustrate its usage. Overall, this is a valuable addition to the project.
0075ad3 to
25f8aad
Compare
a2asrv/example_test.go
Outdated
| "github.com/a2aproject/a2a-go/a2a" | ||
| "github.com/a2aproject/a2a-go/a2asrv" |
There was a problem hiding this comment.
| "github.com/a2aproject/a2a-go/a2a" | |
| "github.com/a2aproject/a2a-go/a2asrv" | |
| "github.com/a2aproject/a2a-go/v2/a2a" | |
| "github.com/a2aproject/a2a-go/v2/a2asrv" |
sorry, module ID changes again
There was a problem hiding this comment.
No worries at all! Will update the import paths to v2 and re-test. Thanks for the heads up.
a2asrv/example_test.go
Outdated
| // Auth value: Bearer token123 | ||
| } | ||
|
|
||
| func ExampleNewServiceParams() { |
There was a problem hiding this comment.
not sure all examples here are useful. handler creation and registration certainly are, but ExamplePassthroughCallInterceptor and ExampleWellKnownAgentCardPath are certainly not.
for passthrough interceptor we can do something like:
func ExamplePassthroughCallInterceptor() {
type myInterceptor struct {
a2asrv.PassthroughCallInterceptor
}
handler := a2asrv.NewHandler(&echoExecutor{}, a2asrv.WithCallInterceptors(myInterceptor{}))
fmt.Println("Handler created:", handler != nil)
// Output:
// Handler created: true
}to show it exists for embedding so that method implementations are not required.
some examples like ExampleNewCallContext, ExampleNewServiceParams are not very practical because that's not the code users usually call, these methods are exported for custom transport implementations. if examples are intended to help less skilled developers understand how to use the package, I'd rather focus on the code they are more likely to write, for example for user this might be:
func ExampleUser() {
authenticate := func(_ string) string { return "user" }
interceptor := &testInterceptor{
BeforeFn: func(ctx context.Context, callCtx *a2asrv.CallContext, req *a2asrv.Request) (context.Context, any, error) {
if auth, ok := callCtx.ServiceParams().Get("authorization"); ok && strings.HasPrefix(auth[0], "Bearer ") {
if name := authenticate(auth[0]); name != "" {
callCtx.User = a2asrv.NewAuthenticatedUser(name, nil)
}
}
return ctx, nil, nil
},
}
executor := &echoExecutor{
ExecuteFn: func(_ context.Context, execCtx *a2asrv.ExecutorContext) iter.Seq2[a2a.Event, error] {
return func(yield func(a2a.Event, error) bool) {
fmt.Println("Auth found:", execCtx.User.Name)
yield(a2a.NewMessage(a2a.MessageRoleAgent, a2a.NewTextPart("echo")), nil)
}
},
}
ctx, _ := a2asrv.NewCallContext(context.Background(), a2asrv.NewServiceParams(map[string][]string{
"Authorization": {"Bearer token"},
}))
handler := a2asrv.NewHandler(executor, a2asrv.WithCallInterceptors(interceptor))
_, err := handler.SendMessage(ctx, &a2a.SendMessageRequest{
Message: a2a.NewMessage(a2a.MessageRoleUser, a2a.NewTextPart("echo")),
})
fmt.Println("Error:", err)
// Output:
// Auth found: user
// Error: <nil>
}for service params ideally it's a jsonrpc http request with headers which are accessed by an interceptor from callContext.
I understand it's tricky and requires a balance of readability&clarity vs showcasing advanced use cases, but I think examples should exist as preventive answers to questions people are likely to ask 🙂
wdyt
There was a problem hiding this comment.
Agreed, makes much more sense to frame examples as answers to questions people are likely to ask rather than an API surface tour. Good call.
Here's what I'm thinking:
- Drop
ExampleWellKnownAgentCardPath,ExampleWithCallInterceptors,ExampleCallContext_Extensions,ExampleNewCallContext,ExampleNewServiceParams - Rework
ExamplePassthroughCallInterceptorto show the embedding pattern - Add
ExampleUserwith the auth interceptor flow as you suggested - Add
ExampleAgentExecutorshowing a minimal streaming implementation (submit → working → artifact → completed) - Add
ExampleNewHandler_fullServershowing the typical wiring: executor → handler → agent card + JSON-RPC on a single mux - Update all import paths to
v2
Would you trim or adjust any of these? I'll apply the same approach to the a2aclient examples in #263.
There was a problem hiding this comment.
sounds good 👍
extra good if it'll have ExampleServiceParams where an http request is made to show that a header ends up in callCtx.ServiceParams() of an interceptor.
the easiest setup for this will be a NewRESTHandler() and GET /tasks/123. the request will return task not found, but we'll only be interested in service params, so doesn't mattter
There was a problem hiding this comment.
Hi @yarolegovich,
While implementing ExampleServiceParams with NewRESTHandler() and GET /tasks/{id} as you suggested, I noticed that HTTP headers don't seem to reach callCtx.ServiceParams() in the interceptor.
Looking at the code, NewRESTHandler handler methods pass req.Context() directly to the request handler without calling NewCallContext(ctx, NewServiceParams(req.Header)), while NewJSONRPCHandler does inject them in ServeHTTP (jsonrpc_server.go:50). Because of this, attachMethodCallContext creates a CallContext with nil ServiceParams.
Here's a minimal test that reproduces it:
func TestREST_ServiceParams(t *testing.T) {
ctx := t.Context()
tid := a2a.NewTaskID()
task := &a2a.Task{ID: tid, ContextID: a2a.NewContextID()}
store := testutil.NewTestTaskStore().WithTasks(t, task)
var gotAuth []string
interceptor := &testInterceptor{
BeforeFn: func(ctx context.Context, callCtx *CallContext, req *Request) (context.Context, any, error) {
gotAuth, _ = callCtx.ServiceParams().Get("authorization")
return ctx, nil, nil
},
}
handler := NewHandler(&mockAgentExecutor{}, WithTaskStore(store), WithCallInterceptors(interceptor))
server := httptest.NewServer(NewRESTHandler(handler))
defer server.Close()
req, err := http.NewRequestWithContext(ctx, "GET", server.URL+"/tasks/"+string(tid), nil)
if err != nil {
t.Fatalf("http.NewRequestWithContext() error = %v", err)
}
req.Header.Set("Authorization", "Bearer test-token")
resp, err := server.Client().Do(req)
if err != nil {
t.Fatalf("server.Client().Do() error = %v", err)
}
defer resp.Body.Close()
if len(gotAuth) == 0 || gotAuth[0] != "Bearer test-token" {
t.Errorf("ServiceParams[authorization] = %v, want [Bearer test-token]", gotAuth)
}
}Result:
=== RUN TestREST_ServiceParams
example_test.go:44: ServiceParams[authorization] = [], want [Bearer test-token]
--- FAIL: TestREST_ServiceParams (0.00s)
That said, I may be approaching this incorrectly or missing something about how the REST handler is intended to work — please let me know if that's the case.
Would you like me to open an issue for this? For ExampleServiceParams I can either wait for the fix and then write it with NewRESTHandler as you suggested, or use NewJSONRPCHandler in the meantime — whatever you prefer.
There was a problem hiding this comment.
nice find, it was probably forgotten and we should fix it. whatever works better for you:
- Open an issue, wait for the fix and proceed with REST
- Use JSONRPC handler. I don't mind it, just thought that a raw REST request would be easier to craft.
- Add the line which we have in jsonrpc handler for constructing call context to REST handler as part of your PR
There was a problem hiding this comment.
Nice, thanks for confirming! I'll go with option 1 — I've opened #275 for this and will wait for the fix. REST feels more natural and simpler for users to understand in an example, so I'd rather keep it that way.
There was a problem hiding this comment.
@yarolegovich all updated — reworked the examples per your feedback, updated import paths to v2, and ExampleServiceParams now uses NewRESTHandler after #275. All 10 examples pass. Ready for review when you get a chance.
|
/gemini review |
|
Warning Gemini is experiencing higher than usual traffic and was unable to create the review. Please try again in a few hours by commenting |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive suite of example tests for the a2asrv package, significantly improving the documentation on pkg.go.dev and providing clear usage patterns for the API. The examples are well-thought-out, covering a broad range of functionalities from basic handler creation to more complex scenarios involving interceptors and full server setups. My review identifies a couple of high-severity issues that could lead to panics due to unsafe slice access in the examples. Additionally, I've pointed out several medium-severity opportunities to align struct initializations with repository best practices for JSON serialization. Overall, this is a valuable addition, and addressing these points will make the examples more robust and exemplary.
|
Thanks! |
🤖 I have created a release *beep* *boop* --- ## [1.0.0](v1.0.0-alpha.3...v1.0.0) (2026-03-17) ### Features * implement the new rest error handling ([#282](#282)) ([a3bda30](a3bda30)) * use v2 suffix for module ID and provide compat support ([#270](#270)) ([dd1b6ba](dd1b6ba)), closes [#250](#250) ### Bug Fixes * a2asrv jsonrpc Content-Type ([#265](#265)) ([2568a46](2568a46)) * bugs before going from alpha ([#279](#279)) ([b1f055c](b1f055c)) * GetTaskRequest nil pointer assignment check ([#258](#258)) ([440bb79](440bb79)) * inject headers into service params ([#277](#277)) ([d33f3bd](d33f3bd)), closes [#275](#275) * propagate cancelation signal using task store ([#272](#272)) ([5e1d462](5e1d462)), closes [#245](#245) * regenerate spec and fix returnImmediately ([#284](#284)) ([2eee0b9](2eee0b9)) * task modified after save ([#266](#266)) ([c15febe](c15febe)) * taskupdater result mutable ([#274](#274)) ([6038d92](6038d92)) * update pushsender ([#256](#256)) ([5f7a594](5f7a594)) * use enum values as in the spec ([#261](#261)) ([eb98981](eb98981)), closes [#251](#251) ### Documentation * **a2asrv:** add Example_* test functions for pkg.go.dev documentation ([#262](#262)) ([7888e37](7888e37)) * add example tests a2a ([#240](#240)) ([4fe08a9](4fe08a9)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
…ation (#263) ## Description Ref #257 🦕 ### Motivation The `a2aclient/` client package does not include testable examples (`Example_*` functions). Usage snippets exist in the README, but they are not executable, not validated by `go test`, and do not render as function-level examples on **pkg.go.dev**. ### Changes Add `a2aclient/example_test.go` using `package a2aclient_test` (external test package), following Go's `ExampleXxx` / `ExampleType_Method` naming convention. Examples added for the following public API surface: | Function / Type | Example demonstrates | |---|---| | `NewFromCard` | Creating a client from an `AgentCard` (with live httptest server) | | `NewFromEndpoints` | Creating a client from known `AgentInterface` endpoints | | `NewFactory` | Setting up a reusable client factory | | `NewFactory` (withConfig) | Factory with `PreferredTransports` configuration | | `Factory.CreateFromCard` | Using factory to create a client from a card | | `Factory.CreateFromEndpoints` | Using factory to create a client from endpoints | | `WithJSONRPCTransport` | Configuring JSON-RPC transport with a custom `http.Client` | | `Resolver.Resolve` | Resolving an `AgentCard` from a URL via `agentcard.DefaultResolver` | | `NewResolver` | Creating a resolver with a custom `http.Client` | | `AuthInterceptor` | Setting up credential-based auth with `InMemoryCredentialsStore` and adding it to a factory via `WithCallInterceptors` | | `WithAdditionalOptions` | Extending a base factory with additional options | All examples include `// Output:` comments and are validated by `go test`. ### Tests The examples themselves are tests — validated through output matching. Existing tests are unaffected. ``` go test ./a2aclient/ -v -run Example ``` ### Additional context Import paths follow the current module declaration (`github.com/a2aproject/a2a-go`), consistent with the temporary revert in #254 (ref #250). Happy to update if the module path changes. This is the second of two PRs addressing #257, following the a2asrv/ examples PR (#262). If there are other packages where this kind of testable examples would be useful, happy to pick them up. --------- Co-authored-by: Yaroslav <yarolegovich@gmail.com>
Description
Ref #257 🦕
Motivation
The
a2asrv/server package does not include testable examples (Example_*functions). Usage snippets exist in the README anddoc.go, but they are not executable, not validated bygo test, and do not render as function-level examples on pkg.go.dev.Changes
Add
a2asrv/example_test.gousingpackage a2asrv_test(external test package), following Go'sExampleXxx/ExampleType_Methodnaming convention.Examples added for the following public API surface:
NewHandlerAgentExecutorNewHandler(withOptions)WithExtendedAgentCardandWithCallInterceptorsNewJSONRPCHandlerhttp.ServeMuxNewStaticAgentCardHandlerAgentCardvia httptest and verifying JSON responseNewAgentCardHandlerAgentCardvia anAgentCardProducerFnWellKnownAgentCardPathWithCallInterceptorsPassthroughCallInterceptorBefore/Afterinterceptor lifecycleNewCallContextServiceParamsand reading request metadataNewServiceParamsCallContext.ExtensionsAll examples include
// Output:comments and are validated bygo test.Tests
The examples themselves are tests — validated through output matching. All 12 pass. Existing tests are unaffected.
Additional context
Import paths follow the current module declaration (
github.com/a2aproject/a2a-go), consistent with the temporary revert in #254 (ref #250). Happy to update if the module path changes.This is the first of two PRs addressing #257. The second PR will add examples for
a2aclient/.