Skip to content

docs(a2asrv): add Example_* test functions for pkg.go.dev documentation#262

Merged
yarolegovich merged 6 commits intoa2aproject:mainfrom
cchinchilla-dev:docs/add-a2asrv-example-tests
Mar 16, 2026
Merged

docs(a2asrv): add Example_* test functions for pkg.go.dev documentation#262
yarolegovich merged 6 commits intoa2aproject:mainfrom
cchinchilla-dev:docs/add-a2asrv-example-tests

Conversation

@cchinchilla-dev
Copy link
Copy Markdown
Contributor

Description

Ref #257 🦕

Motivation

The a2asrv/ server package does not include testable examples (Example_* functions). Usage snippets exist in the README and doc.go, but they are not executable, not validated by go test, and do not render as function-level examples on pkg.go.dev.

Changes

Add a2asrv/example_test.go using package a2asrv_test (external test package), following Go's ExampleXxx / ExampleType_Method naming convention.

Examples added for the following public API surface:

Function / Type Example demonstrates
NewHandler Creating a basic request handler with an AgentExecutor
NewHandler (withOptions) Creating a handler with WithExtendedAgentCard and WithCallInterceptors
NewJSONRPCHandler Wrapping a handler with JSON-RPC transport and registering with http.ServeMux
NewStaticAgentCardHandler Serving a static AgentCard via httptest and verifying JSON response
NewAgentCardHandler Serving a dynamic AgentCard via an AgentCardProducerFn
WellKnownAgentCardPath Displaying the standard well-known path constant
WithCallInterceptors Adding server-side middleware to a handler
PassthroughCallInterceptor Demonstrating the no-op Before/After interceptor lifecycle
NewCallContext Creating a call context with ServiceParams and reading request metadata
NewServiceParams Case-insensitive service parameter lookups
CallContext.Extensions Requesting, activating, and inspecting extensions

All examples include // Output: comments and are validated by go test.

Tests

The examples themselves are tests — validated through output matching. All 12 pass. Existing tests are unaffected.

go test ./a2asrv/ -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 first of two PRs addressing #257. The second PR will add examples for a2aclient/.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 a2asrv and a2aclient packages by introducing a suite of Example_* functions. These new examples serve as executable documentation, automatically validated by Go's testing framework, and will be rendered on pkg.go.dev to provide clear, functional usage patterns for key API components.

Highlights

  • Enhanced Documentation with Executable Examples: Introduced Example_* test functions for both the a2asrv and a2aclient packages, significantly enhancing their documentation on pkg.go.dev with executable usage examples.
  • Comprehensive a2asrv Examples: Added examples for core a2asrv functionalities, including handler creation (NewHandler), JSON-RPC integration (NewJSONRPCHandler), agent card serving (static and dynamic via NewStaticAgentCardHandler and NewAgentCardHandler), call interceptors (WithCallInterceptors, PassthroughCallInterceptor), and context management (NewCallContext, NewServiceParams, CallContext.Extensions).
  • Comprehensive a2aclient Examples: Included Example_* test functions for the a2aclient package, providing executable demonstrations for client creation (NewFromCard, NewFromEndpoints), factory usage (NewFactory, Factory_CreateFromCard, Factory_CreateFromEndpoints), transport configuration (WithJSONRPCTransport), agent card resolution (Resolver_Resolve, NewResolver), and call interceptors (WithCallInterceptors, AuthInterceptor, WithAdditionalOptions).
  • Automated Validation: Ensured all new examples are validated by go test through output matching, guaranteeing their correctness and up-to-dateness within the codebase.
  • Partial Resolution of Issue [Feat]: Add Example_* test functions for a2aclient/ and a2asrv/ packages #257: This pull request is the first part of a two-PR series addressing issue [Feat]: Add Example_* test functions for a2aclient/ and a2asrv/ packages #257, focusing specifically on adding examples for the a2asrv and a2aclient packages.
Changelog
  • a2aclient/example_test.go
    • Added a new external test file containing Example_* functions to demonstrate usage of a2aclient components.
  • a2asrv/example_test.go
    • Added a new external test file containing Example_* functions to demonstrate usage of a2asrv components.
Activity
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +25 to +26
"github.com/a2aproject/a2a-go/a2a"
"github.com/a2aproject/a2a-go/a2asrv"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries at all! Will update the import paths to v2 and re-test. Thanks for the heads up.

// Auth value: Bearer token123
}

func ExampleNewServiceParams() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ExamplePassthroughCallInterceptor to show the embedding pattern
  • Add ExampleUser with the auth interceptor flow as you suggested
  • Add ExampleAgentExecutor showing a minimal streaming implementation (submit → working → artifact → completed)
  • Add ExampleNewHandler_fullServer showing 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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice find, it was probably forgotten and we should fix it. whatever works better for you:

  1. Open an issue, wait for the fix and proceed with REST
  2. Use JSONRPC handler. I don't mind it, just thought that a raw REST request would be easier to craft.
  3. Add the line which we have in jsonrpc handler for constructing call context to REST handler as part of your PR

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

@cchinchilla-dev
Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist
Copy link
Copy Markdown
Contributor

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.

cchinchilla-dev added a commit to cchinchilla-dev/a2a-go that referenced this pull request Mar 12, 2026
@cchinchilla-dev
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@yarolegovich yarolegovich merged commit 7888e37 into a2aproject:main Mar 16, 2026
4 checks passed
@yarolegovich
Copy link
Copy Markdown
Member

Thanks!

yarolegovich pushed a commit that referenced this pull request Mar 17, 2026
🤖 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).
yarolegovich added a commit that referenced this pull request Mar 23, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants