Skip to content

feat: OpenAPI response validation with array/JSON:API schema support#288

Merged
intel352 merged 6 commits intomainfrom
copilot/add-openapi-response-validation
Mar 12, 2026
Merged

feat: OpenAPI response validation with array/JSON:API schema support#288
intel352 merged 6 commits intomainfrom
copilot/add-openapi-response-validation

Conversation

Copy link
Contributor

Copilot AI commented Mar 11, 2026

The OpenAPI module validated incoming requests against the spec but had no mechanism to validate outgoing responses. Responses could silently deviate from the spec, including complex formats like JSON:API.

Core changes

  • openAPIResponse now parses content with schemas from the spec (previously only had description)
  • openAPISchema extended with items, minItems, maxItems (arrays) and additionalProperties (maps)
  • responseCapturingWriter buffers response body/status/headers for validation before flushing to the client
  • validateResponse() checks status code, content-type (including vendor types like application/vnd.api+json), and body against schema
  • Array validation added to validateJSONValue — validates item schemas and cardinality constraints
  • response_action config: "warn" (default) logs violations, "error" rejects with HTTP 500

Config

config:
  spec_file: specs/petstore.yaml
  base_path: /api/v1
  validation:
    request: true
    response: true
    response_action: warn  # or "error"

Response flow

All three response paths are covered:

  1. Direct write — pipeline step writes to ResponseWriter → captured, validated, then flushed or rejected
  2. Pipeline contextresponse_status/response_body/response_headers in output → validated before writing
  3. Default fallback — 200 with JSON-encoded state → validated against spec

Examples & docs

  • Updated example/specs/petstore.yaml with response schemas
  • Added example/specs/jsonapi-articles.yaml — JSON:API envelope with nested data[].type, data[].id, data[].attributes validation
  • Added example/openapi-jsonapi-articles.yaml — working workflow config
  • Updated DOCUMENTATION.md module table

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

- Extend openAPIResponse with Content/schema for response body validation
- Extend openAPISchema with Items, MinItems, MaxItems, AdditionalProperties
- Add responseCapturingWriter for buffering responses before validation
- Add validateResponse() for checking status, content-type, and body schema
- Add writeAndValidatePipelineResponse() for pipeline context responses
- Add array type validation in validateJSONValue (items, minItems, maxItems)
- Add ResponseAction config field ("warn" logs, "error" rejects with 500)
- Add comprehensive tests for response validation scenarios
- Add JSON:API complex response validation tests
- Update petstore example spec with response schemas
- Create JSON:API articles example spec and workflow config
- Update DOCUMENTATION.md

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copilot AI changed the title [WIP] Add validation for OpenAPI responses in workflow feat: OpenAPI response validation with array/JSON:API schema support Mar 11, 2026
@intel352 intel352 marked this pull request as ready for review March 11, 2026 22:50
Copilot AI review requested due to automatic review settings March 11, 2026 22:50
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds response-side OpenAPI validation to the openapi module so that outgoing HTTP responses (status/content-type/body) can be checked against the spec, including array schemas and vendor media types (e.g., JSON:API).

Changes:

  • Extend OpenAPI parsing/validation to support response schemas plus array constraints (items, minItems, maxItems) and map schemas (additionalProperties).
  • Buffer direct-write responses for validation prior to flushing, and add "warn" vs "error" behavior via response_action.
  • Add extensive test coverage and update example specs/configs and documentation.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
module/openapi.go Implements response validation flow, buffering for direct-write, and array schema validation; adds response_action.
module/openapi_test.go Adds response validation fixtures and tests (valid/invalid, warn/error, JSON:API, direct write, array constraints).
example/specs/petstore.yaml Adds response content schemas so examples can be validated.
example/specs/jsonapi-articles.yaml New JSON:API example spec with nested response schemas.
example/openapi-petstore.yaml Enables response validation in the petstore example config.
example/openapi-jsonapi-articles.yaml New runnable example config demonstrating JSON:API response validation.
DOCUMENTATION.md Documents openapi module as supporting request + response validation.

