|
| 1 | +--- |
| 2 | +name: typespec-go-add-spector-test |
| 3 | +description: > |
| 4 | + Adds a Spector mock API test for the typespec-go emitter. Use when given a Spector |
| 5 | + case link (http-specs or azure-http-specs) to opt the spec into generation, write |
| 6 | + Go client tests, and validate them against the Spector mock server. |
| 7 | +allowed-tools: shell |
| 8 | +--- |
| 9 | + |
| 10 | +# Add a Spector Test for typespec-go |
| 11 | + |
| 12 | +## Inputs |
| 13 | + |
| 14 | +You will receive one or more Spector cases. Each case can be expressed as any of: |
| 15 | + |
| 16 | +- A link under one of these roots: |
| 17 | + - `https://github.com/microsoft/typespec/tree/main/packages/http-specs/specs/...` |
| 18 | + - `https://github.com/Azure/typespec-azure/tree/main/packages/azure-http-specs/specs/...` |
| 19 | +- A spec path relative to `specs/` (for example, `type/model/empty`), optionally with the |
| 20 | + spec type (`http-specs` or `azure-http-specs`) when it is ambiguous. |
| 21 | +- A pull request link (in `microsoft/typespec` or `Azure/typespec-azure`) that adds or |
| 22 | + changes specs. Inspect the PR's changed files under `packages/http-specs/specs/` or |
| 23 | + `packages/azure-http-specs/specs/` and treat each affected spec directory as a case. |
| 24 | + |
| 25 | +When multiple cases are provided, repeat the workflow below for each one. |
| 26 | + |
| 27 | +## Output |
| 28 | + |
| 29 | +- An opt-in entry in `packages/typespec-go/spector.config.http.yaml` or |
| 30 | + `packages/typespec-go/spector.config.azure.yaml`. |
| 31 | +- Generated Go client code under `packages/typespec-go/test/http-specs/` or |
| 32 | + `packages/typespec-go/test/azure-http-specs/`. |
| 33 | +- Hand-written `*_client_test.go` files that exercise the generated client against the |
| 34 | + Spector mock API server. Create one test file per generated `zz_*_client.go` file. |
| 35 | + |
| 36 | +## Workflow |
| 37 | + |
| 38 | +1. Ensure dependencies are installed and `typespec-go` is built. |
| 39 | +2. Identify the Spector case, spec type, and spec path. |
| 40 | +3. Add the spec to the appropriate opt-in Spector config. |
| 41 | +4. Regenerate the selected Go client with `pnpm tspcompile --filter=<module>`. |
| 42 | +5. Read `mockapi.ts` and the generated client method signatures. |
| 43 | +6. Write one `*_client_test.go` file per generated client. |
| 44 | +7. Start Spector, run the generated module's Go tests, and stop Spector. |
| 45 | + |
| 46 | +## Prerequisites |
| 47 | + |
| 48 | +Run commands from the repository root unless a step specifies otherwise: |
| 49 | + |
| 50 | +```bash |
| 51 | +pnpm install |
| 52 | +cd packages/typespec-go |
| 53 | +pnpm build:deps |
| 54 | +``` |
| 55 | + |
| 56 | +`pnpm build:deps` builds `typespec-go` and its workspace dependencies through Turbo. Do |
| 57 | +not substitute `pnpm build`, which assumes those dependencies are already built. |
| 58 | + |
| 59 | +## 1. Identify the Spec |
| 60 | + |
| 61 | +Determine the spec type from the link: |
| 62 | + |
| 63 | +- `microsoft/typespec/.../packages/http-specs/specs/<path>` uses |
| 64 | + `spector.config.http.yaml` and generates to `test/http-specs/`. |
| 65 | +- `Azure/typespec-azure/.../packages/azure-http-specs/specs/<path>` uses |
| 66 | + `spector.config.azure.yaml` and generates to `test/azure-http-specs/`. |
| 67 | + |
| 68 | +The spec path is the portion after `specs/`. For example, |
| 69 | +`type/model/empty` is the path for |
| 70 | +`.../packages/http-specs/specs/type/model/empty`. |
| 71 | + |
| 72 | +Search the matching `spector.config.*.yaml` file before adding an entry. If it is |
| 73 | +already enabled, retain its existing configuration and proceed to the test work. |
| 74 | + |
| 75 | +## 2. Opt Into Generation |
| 76 | + |
| 77 | +Spector selection is opt-in. Do not add or modify hard-coded external Spector groups in |
| 78 | +`.scripts/tspcompile.js`; it loads and resolves the two YAML config files. |
| 79 | + |
| 80 | +Add an enabled entry under `specs`, providing a unique Go module name in `options.module`. |
| 81 | +Use lowercase names without hyphens and retain the conventional `group` suffix: |
| 82 | + |
| 83 | +```yaml |
| 84 | +specs: |
| 85 | + "type/model/empty": { options: { module: emptygroup, single-client: true } } |
| 86 | +``` |
| 87 | +
|
| 88 | +For Azure specs that require a particular `.tsp` file, include it in the key: |
| 89 | + |
| 90 | +```yaml |
| 91 | +specs: |
| 92 | + "azure/client-generator-core/api-version/header/client.tsp": |
| 93 | + { options: { module: apiversionheadergroup } } |
| 94 | +``` |
| 95 | + |
| 96 | +Emitter options belong in the same `options` object and are passed as |
| 97 | +`@azure-tools/typespec-go` emitter options. `module` is required. A single spec may |
| 98 | +produce multiple test modules by supplying a list of option objects: |
| 99 | + |
| 100 | +```yaml |
| 101 | +specs: |
| 102 | + "azure/versioning/previewVersion": |
| 103 | + - options: { module: previewversiongroup, api-version: "2024-12-01-preview" } |
| 104 | + - options: { module: previewversiongroupspecificversion, api-version: "2024-06-01" } |
| 105 | +``` |
| 106 | + |
| 107 | +Do not override the generation-owned `module`, `emitter-output-dir`, or `file-prefix` |
| 108 | +options; set only this config's required `module` value. Per-test emitter defaults such as |
| 109 | +`generate-fakes`, `inject-spans`, `head-as-boolean`, and `fix-const-stuttering` may be |
| 110 | +overridden when the test needs it. |
| 111 | + |
| 112 | +When a known unsupported spec should be explicitly tracked but not generated, add it as |
| 113 | +`false` with a nearby comment linking the issue: |
| 114 | + |
| 115 | +```yaml |
| 116 | +# requires union support: https://github.com/Azure/autorest.go/issues/1234 |
| 117 | +"type/union": false |
| 118 | +``` |
| 119 | + |
| 120 | +## 3. Generate the Client |
| 121 | + |
| 122 | +From `packages/typespec-go`, regenerate only the new module: |
| 123 | + |
| 124 | +```bash |
| 125 | +pnpm tspcompile --filter=emptygroup |
| 126 | +``` |
| 127 | + |
| 128 | +`--filter` is a regular expression matched against module names. The generator derives |
| 129 | +the output path from the spec path and module name. Confirm the resulting module contains |
| 130 | +the expected `zz_*_client.go` files. Do not edit generated `zz_*` files. |
| 131 | + |
| 132 | +If the generated module requires dependencies that are absent from its `go.sum`, run: |
| 133 | + |
| 134 | +```bash |
| 135 | +cd test/<http-specs|azure-http-specs>/<spec-parent-path>/<module> |
| 136 | +go mod tidy |
| 137 | +``` |
| 138 | + |
| 139 | +Generated Go fixtures are ignored by git. Commit the opt-in config and hand-written test |
| 140 | +files, not generated output. |
| 141 | + |
| 142 | +## 4. Read the Mock API and Generated Surface |
| 143 | + |
| 144 | +Read the upstream `mockapi.ts` for the selected spec: |
| 145 | + |
| 146 | +- HTTP specs: `packages/typespec-go/node_modules/@typespec/http-specs/specs/<spec-path>/mockapi.ts` |
| 147 | +- Azure HTTP specs: `packages/typespec-go/node_modules/@azure-tools/azure-http-specs/specs/<spec-path>/mockapi.ts` |
| 148 | + |
| 149 | +For each scenario, match the expected URI, method, request body, headers, query and path |
| 150 | +parameters, plus response status, headers, and body. Then inspect the generated |
| 151 | +`zz_*_client.go` file for the exact client constructor, method names, and parameter types. |
| 152 | + |
| 153 | +## 5. Write Go Client Tests |
| 154 | + |
| 155 | +Create one hand-written `<name>_client_test.go` for every generated |
| 156 | +`zz_<name>_client.go`. The test package is `<module>_test`; use `require`, not `assert`. |
| 157 | + |
| 158 | +```go |
| 159 | +package emptygroup_test |
| 160 | +
|
| 161 | +import ( |
| 162 | + "context" |
| 163 | + "emptygroup" |
| 164 | + "testing" |
| 165 | +
|
| 166 | + "github.com/stretchr/testify/require" |
| 167 | +) |
| 168 | +
|
| 169 | +func TestEmptyClientGetEmpty(t *testing.T) { |
| 170 | + client, err := emptygroup.NewEmptyClientWithNoCredential("http://localhost:3000", nil) |
| 171 | + require.NoError(t, err) |
| 172 | +
|
| 173 | + resp, err := client.GetEmpty(context.Background(), nil) |
| 174 | + require.NoError(t, err) |
| 175 | + require.Zero(t, resp) |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +Use `http://localhost:3000` for every Spector client. For sub-clients, construct the |
| 180 | +root client and access the sub-client from it. Common assertion patterns: |
| 181 | + |
| 182 | +- 204 responses: `require.Zero(t, resp)`. |
| 183 | +- Response bodies: `require.EqualValues(t, expected, resp.<Field>)`. |
| 184 | +- Pointer fields: `to.Ptr(value)` from `github.com/Azure/azure-sdk-for-go/sdk/azcore/to`. |
| 185 | +- Time values: `require.WithinDuration(t, expected, actual, 0)`. |
| 186 | +- Pagers: iterate with `pager.More()` and `pager.NextPage(context.Background())`. |
| 187 | +- LROs: call `poller.PollUntilDone(context.Background(), nil)`. |
| 188 | + |
| 189 | +## 6. Validate Against Spector |
| 190 | + |
| 191 | +From `packages/typespec-go`, start the mock server: |
| 192 | + |
| 193 | +```bash |
| 194 | +pnpm spector --start |
| 195 | +``` |
| 196 | + |
| 197 | +Then run the selected module's tests: |
| 198 | + |
| 199 | +```bash |
| 200 | +cd test/<http-specs|azure-http-specs>/<spec-parent-path>/<module> |
| 201 | +go test -v ./... |
| 202 | +``` |
| 203 | + |
| 204 | +Stop the server when validation completes: |
| 205 | + |
| 206 | +```bash |
| 207 | +pnpm spector --stop |
| 208 | +``` |
| 209 | + |
| 210 | +For full generated Go test coverage, use `pnpm test:go:e2e`. If a focused test fails, |
| 211 | +verify the server is running, the endpoint is `http://localhost:3000`, the test matches |
| 212 | +`mockapi.ts`, and the generated method signature was used correctly. |
| 213 | + |
| 214 | +Run `pnpm lint:go` after regeneration when `golangci-lint` and `shadow` are available on |
| 215 | +`PATH`. This lint command covers all generated Go modules, including the new Spector test. |
| 216 | + |
| 217 | +## Notes |
| 218 | + |
| 219 | +- Each generated test group is an independent Go module. |
| 220 | +- Hand-written Go tests are the source of truth; regeneration can replace generated files. |
| 221 | +- Keep config entries organized with the existing logical/alphabetical layout. |
| 222 | +- The config format is standardized across emitters. Keep selection and per-spec options |
| 223 | + together in the relevant `spector.config.*.yaml` file. |
0 commit comments