Testing Strategy
Purpose
Testing in this repository is designed to continuously guarantee the following:
- Regression detection in code generation logic
- Type safety of generated TypeScript code
- Consistency between generated code, GraphQL schema, and GraphQL execution results
- Safe releases across a multi-package repository with dependency constraints
Testing Layers
| Layer | Primary Target | Main Location |
|---|---|---|
| Unit Test | String generation, printers, helper logic | packages/**/src/**/*.test.ts, packages/**/src/**/__tests__/ |
| Golden Test | protoc-gen-pothos generation output, type checks, GraphQL behavior | packages/protoc-gen-pothos/src/__tests__/golden/, tests/golden/ |
| Workspace Test Orchestration | Full monorepo test execution | pnpm test (turbo run test) |
Code Generation CLI Testing Principles
For code generation CLIs such as protoc-gen-pothos, tests should use golden file tests as the default strategy.
- Test cases should be designed to be as MECE as practical (single concern per package, minimal overlap)
- Non-golden unit tests should be kept to a minimum
- Add unit tests only for logic that cannot be directly covered by golden tests (for example: path normalization, error formatting, helper boundary behavior)
Execution Policy
- Run all tests:
pnpm test - Run only
protoc-gen-pothos:pnpm --dir packages/protoc-gen-pothos test - Run only Golden Tests:
pnpm --dir packages/protoc-gen-pothos vitest run src/__tests__/golden/golden.test.ts - Update snapshots:
pnpm --dir packages/protoc-gen-pothos vitest run src/__tests__/golden/golden.test.ts -u
In turbo.json, the test task depends on ^build.
This ensures tests run against built dependency packages and generated test API artifacts.
Golden Test Specification (protoc-gen-pothos)
Directory Convention
Test cases are managed in this two-level structure:
tests/golden/<runtime-variant>/<proto-package>/Examples:
tests/golden/ts-proto/testapis.basic.enums/tests/golden/ts-proto-forcelong/testapis.basic.scalars/tests/golden/protobuf-es-v1/testapis.options.deprecation/
What Each Test Case Validates
packages/protoc-gen-pothos/src/__tests__/golden/golden.test.ts runs the following checks per discovered case:
- Code generation (
protocGenPothos.run) and generated file snapshot comparison - Type checking with per-case
tsconfig.json(TypeScript Compiler API) - Type error snapshot comparison (
__expected__/type-errors.txt) - GraphQL SDL snapshot comparison (
__expected__/schema.graphql) - GraphQL execution result snapshot comparison (
__expected__/query-result.json) only whenquery.graphqlexists
query.graphql-Based Response Validation
- Each case may optionally include
query.graphql - Cases with
query.graphqlare auto-detected ashasQuery=true - At runtime, the test loads
schema.ts, executesgraphql({ schema, source: query }), and serializes the result as JSON - The JSON result is validated via
toMatchFileSnapshotagainstquery-result.json
This keeps query definitions data-oriented and separate from TypeScript test implementation.
Snapshot File Responsibilities
__expected__/**/*.pb.pothos.ts: expected generated code__expected__/type-errors.txt: expected type-check diagnostics (normally empty)__expected__/schema.graphql: expected GraphQL SDL__expected__/query-result.json: expected GraphQL execution result (only for cases withquery.graphql)
testapis-proto Fixture Strategy
All golden test fixture protos live under:
devPackages/testapis-proto/proto/testapis/Current package groups:
basic: scalars, enums, nested, empty, proto3 presence, well-known typesbehavior: comment-derived behavior (Required,Input only,Output only)options: graphql extension options (schema/message/field/oneof/enum, nullability, deprecation, no_partial)oneof: message-only oneof and non-message oneofimports: same-dir, cross-package, oneof cross-file, squashed union, transitive import, symbol collision
Design rules:
- One concern per package
- Imports are explicit test axes (do not hide import behavior in unrelated cases)
- Runtime-sensitive behavior must have dedicated cases
- Keep packages minimal but complete for the scenario they target
- Use stable names that clearly describe test intent
Golden Matrix
Default runtime variants (ts-proto, protobuf-es-v1, protobuf-es) include:
testapis.basic.emptytestapis.basic.enumstestapis.basic.nestedtestapis.basic.presencetestapis.basic.scalarstestapis.basic.wktypestestapis.behavior.field_commentstestapis.imports.cross_pkg_btestapis.imports.oneof_cross_filetestapis.imports.same_dirtestapis.imports.squashed_uniontestapis.imports.transitivetestapis.oneof.message_onlytestapis.options.deprecationtestapis.options.field_nullabilitytestapis.options.input_no_partialtestapis.options.message_and_fieldtestapis.options.schema
Runtime-specific variants:
ts-proto-partial-inputs:testapis.options.message_and_fieldwithpartial_inputsts-proto-forcelong:testapis.basic.scalars,testapis.basic.wktypesprotobuf-es-v1andprotobuf-es:testapis.oneof.non_messagewithignore_non_message_oneof_fields
Per-Case Config (config.json)
Optional config.json in each case directory is used to control discovery-time behavior.
Supported keys:
additionalParams: string[](appended to plugin parameter string)prefixMatch: boolean(select nested proto paths under package prefix)
Examples:
additionalParams: ["emit_imported_files"]for cross-package import emission casesadditionalParams: ["ignore_non_message_oneof_fields"]for non-message oneof casesprefixMatch: truefor packages where nested directory matching is required
How To Add a New Case
- Add or update fixture protos under
devPackages/testapis-proto/proto/testapis/... - Run fixture generation if needed:
pnpm gen:testapis - Create
tests/golden/<runtime-variant>/<proto-package>/ - Add at minimum
builder.ts,schema.ts, andtsconfig.json - Add
config.jsonwhen runtime parameters orprefixMatchare required - Add
query.graphqlif response-level validation is required - Generate/update expected files with
pnpm --dir packages/protoc-gen-pothos vitest run src/__tests__/golden/golden.test.ts -u - Review snapshot diffs to ensure no unintended behavior changes are included
PR Review Checklist
- No unintended snapshot changes
- Type error snapshot changes are intentional
schema.graphqlandquery-result.jsondiffs are behaviorally understood- New cases follow runtime-variant and package naming conventions
- New fixture packages are single-purpose and do not mix unrelated concerns