@github-actions
Copy link

github-actions bot commented Mar 11, 2026

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:244: parsing iteration count: invalid syntax
baseline-bench.txt:299609: parsing iteration count: invalid syntax
baseline-bench.txt:620730: parsing iteration count: invalid syntax
baseline-bench.txt:934018: parsing iteration count: invalid syntax
baseline-bench.txt:1225997: parsing iteration count: invalid syntax
baseline-bench.txt:1521159: parsing iteration count: invalid syntax
benchmark-results.txt:244: parsing iteration count: invalid syntax
benchmark-results.txt:315188: parsing iteration count: invalid syntax
benchmark-results.txt:624150: parsing iteration count: invalid syntax
benchmark-results.txt:884731: parsing iteration count: invalid syntax
benchmark-results.txt:1198751: parsing iteration count: invalid syntax
benchmark-results.txt:1495572: parsing iteration count: invalid syntax
goos: linux
goarch: amd64
pkg: github.com/GoCodeAlone/workflow/dynamic
cpu: AMD EPYC 7763 64-Core Processor                
                            │ baseline-bench.txt │       benchmark-results.txt        │
                            │       sec/op       │    sec/op     vs base              │
InterpreterCreation-4               7.477m ± 60%   6.438m ± 57%       ~ (p=0.937 n=6)
ComponentLoad-4                     3.477m ±  5%   3.467m ±  1%       ~ (p=0.310 n=6)
ComponentExecute-4                  2.035µ ±  2%   2.000µ ±  2%  -1.74% (p=0.015 n=6)
PoolContention/workers-1-4          1.097µ ±  4%   1.082µ ±  3%  -1.41% (p=0.041 n=6)
PoolContention/workers-2-4          1.117µ ±  5%   1.081µ ±  1%  -3.18% (p=0.002 n=6)
PoolContention/workers-4-4          1.087µ ±  1%   1.084µ ±  2%       ~ (p=0.513 n=6)
PoolContention/workers-8-4          1.091µ ±  4%   1.080µ ±  0%  -1.05% (p=0.002 n=6)
PoolContention/workers-16-4         1.100µ ±  2%   1.098µ ±  3%       ~ (p=0.671 n=6)
ComponentLifecycle-4                3.536m ±  1%   3.458m ±  1%  -2.19% (p=0.002 n=6)
SourceValidation-4                  2.341µ ±  2%   2.288µ ±  1%  -2.26% (p=0.002 n=6)
RegistryConcurrent-4                757.6n ±  1%   769.5n ±  5%  +1.56% (p=0.009 n=6)
LoaderLoadFromString-4              3.566m ±  2%   3.482m ±  0%  -2.37% (p=0.002 n=6)
geomean                             18.75µ         18.31µ        -2.35%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               1.944Mi ± 0%   1.944Mi ± 0%       ~ (p=0.623 n=6)
ComponentLoad-4                     2.097Mi ± 0%   2.097Mi ± 0%       ~ (p=0.937 n=6)
ComponentExecute-4                  1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4         1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                2.099Mi ± 0%   2.099Mi ± 0%       ~ (p=0.909 n=6)
SourceValidation-4                  1.984Ki ± 0%   1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                1.133Ki ± 0%   1.133Ki ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4              2.099Mi ± 0%   2.099Mi ± 0%       ~ (p=0.920 n=6)
geomean                             15.05Ki        15.05Ki       -0.00%
¹ all samples are equal

                            │ baseline-bench.txt │        benchmark-results.txt        │
                            │     allocs/op      │  allocs/op   vs base                │
