[libgraphql-core-v1] Task 16.6a: Validate @oneOf input object constraints#105
Merged
Conversation
Implements Task 16.6a from the v1 plan. The @OneOf directive was registered as a builtin since Task 14, but nothing enforced its type-validation constraints -- a schema like `input X @OneOf { a: Int! }` built silently despite being invalid per the September 2025 spec. Per the spec's Input Objects Type Validation rules, every field of a @OneOf input object must have a nullable type and must not declare a default value (each input represents exactly one choice, so a required field or an always-present default would be contradictory). The check lives in the existing InputObjectTypeValidator (already orchestrated by SchemaBuilder::build() step 4, so no new wiring), with two new precisely-targeted TypeValidationErrorKind variants rather than one merged kind -- a field violating both rules reports both errors, and each error's span points at the offending construct (the type annotation for nullability; the field for defaults). Both carry ErrorNote::spec links per the plan's error-note convention. Detection matches the directive annotation by name; argument-level validation of directive applications is out of scope here (owned by Task 16.6f). Tests: 5 unit tests (valid oneOf, non-nullable rejected, defaulted rejected, both-violations reports two errors, non-oneOf control) plus 2 end-to-end build_from_str tests proving the annotation survives the full parse -> build -> validate pipeline. Plan doc updated with completion notes per the Execution Protocol. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TipQtpvuLuHjHmRsVEgwJw
Contributor
There was a problem hiding this comment.
Pull request overview
Implements GraphQL September 2025 spec validation for @oneOf input objects in libgraphql-core-v1, ensuring schema builds fail when @oneOf fields violate the required constraints (outer type must be nullable; no default values).
Changes:
- Added
InputObjectTypeValidator::validate_oneof_constraints()and wired it into the existing input-object validation flow. - Introduced two new
TypeValidationErrorKindvariants for precise, separately-reportable oneOf violations (non-nullable field; default value). - Added unit + end-to-end tests covering valid/invalid
@oneOfcases, including list outer-nullability behavior, and updated the v1 plan doc completion notes.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| libgraphql-core-v1-plan.md | Marks Task 16.6a complete and documents implementation notes + the known extension-gap follow-up. |
| crates/libgraphql-core-v1/src/validators/input_object_type_validator.rs | Adds @oneOf constraint validation and runs it as part of input-object type validation. |
| crates/libgraphql-core-v1/src/schema/type_validation_error.rs | Adds dedicated error kinds/messages for @oneOf field constraint violations. |
| crates/libgraphql-core-v1/src/validators/tests/input_object_type_validator_tests.rs | Adds unit tests for @oneOf constraint enforcement (including list outer nullability). |
| crates/libgraphql-core-v1/src/schema/tests/schema_builder_tests.rs | Adds end-to-end build tests verifying @oneOf violations are surfaced through build_from_str. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
Summary
Task 16.6a (first of the six schema-hardening PRs from the Task 16.5 audit): implement the
@oneOfinput-object validation the audit found entirely missing —input X @oneOf { a: Int! }previously built without error despite being invalid per the September 2025 spec's Input Objects Type Validation rules.What & why
Per spec, every field of a
@oneOfinput object must (a) have a nullable type and (b) declare no default value — a oneOf input represents exactly one caller-supplied choice, so a required field or an always-present default is contradictory.validate_oneof_constraints()in the existingInputObjectTypeValidator(already orchestrated bySchemaBuilder::build()step 4 — zero new wiring)TypeValidationErrorKindvariants rather than one merged kind:InvalidNonNullableOneOfInputField(span → the offending type annotation, where the!lives) andInvalidOneOfInputFieldWithDefaultValue(span → the field). A field violating both reports both. Each carries anErrorNote::speclink[Int]!is rejected,[Int!]is accepted"oneOf"— sound becauseabsorb_directive()rejects user redefinition of builtins, so any annotation with that name is the genuine builtin. Argument-level directive-application validation is out of scope (owned by Task 16.6f)Tests (9 new)
7 unit: valid oneOf; non-nullable rejected; defaulted rejected; both-violations-on-one-field reports two errors; non-oneOf control (constraints must not leak);
[Int]!rejected /[Int!]accepted list-nullability pair.2 end-to-end via
build_from_str: proves the annotation survives the full parse → builder → validator pipeline, plus the valid-oneOf happy path.Known gap (tracked, blocked on 16.6b)
extend input X @oneOfis invisible today because all type extensions are silently dropped (16.6b's charter). The plan doc now requires a oneOf-via-extension regression test as part of 16.6b.Verification
cargo test --workspace— 1322 tests green (217 in v1, up from 208)cargo clippy --workspace --tests -- -Dwarnings— clean[x]boxes + Completion Notes per the Execution Protocol (same commit)🤖 Generated with Claude Code
https://claude.ai/code/session_01TipQtpvuLuHjHmRsVEgwJw
Generated by Claude Code