- name
- typespec-go-add-spector-test
- description
- Adds a Spector mock API test for the typespec-go emitter. Use when given a Spector case link (http-specs or azure-http-specs) to opt the spec into generation, write Go client tests, and validate them against the Spector mock server.
- allowed-tools
- shell
# Add a Spector Test for typespec-go
## Inputs
You will receive one or more Spector cases. Each case can be expressed as any of:
- A link under one of these roots:
- `https://github.com/microsoft/typespec/tree/main/packages/http-specs/specs/...`
- `https://github.com/Azure/typespec-azure/tree/main/packages/azure-http-specs/specs/...`
- A spec path relative to `specs/` (for example, `type/model/empty`), optionally with the
spec type (`http-specs` or `azure-http-specs`) when it is ambiguous.
- A pull request link (in `microsoft/typespec` or `Azure/typespec-azure`) that adds or
changes specs. Inspect the PR's changed files under `packages/http-specs/specs/` or
`packages/azure-http-specs/specs/` and treat each affected spec directory as a case.
When multiple cases are provided, repeat the workflow below for each one.
## Output
- An opt-in entry in `packages/typespec-go/spector.config.http.yaml` or
`packages/typespec-go/spector.config.azure.yaml`.
- Generated Go client code under `packages/typespec-go/test/http-specs/` or
`packages/typespec-go/test/azure-http-specs/`.
- Hand-written `*_client_test.go` files that exercise the generated client against the
Spector mock API server. Create one test file per generated `zz_*_client.go` file.
## Workflow
1. Ensure dependencies are installed and `typespec-go` is built.
2. Identify the Spector case, spec type, and spec path.
3. Add the spec to the appropriate opt-in Spector config.
4. Regenerate the selected Go client with `pnpm tspcompile --filter=<module>`.
5. Read `mockapi.ts` and the generated client method signatures.
6. Write one `*_client_test.go` file per generated client.
7. Start Spector, run the generated module's Go tests, and stop Spector.
## Prerequisites
Run commands from the repository root unless a step specifies otherwise:
```bash
pnpm install
cd packages/typespec-go
pnpm build:deps
```
`pnpm build:deps` builds `typespec-go` and its workspace dependencies through Turbo. Do
not substitute `pnpm build`, which assumes those dependencies are already built.
## 1. Identify the Spec
Determine the spec type from the link:
- `microsoft/typespec/.../packages/http-specs/specs/<path>` uses
`spector.config.http.yaml` and generates to `test/http-specs/`.
- `Azure/typespec-azure/.../packages/azure-http-specs/specs/<path>` uses
`spector.config.azure.yaml` and generates to `test/azure-http-specs/`.
The spec path is the portion after `specs/`. For example,
`type/model/empty` is the path for
`.../packages/http-specs/specs/type/model/empty`.
Search the matching `spector.config.*.yaml` file before adding an entry. If it is
already enabled, retain its existing configuration and proceed to the test work.
## 2. Opt Into Generation
Spector selection is opt-in. Do not add or modify hard-coded external Spector groups in
`.scripts/tspcompile.js`; it loads and resolves the two YAML config files.
Add an enabled entry under `specs`, providing a unique Go module name in `options.module`.
Use lowercase names without hyphens and retain the conventional `group` suffix:
```yaml
specs:
"type/model/empty": { options: { module: emptygroup, single-client: true } }
```
For Azure specs that require a particular `.tsp` file, include it in the key:
```yaml
specs:
"azure/client-generator-core/api-version/header/client.tsp":
{ options: { module: apiversionheadergroup } }
```
Emitter options belong in the same `options` object and are passed as
`@azure-tools/typespec-go` emitter options. `module` is required. A single spec may
produce multiple test modules by supplying a list of option objects:
```yaml
specs:
"azure/versioning/previewVersion":
- options: { module: previewversiongroup, api-version: "2024-12-01-preview" }
- options: { module: previewversiongroupspecificversion, api-version: "2024-06-01" }
```
Do not override the generation-owned `module`, `emitter-output-dir`, or `file-prefix`
options; set only this config's required `module` value. Per-test emitter defaults such as
`generate-fakes`, `inject-spans`, `head-as-boolean`, and `fix-const-stuttering` may be
overridden when the test needs it.
When a known unsupported spec should be explicitly tracked but not generated, add it as
`false` with a nearby comment linking the issue:
```yaml
# requires union support: https://github.com/Azure/autorest.go/issues/1234
"type/union": false
```
## 3. Generate the Client
From `packages/typespec-go`, regenerate only the new module:
```bash
pnpm tspcompile --filter=emptygroup
```
`--filter` is a regular expression matched against module names. The generator derives
the output path from the spec path and module name. Confirm the resulting module contains
the expected `zz_*_client.go` files. Do not edit generated `zz_*` files.
If the generated module requires dependencies that are absent from its `go.sum`, run:
```bash
cd test/<http-specs|azure-http-specs>/<spec-parent-path>/<module>
go mod tidy
```
Generated Go fixtures are ignored by git. Commit the opt-in config and hand-written test
files, not generated output.
## 4. Read the Mock API and Generated Surface
Read the upstream `mockapi.ts` for the selected spec:
- HTTP specs: `packages/typespec-go/node_modules/@typespec/http-specs/specs/<spec-path>/mockapi.ts`
- Azure HTTP specs: `packages/typespec-go/node_modules/@azure-tools/azure-http-specs/specs/<spec-path>/mockapi.ts`
For each scenario, match the expected URI, method, request body, headers, query and path
parameters, plus response status, headers, and body. Then inspect the generated
`zz_*_client.go` file for the exact client constructor, method names, and parameter types.
## 5. Write Go Client Tests
Create one hand-written `<name>_client_test.go` for every generated
`zz_<name>_client.go`. The test package is `<module>_test`; use `require`, not `assert`.
```go
package emptygroup_test
import (
"context"
"emptygroup"
"testing"
"github.com/stretchr/testify/require"
)
func TestEmptyClientGetEmpty(t *testing.T) {
client, err := emptygroup.NewEmptyClientWithNoCredential("http://localhost:3000", nil)
require.NoError(t, err)
resp, err := client.GetEmpty(context.Background(), nil)
require.NoError(t, err)
require.Zero(t, resp)
}
```
Use `http://localhost:3000` for every Spector client. For sub-clients, construct the
root client and access the sub-client from it. Common assertion patterns:
- 204 responses: `require.Zero(t, resp)`.
- Response bodies: `require.EqualValues(t, expected, resp.<Field>)`.
- Pointer fields: `to.Ptr(value)` from `github.com/Azure/azure-sdk-for-go/sdk/azcore/to`.
- Time values: `require.WithinDuration(t, expected, actual, 0)`.
- Pagers: iterate with `pager.More()` and `pager.NextPage(context.Background())`.
- LROs: call `poller.PollUntilDone(context.Background(), nil)`.
## 6. Validate Against Spector
From `packages/typespec-go`, start the mock server:
```bash
pnpm spector --start
```
Then run the selected module's tests:
```bash
cd test/<http-specs|azure-http-specs>/<spec-parent-path>/<module>
go test -v ./...
```
Stop the server when validation completes:
```bash
pnpm spector --stop
```
For full generated Go test coverage, use `pnpm test:go:e2e`. If a focused test fails,
verify the server is running, the endpoint is `http://localhost:3000`, the test matches
`mockapi.ts`, and the generated method signature was used correctly.
Run `pnpm lint:go` after regeneration when `golangci-lint` and `shadow` are available on
`PATH`. This lint command covers all generated Go modules, including the new Spector test.
## Notes
- Each generated test group is an independent Go module.
- Hand-written Go tests are the source of truth; regeneration can replace generated files.
- Keep config entries organized with the existing logical/alphabetical layout.
- The config format is standardized across emitters. Keep selection and per-spec options
together in the relevant `spector.config.*.yaml` file.
Auf GitHub ansehen