InterpreterCreation-4                15.09k ± 0%   15.09k ± 0%       ~ (p=1.000 n=6)
ComponentLoad-4                      17.43k ± 0%   17.43k ± 0%       ~ (p=1.000 n=6)
ComponentExecute-4                    25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4           25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                 17.48k ± 0%   17.48k ± 0%       ~ (p=1.000 n=6) ¹
SourceValidation-4                    32.00 ± 0%    32.00 ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                  2.000 ± 0%    2.000 ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4               17.47k ± 0%   17.47k ± 0%       ~ (p=1.000 n=6) ¹
geomean                               181.2         181.2       +0.00%
¹ all samples are equal

pkg: github.com/GoCodeAlone/workflow/middleware
                                  │ baseline-bench.txt │       benchmark-results.txt       │
                                  │       sec/op       │   sec/op     vs base              │
CircuitBreakerDetection-4                  297.2n ± 2%   286.4n ± 7%       ~ (p=0.065 n=6)
CircuitBreakerExecution_Success-4          22.53n ± 1%   22.50n ± 0%       ~ (p=0.123 n=6)
CircuitBreakerExecution_Failure-4          64.51n ± 0%   64.32n ± 0%  -0.29% (p=0.006 n=6)
geomean                                    75.59n        74.56n       -1.36%

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │        B/op        │    B/op     vs base                │
CircuitBreakerDetection-4                 144.0 ± 0%     144.0 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │     allocs/op      │ allocs/op   vs base                │
CircuitBreakerDetection-4                 1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/module
                                 │ baseline-bench.txt │       benchmark-results.txt        │
                                 │       sec/op       │    sec/op     vs base              │
JQTransform_Simple-4                     919.0n ± 24%   875.2n ± 29%       ~ (p=0.240 n=6)
JQTransform_ObjectConstruction-4         1.498µ ±  2%   1.458µ ±  1%  -2.64% (p=0.002 n=6)
JQTransform_ArraySelect-4                3.466µ ±  1%   3.312µ ±  1%  -4.46% (p=0.002 n=6)
JQTransform_Complex-4                    39.32µ ±  3%   37.82µ ±  1%  -3.80% (p=0.002 n=6)
JQTransform_Throughput-4                 1.818µ ±  1%   1.787µ ±  0%  -1.73% (p=0.002 n=6)
SSEPublishDelivery-4                     65.70n ±  1%   67.17n ±  3%  +2.25% (p=0.002 n=6)
geomean                                  1.679µ         1.636µ        -2.55%

                                 │ baseline-bench.txt │        benchmark-results.txt         │
                                 │        B/op        │     B/op      vs base                │
JQTransform_Simple-4                   1.273Ki ± 0%     1.273Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4       1.773Ki ± 0%     1.773Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4              2.625Ki ± 0%     2.625Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                  16.22Ki ± 0%     16.22Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4               1.984Ki ± 0%     1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                     0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                 │ baseline-bench.txt │       benchmark-results.txt        │
                                 │     allocs/op      │ allocs/op   vs base                │
JQTransform_Simple-4                     10.00 ± 0%     10.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4         15.00 ± 0%     15.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4                30.00 ± 0%     30.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                    324.0 ± 0%     324.0 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4                 17.00 ± 0%     17.00 ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                     0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/schema
                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │       sec/op       │    sec/op     vs base              │
SchemaValidation_Simple-4                    1.134µ ± 5%   1.109µ ±  1%  -2.25% (p=0.026 n=6)
SchemaValidation_AllFields-4                 1.693µ ± 2%   1.681µ ± 10%       ~ (p=1.000 n=6)
SchemaValidation_FormatValidation-4          1.592µ ± 2%   1.607µ ±  1%       ~ (p=0.331 n=6)
SchemaValidation_ManySchemas-4               1.851µ ± 2%   1.829µ ±  3%       ~ (p=0.457 n=6)
geomean                                      1.542µ        1.530µ        -0.82%

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │        B/op        │    B/op     vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │     allocs/op      │ allocs/op   vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/store
                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │       sec/op       │    sec/op     vs base               │
