Repository navigation
feat(apidocs): Opt-in check of test responses against the OpenAPI schema - #131077
Draft
sentry-junior[bot] wants to merge 1 commit into
Draft
sentry-junior[bot] wants to merge 1 commit into
sentry-junior[bot] wants to merge 1 commit into
Conversation
Proof of concept for #131073. With SENTRY_OPENAPI_RESPONSE_CHECK=report, every response that goes through the Django test client is matched to a documented operation, and an undocumented status or a JSON body that doesn't match its schema is collected and summarized at the end of the run. strict also fails the request. Refs #131073 Co-Authored-By: BYK <burak.kaya@sentry.io>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proof of concept for #131073: find out what it costs to check endpoint test responses against the published OpenAPI schema. It's opt-in and changes nothing unless you set the env var.
What it does
A pytest plugin (
sentry.testutils.pytest.openapi_responses) wrapsdjango.test.Client.request, so it sees every request a test makes:get_success_response,get_error_response, and plainself.client.get(...)too. For each response it:tests/apidocs/openapi-derefed.json. Literal segments win over{params}. A path or method that isn't in the schema isn't public and is skipped (internal RPC, private endpoints).undocumented-statuswhen the status isn't a key in that operation'sresponses.body-mismatchwhen the status has a JSON schema and the rendered body doesn't validate against it (openapi_schema_validator.OAS30ReadValidator, cached per operation and status).Each xdist worker writes its findings to
.artifacts/openapi-response-check/<worker>.json, and the controller prints one deduplicated summary.reportonly collects findings.strictalso raises in the request that produced the mismatch.This PR has no baseline or ratchet and no CI wiring yet. Those depend on the cost numbers below.
Cost
Measured on an 8 vCPU sandbox:
make build-api-docs(deprecated + spectacular--validate+ deref)--validate: 16s, deref: 6s)tests/sentry/core/endpoints+tests/sentry/issues/endpoints,-n 6, ~1,600 teststest_group_ai_autofix.py, 889 requestsThe wall-clock difference between runs with and without the check was within noise. Snuba-backed tests in this sandbox flake differently from run to run under xdist. Run serially, the same files pass identically with the check on and off.
The check itself is close to free. The real cost is getting a fresh
openapi-derefed.jsonto wherever the tests run.Findings from the sample run
These come from two test directories plus the autofix tests, and include the endpoint that #131067 fixed:
POST …/issues/{issue_id}/autofix/ 202: thecoding_agent_handoffstep returnsAutofixHandoffResponse(successes/failures), but the schema documents onlyAutofixPostResponse('feature_id' is a required property). This is the same kind of drift as docs(seer): Document Autofix 402/409 error responses #131067.GET …/autofix/ 304: the conditional GET isn't documented.listOrganizations/getProjectallowMemberInviteis missing,listOrganizationIssuesinboxandupdateOrganizationIssuesassignedToreturnnullbut aren't nullable,getOrganizationMemberuser.avatarUrlis missing,updateProjectOwnershipowners[].nameis missing,listOrganizationIssueEventsevent.typeis missing,listOrganizationIssueTagValueskeyis missing,listTeamMembersuserisnull.deleteProject 401(sudo),updateOrganizationMember 404,listOrganizationTeams 400,updateProjectOwnership 403,updateOrganizationIssue 204(discard),getProject 302(renamed project).429from rate-limit tests and5xxfrom mocked timeouts. These probably need one global exemption, or a single shared documented response.That's 24 distinct mismatches from about 1,600 tests.
Next steps (not in this PR)
METHOD template status kind, likeresponse-coverage-baseline.txt.api-docsjob already builds the spec on every backend PR (its filter includesbackend_all), so the cheapest wiring is to uploadopenapi-derefed.jsonas an artifact there and setSENTRY_OPENAPI_RESPONSE_CHECK=reportin thebackend-testshards that download it. Alternatively, generate the spec inside the pytest controller (~16s per shard, always fresh).--
View Session [Sentry]