Skip to content

fix!: implement validation for types in fragment condition#692

Open
stuebingerb wants to merge 1 commit into
mainfrom
fix/fix-fragment-condition-validation
Open

fix!: implement validation for types in fragment condition#692
stuebingerb wants to merge 1 commit into
mainfrom
fix/fix-fragment-condition-validation

Conversation

@stuebingerb

@stuebingerb stuebingerb commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Fragments can now only be defined on types that are actually possible at their position, e.g. you cannot define a fragment on Cat when the return type is known to be Dog. Similarly, fragments on interfaces and unions must be for one of the known possibleTypes.

Fixes #683

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Fragment validation

Layer / File(s) Summary
Enclosing-type fragment validation
kgraphql/src/main/kotlin/.../RequestInterpreter.kt
Fragment resolution now propagates optional enclosing types and validates compatibility using possible-type intersections.
Fragment condition and intersection coverage
kgraphql/src/test/kotlin/.../FragmentsSpecificationTest.kt
Tests cover invalid and compatible object, union, interface, and intersection fragment conditions.
Interface and union regression coverage
kgraphql/src/test/kotlin/.../typesystem/*SpecificationTest.kt
Tests cover mixed interface implementations, explicit union resolver types, and remove an obsolete ordering case.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement the requested fragment spread validation rules for incompatible types and possible-type checks.
Out of Scope Changes check ✅ Passed The modified tests and minor schema adjustments appear directly tied to the fragment-validation work.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title is conventional-commit style and clearly describes the fragment validation change.
Description check ✅ Passed The description matches the PR by explaining the new fragment-position validation rules and linking the issue.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fix-fragment-condition-validation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Fragments can now only be defined on types that are actually possible
at their position, e.g. you cannot define a fragment on `Cat` when the
return type is known to be `Dog`. Similarly, fragments on interfaces
and unions must be for one of the known `possibleTypes`.

Fixes #683

BREAKING CHANGE: invalid fragment conditions were previously simply
ignored and now cause the query to fail.
@stuebingerb
stuebingerb force-pushed the fix/fix-fragment-condition-validation branch from c49a64a to 8e3892e Compare July 15, 2026 16:58
@stuebingerb stuebingerb added the breaking Introduces a breaking change label Jul 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/specification/language/FragmentsSpecificationTest.kt (1)

605-719: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a named-fragment-spread variant for the impossible interface/union cases.

The new impossible-type tests for interfaces/unions/intersections only exercise inline fragments (... on X). The named-fragment-spread message format ("...of type '$fragmentName'...") is only covered for the plain object case (Lines 466-480). Adding one spread-based case for the interface/union path would close a small coverage gap in the new validation surface.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 66f615e9-884a-4695-a12c-c146bf95e51e

📥 Commits

Reviewing files that changed from the base of the PR and between 874c490 and c49a64a.

📒 Files selected for processing (5)
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/RequestInterpreter.kt
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/specification/language/FragmentsSpecificationTest.kt
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/specification/typesystem/InterfacesSpecificationTest.kt
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/specification/typesystem/ObjectsSpecificationTest.kt
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/specification/typesystem/UnionsSpecificationTest.kt
💤 Files with no reviewable changes (1)
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/specification/typesystem/ObjectsSpecificationTest.kt

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/RequestInterpreter.kt">

<violation number="1" location="kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/RequestInterpreter.kt:323">
P1: Invalid fragment conditions on union fields are still accepted and silently yield no selected fields, because `handleUnion` bypasses `validateForFragment`. Route its fragment/inline-fragment handling through the same applicability validation before building `memberChildren`.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

return when (type.kind) {
TypeKind.OBJECT -> listOf(type)
TypeKind.INTERFACE -> checkNotNull(type.possibleTypes)
TypeKind.UNION -> checkNotNull(type.possibleTypes)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Invalid fragment conditions on union fields are still accepted and silently yield no selected fields, because handleUnion bypasses validateForFragment. Route its fragment/inline-fragment handling through the same applicability validation before building memberChildren.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/RequestInterpreter.kt, line 323:

<comment>Invalid fragment conditions on union fields are still accepted and silently yield no selected fields, because `handleUnion` bypasses `validateForFragment`. Route its fragment/inline-fragment handling through the same applicability validation before building `memberChildren`.</comment>

<file context>
@@ -311,6 +314,27 @@ internal class RequestInterpreter(private val schemaModel: SchemaModel) {
+                return when (type.kind) {
+                    TypeKind.OBJECT -> listOf(type)
+                    TypeKind.INTERFACE -> checkNotNull(type.possibleTypes)
+                    TypeKind.UNION -> checkNotNull(type.possibleTypes)
+                    else -> emptyList()
+                }
</file context>

@stuebingerb stuebingerb Jul 17, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

That might actually be correct. I have an upcoming refactoring for union types prepared that would simply re-use regular fragments, though, so I'd like to postpone that topic. Please keep this comment in mind and check again with that upcoming refactoring PR.

@stuebingerb stuebingerb Jul 17, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@cubic-dev-ai Please create a test case that would currently fail to verify the behavior is correct after the refactoring.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@stuebingerb I can help — what did you want me to do?

  • Manage learnings: reply in the relevant thread and say @cubic add this to our learnings (I'll extract the learning from context). For delete/update, paste the learning link from Settings → AI review → Learnings.
  • Make a code change: @cubic fix this and push commits (or open a PR)
  • Ask a question: @cubic why is this needed?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@cubic create a test that demonstrates the problem of missing fragment validation for union fields

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@stuebingerb Fix with cubic is available during trial and on the Pro plan. Upgrade your plan to use this feature. https://www.cubic.dev/settings?tab=subscription

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Marvellous, thanks for suggesting it then.
Alright, @cubic why is this needed?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@stuebingerb The original comment flagged that handleUnion processes its own fragments directly (matching by type name against possibleTypes) instead of routing through validateForFragment. So if you write:

{
    unions {
        ... on Actor { name }   # Actor is NOT a member of the union
    }
}

handleUnion silently returns emptyList() for that fragment — no error, no warning, just missing fields. The new validateForFragment logic you added would catch that with:

Invalid type 'Actor' in type condition on inline fragment of type 'TopUnion'; must be one of '[Union1, Union2]'

But handleUnion never calls it, so those invalid conditions slip through.

Same issue applies to FragmentSpreadNode inside union selections — handleUnion calls ctx.getFragment() directly which doesn't validate against the enclosing union type.

That said — you mentioned you have a refactoring coming that will unify union fragment handling with the regular path. If that refactoring routes union fragments through findFragmentType (which calls validateForFragment), then the gap closes automatically and this comment becomes obsolete. The test I suggested was just to make the gap visible before the refactoring.

Tip: get faster answers by chatting with cubic’s review copilot in the review UI

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Branchfix/fix-fragment-condition-validation
Testbedubuntu-latest
Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
de.stuebingerb.kgraphql.FunctionExecutionBenchmark.benchmarkFunctionExecution📈 view plot
🚷 view threshold
5,463,716.27 ops/s
(-4.97%)Baseline: 5,749,665.45 ops/s
4,934,220.67 ops/s
(90.31%)
de.stuebingerb.kgraphql.ParallelExecutionBenchmark.queryBenchmark📈 view plot
🚷 view threshold
1.30 ops/s
(+0.03%)Baseline: 1.30 ops/s
1.30 ops/s
(99.80%)
de.stuebingerb.kgraphql.QueryBenchmark.executionError📈 view plot
🚷 view threshold
21,878.05 ops/s
(+25.01%)Baseline: 17,500.83 ops/s
10,851.17 ops/s
(49.60%)
de.stuebingerb.kgraphql.QueryBenchmark.inputFromDocument📈 view plot
🚷 view threshold
24,973.84 ops/s
(+15.59%)Baseline: 21,605.90 ops/s
13,915.25 ops/s
(55.72%)
de.stuebingerb.kgraphql.QueryBenchmark.inputFromVariable📈 view plot
🚷 view threshold
24,508.11 ops/s
(+17.62%)Baseline: 20,837.16 ops/s
13,654.28 ops/s
(55.71%)
de.stuebingerb.kgraphql.QueryBenchmark.largeList📈 view plot
🚷 view threshold
4.60 ops/s
(-2.29%)Baseline: 4.70 ops/s
4.07 ops/s
(88.48%)
de.stuebingerb.kgraphql.QueryBenchmark.largeListWithFragment📈 view plot
🚷 view threshold
5.19 ops/s
(-0.72%)Baseline: 5.23 ops/s
4.51 ops/s
(87.01%)
de.stuebingerb.kgraphql.QueryBenchmark.manyChildren📈 view plot
🚷 view threshold
193.16 ops/s
(+6.01%)Baseline: 182.21 ops/s
137.41 ops/s
(71.14%)
de.stuebingerb.kgraphql.QueryBenchmark.manyChildrenWithFragment📈 view plot
🚷 view threshold
205.73 ops/s
(+6.67%)Baseline: 192.87 ops/s
148.17 ops/s
(72.02%)
de.stuebingerb.kgraphql.QueryBenchmark.manyDataChildren📈 view plot
🚷 view threshold
8.91 ops/s
(+0.10%)Baseline: 8.90 ops/s
8.77 ops/s
(98.43%)
de.stuebingerb.kgraphql.QueryBenchmark.manyOperations📈 view plot
🚷 view threshold
311.51 ops/s
(+2.86%)Baseline: 302.84 ops/s
235.63 ops/s
(75.64%)
de.stuebingerb.kgraphql.QueryBenchmark.manyOperationsWithFragment📈 view plot
🚷 view threshold
320.05 ops/s
(+1.33%)Baseline: 315.86 ops/s
251.79 ops/s
(78.67%)
de.stuebingerb.kgraphql.QueryBenchmark.nestedObject📈 view plot
🚷 view threshold
9,114.08 ops/s
(+17.72%)Baseline: 7,742.05 ops/s
6,092.12 ops/s
(66.84%)
de.stuebingerb.kgraphql.RequestCachingBenchmark.invalidRequest📈 view plot
🚷 view threshold
142,801.09 ops/s
(+0.82%)Baseline: 141,634.58 ops/s
135,326.86 ops/s
(94.77%)
de.stuebingerb.kgraphql.RequestCachingBenchmark.largeRequest📈 view plot
🚷 view threshold
9,624.02 ops/s
(+20.30%)Baseline: 8,000.09 ops/s
5,910.57 ops/s
(61.41%)
de.stuebingerb.kgraphql.RequestCachingBenchmark.smallRequest📈 view plot
🚷 view threshold
13,711.96 ops/s
(+18.87%)Baseline: 11,534.86 ops/s
8,279.44 ops/s
(60.38%)
de.stuebingerb.kgraphql.SimpleExecutionOverheadBenchmark.benchmark📈 view plot
🚷 view threshold
488,179.47 ops/s
(+3.54%)Baseline: 471,509.66 ops/s
443,990.39 ops/s
(90.95%)
🐰 View full continuous benchmarking report in Bencher

@stuebingerb stuebingerb changed the title fix: implement validation for types in fragment condition fix!: implement validation for types in fragment condition Jul 15, 2026
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.95238% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.05%. Comparing base (874c490) to head (8e3892e).

Files with missing lines Patch % Lines
...rb/kgraphql/schema/structure/RequestInterpreter.kt 80.95% 1 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #692      +/-   ##
==========================================
- Coverage   84.06%   84.05%   -0.01%     
==========================================
  Files         151      151              
  Lines        5008     5024      +16     
  Branches      864      870       +6     
==========================================
+ Hits         4210     4223      +13     
- Misses        492      493       +1     
- Partials      306      308       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Introduces a breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing/incorrect fragment condition validation

1 participant