EventStoreAppend_InMemory-4                1.252µ ± 18%   1.241µ ± 17%        ~ (p=1.000 n=6)
EventStoreAppend_SQLite-4                  1.259m ±  5%   1.902m ±  3%  +51.11% (p=0.002 n=6)
GetTimeline_InMemory/events-10-4           14.37µ ±  4%   13.79µ ±  2%   -4.08% (p=0.009 n=6)
GetTimeline_InMemory/events-50-4           81.05µ ± 20%   74.21µ ± 16%        ~ (p=0.132 n=6)
GetTimeline_InMemory/events-100-4          132.7µ ±  1%   124.3µ ±  1%   -6.32% (p=0.002 n=6)
GetTimeline_InMemory/events-500-4          677.3µ ±  1%   638.7µ ±  0%   -5.70% (p=0.002 n=6)
GetTimeline_InMemory/events-1000-4         1.399m ±  4%   1.300m ±  2%   -7.11% (p=0.002 n=6)
GetTimeline_SQLite/events-10-4             112.9µ ±  1%   108.8µ ±  1%   -3.59% (p=0.002 n=6)
GetTimeline_SQLite/events-50-4             263.2µ ±  2%   249.5µ ±  0%   -5.22% (p=0.002 n=6)
GetTimeline_SQLite/events-100-4            445.8µ ±  2%   422.5µ ±  2%   -5.23% (p=0.002 n=6)
GetTimeline_SQLite/events-500-4            1.881m ±  8%   1.779m ±  1%   -5.40% (p=0.002 n=6)
GetTimeline_SQLite/events-1000-4           3.736m ±  2%   3.468m ±  0%   -7.16% (p=0.002 n=6)
geomean                                    229.6µ         225.9µ         -1.63%

                                   │ baseline-bench.txt │        benchmark-results.txt         │
                                   │        B/op        │     B/op      vs base                │
EventStoreAppend_InMemory-4                 760.0 ± 10%     785.5 ± 7%       ~ (p=0.240 n=6)
EventStoreAppend_SQLite-4                 1.984Ki ±  1%   1.981Ki ± 2%       ~ (p=0.292 n=6)
GetTimeline_InMemory/events-10-4          7.953Ki ±  0%   7.953Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4          46.62Ki ±  0%   46.62Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4         94.48Ki ±  0%   94.48Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4         472.8Ki ±  0%   472.8Ki ± 0%       ~ (p=1.000 n=6)
GetTimeline_InMemory/events-1000-4        944.3Ki ±  0%   944.3Ki ± 0%  +0.00% (p=0.002 n=6)
GetTimeline_SQLite/events-10-4            16.74Ki ±  0%   16.74Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4            87.14Ki ±  0%   87.14Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4           175.4Ki ±  0%   175.4Ki ± 0%       ~ (p=0.455 n=6)
GetTimeline_SQLite/events-500-4           846.1Ki ±  0%   846.1Ki ± 0%  -0.00% (p=0.024 n=6)
GetTimeline_SQLite/events-1000-4          1.639Mi ±  0%   1.639Mi ± 0%       ~ (p=0.643 n=6)
geomean                                   67.13Ki         67.30Ki       +0.26%
¹ all samples are equal

                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │     allocs/op      │  allocs/op   vs base                │
EventStoreAppend_InMemory-4                  7.000 ± 0%    7.000 ± 0%       ~ (p=1.000 n=6) ¹
EventStoreAppend_SQLite-4                    53.00 ± 0%    53.00 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-10-4             125.0 ± 0%    125.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4             653.0 ± 0%    653.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4           1.306k ± 0%   1.306k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4           6.514k ± 0%   6.514k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-1000-4          13.02k ± 0%   13.02k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-10-4               382.0 ± 0%    382.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4              1.852k ± 0%   1.852k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4             3.681k ± 0%   3.681k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4             18.54k ± 0%   18.54k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-1000-4            37.29k ± 0%   37.29k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                     1.162k        1.162k       +0.00%
¹ all samples are equal

Benchmarks run with go test -bench=. -benchmem -count=6.
Regressions ≥ 20% are flagged. Results compared via benchstat.

@intel352
Copy link
Contributor

@copilot apply changes based on the comments in this thread

Also fix failing lint, failing test, and failing example configs.

- responseCapturingWriter uses own header map to prevent header leakage
  when validation fails and a 500 is returned instead of the pipeline response
- validateJSONBody accepts bodyLabel parameter so error messages correctly
  say 'request body' or 'response body' depending on context (was always
  hardcoded to 'request body')
- Implement additionalProperties validation in validateJSONBody — keys not in
  Properties are now validated against AdditionalProperties schema when defined
- Rename TestOpenAPIModule_ResponseValidation_DefaultFallback_Valid to
  TestOpenAPIModule_ResponseValidation_DefaultFallback_InvalidFallback (test
  asserts 500 for schema mismatch, not a valid response)
- Add nil guards for router.findHandler() in all 4 array constraint and
  warn-action subtests

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 11, 2026

@copilot apply changes based on the comments in this thread

Also fix failing lint, failing test, a...

Applied all review changes in ef2128d:

  1. Header leakage (openapi.go:601-605) — responseCapturingWriter now maintains its own http.Header map; headers are only copied to the underlying writer during flush(). When validation fails and a 500 is returned, pipeline headers never reach the client.

  2. Error message context (openapi.go:697) — validateJSONBody now accepts a bodyLabel parameter. Request validation passes "request body", response validation passes "response body", and nested object fields pass the field name. Error messages now read correctly in all contexts.

  3. additionalProperties validation (openapi.go:117-120) — validateJSONBody now validates keys not present in Properties against AdditionalProperties schema when it is specified.

  4. Test rename (openapi_test.go:1769) — TestOpenAPIModule_ResponseValidation_DefaultFallback_ValidTestOpenAPIModule_ResponseValidation_DefaultFallback_InvalidFallback.

  5. Nil guards (openapi_test.go:2248, 2288, 2328, 2375) — All four router.findHandler() call sites in array constraint subtests and the warn-action test now have t.Fatal nil guards.

@intel352
Copy link
Contributor

@copilot apply changes based on the comments in this thread

Copy link
Contributor Author

Copilot AI commented Mar 12, 2026

@copilot apply changes based on the comments in this thread

All items from that review thread were already applied in ef2128d:

  • Nil guards on all 4 router.findHandler() calls (lines 2249, 2292, 2335, 2385)
  • additionalProperties validation implemented in validateJSONBody
  • validateJSONBody takes a bodyLabel parameter for context-aware error messages
  • responseCapturingWriter uses its own header map (no header leakage on validation errors)
  • Test renamed to TestOpenAPIModule_ResponseValidation_DefaultFallback_InvalidFallback

@intel352
Copy link
Contributor

@copilot fix all build, test, linter, and validating issues in the codebase

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 12, 2026

@copilot fix all build, test, linter, and validating issues in the codebase

Fixed all CI failures in 6ce6502:

  1. Lint (modernize/rules.go:213) — converted untagged switch { case val.Kind == ... } to tagged switch val.Kind { case ... } to resolve QF1002: could use tagged switch staticcheck finding.

  2. Build + Validate Example Configs (example/) — ran go mod tidy in the example/ directory to sync go.mod/go.sum with the parent module's upgraded dependency versions (modular sub-modules v1.12.0 → v1.12.3/v1.14.0).

@intel352 intel352 merged commit 858204f into main Mar 12, 2026
11 checks passed
@intel352 intel352 deleted the copilot/add-openapi-response-validation branch March 12, 2026 03:59
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.

3 participants