Add test coverage for AbstractDetachedCriteria and rx DetachedCriteria - #16143
Add test coverage for AbstractDetachedCriteria and rx DetachedCriteria#16143borinquenkid wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request significantly expands unit test coverage around detached criteria usage in both the core (AbstractDetachedCriteria via grails.gorm.DetachedCriteria) and reactive (grails.gorm.rx.DetachedCriteria) stacks, while also fixing reactive subquery handling and simplifying query preparation behavior.
Changes:
- Add extensive mock-based Spock specs to drive high line/method coverage for
AbstractDetachedCriteriaand reactiveDetachedCriteria. - Fix reactive closure-based subqueries by introducing a
QueryableCriteria-compatibleSubqueryAdapter(avoids runtimeClassCastException). - Simplify reactive
prepareQuery()by removing redundant fetch-strategy application and relying onDynamicFinder.applyDetachedCriteria().
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| grails-datamapping-rx/src/test/groovy/org/grails/gorm/rx/api/DetachedCriteriaQuerySpec.groovy | New spec covering reactive query execution paths that go through prepareQuery() and static API lookup. |
| grails-datamapping-rx/src/test/groovy/grails/gorm/rx/DetachedCriteriaSpec.groovy | New spec verifying reactive DetachedCriteria override methods delegate correctly and return the narrowed type. |
| grails-datamapping-rx/src/main/groovy/grails/gorm/rx/DetachedCriteria.groovy | Fix reactive subquery construction and remove redundant fetch-strategy application during query preparation. |
| grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/query/criteria/AbstractDetachedCriteriaSpec.groovy | New comprehensive spec driving coverage of AbstractDetachedCriteria behavior via the concrete DetachedCriteria. |
| grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/criteria/AbstractDetachedCriteria.groovy | Small correctness/cleanup tweaks (definite assignment, safer list access, variable shadowing cleanup). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
The suggestion to add a JoinType assertion to the test is appropriate. Verifying that |
…gy test Addresses Copilot review feedback on PR #16143: the existing fetch-strategy test only covered join(String)/select(String), not the JoinType-preserving behavior the removed duplicate loop had been silently discarding. Adds a dedicated test verifying query.join(property, joinType) is called when a custom JoinType is set. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (1)
grails-datamapping-rx/src/test/groovy/org/grails/gorm/rx/api/DetachedCriteriaQuerySpec.groovy:42
- The class is public, so the Javadoc claim that it's "Package-local" is inaccurate. Either adjust the wording, or make the spec package-scoped (e.g., via @PackageScope) if that’s the intent.
* Package-local so the test can call the {@code protected static}
* {@code RxGormEnhancer.registerEntityWithConnectionSource} directly rather than driving the
* full {@code registerEntity} multi-tenancy/connection-source resolution machinery.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 8.1.x #16143 +/- ##
==================================================
+ Coverage 52.8038% 53.4558% +0.6519%
- Complexity 18863 19493 +630
==================================================
Files 2079 2081 +2
Lines 97207 98994 +1787
Branches 16873 17361 +488
==================================================
+ Hits 51329 52918 +1589
- Misses 38468 38531 +63
- Partials 7410 7545 +135
🚀 New features to boost your workflow:
|
|
Let's just combine this PR with the other one? It looks like the tests that are commented were never uncommented and we can do all of this in one PR. @borinquenkid |
@jdaugherty I would prefer to keep them separate so they are as focused as possible |
Adds mock-based unit specs for AbstractDetachedCriteria (via grails.gorm.DetachedCriteria) and for the reactive grails.gorm.rx.DetachedCriteria, taking both from ~0% to full line/method coverage without needing a real datastore. Writing the rx specs surfaced two real bugs, both fixed here: - buildQueryableCriteria() cast the built DetachedCriteria to QueryableCriteria, but the rx class never implemented that interface, so every closure-based subquery (in, inList, notIn, eqAll/gtAll/ltAll/geAll/leAll, gtSome/geSome/ltSome/leSome) threw a ClassCastException at runtime. Fixed with a small SubqueryAdapter that extends the shared AbstractDetachedCriteria base directly, since the reactive class's own find()/list() return Observable and can't coexist with QueryableCriteria's T/List<T> signatures on the same type. - prepareQuery() applied fetch strategies (join/select) twice: once via DynamicFinder.applyDetachedCriteria(), then again via a redundant hand-rolled loop that also ignored custom JoinTypes. Removed the dead duplicate. Also fixes a handful of definite-assignment/shadowing/raw-getAt warnings in AbstractDetachedCriteria (uninitialized `prop` in createAlias, a local variable named `criteria` shadowing the instance field of the same name in clone(), and negative-index List access replaced with getLast()). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…gy test Addresses Copilot review feedback on PR #16143: the existing fetch-strategy test only covered join(String)/select(String), not the JoinType-preserving behavior the removed duplicate loop had been silently discarding. Adds a dedicated test verifying query.join(property, joinType) is called when a custom JoinType is set. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
7610e29 to
7de0bc6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (2)
grails-datamapping-rx/src/main/groovy/grails/gorm/rx/DetachedCriteria.groovy:653
- The extra cast chain
(Class<Observable<T>>) (Class) targetClassis redundant (sincetargetClassis already aClass), and it makes the intent harder to read. A single cast is enough here.
if (additionalCriteria != null) {
def additionalDetached = new DetachedCriteria((Class<Observable<T>>) (Class) targetClass).build(additionalCriteria)
DynamicFinder.applyDetachedCriteria(query, additionalDetached)
grails-datamapping-rx/src/test/groovy/org/grails/gorm/rx/api/DetachedCriteriaQuerySpec.groovy:119
- This spec name says
get(Closure)but the test actually callscriteria.get()with no closure. Renaming the feature text avoids confusion about which overload is being exercised.
void "get(Closure) applies a max of 1 and delegates to the no-arg RxQuery#singleResult"() {
| } | ||
| if (junctions) { | ||
| junctions[-1].add(criterion) | ||
| junctions.getLast().add(criterion) |
There was a problem hiding this comment.
-1 is an accepted way to get the last when the array is defined, why change it?
| datastoreClient.createStaticApi(persistentEntity, ConnectionSource.DEFAULT) >> staticApi | ||
| datastoreClient.createInstanceApi(persistentEntity, ConnectionSource.DEFAULT) >> Mock(RxGormInstanceApi) | ||
| datastoreClient.createValidationApi(persistentEntity, ConnectionSource.DEFAULT) >> Mock(RxGormValidationApi) | ||
| RxGormEnhancer.registerEntityWithConnectionSource(persistentEntity, ConnectionSource.DEFAULT, ConnectionSource.DEFAULT, datastoreClient) |
There was a problem hiding this comment.
Almost all of this spec duplicates coverage that grails.gorm.rx.DetachedCriteriaSpec already provides on 8.1.x (find/findAll/get/toList/list/getCount/updateAll/deleteAll/toQuery/toObservable/subscribe, the prepareQuery max/offset and fetch-strategy paths, and additional-criteria merging). The only new behavior covered here is the custom JoinType passthrough.
That existing spec also shows this setup works through the public RxGormEnhancer.registerEntity(entity, datastoreClient) with a stubbed ConnectionSources, so the package-local placement to reach the protected registerEntityWithConnectionSource is not needed.
Suggest moving the "prepareQuery passes a custom JoinType through to the query" feature method into the existing DetachedCriteriaSpec and dropping this file.
This is the reason I think this should have been merged into the other PR
|
|
||
| void "eq adds an Equals criterion"() { | ||
| given: | ||
| def criteria = new DetachedCriteria(TestEntity) |
There was a problem hiding this comment.
TestEntity here resolves to the class declared at the bottom of DetachedCriteriaCloneSpec.groovy. Depending on a fixture defined inside another spec's file is fragile - refactoring that spec would silently break this one. Suggest either moving TestEntity into its own file in this package or declaring a dedicated fixture class in this file.
- Revert the accepted `junctions[-1]` idiom in AbstractDetachedCriteria.add() rather than switching to getLast(). - Fold the rx JoinType passthrough coverage into the existing grails.gorm.rx.DetachedCriteriaSpec (which already exercises this setup through the public RxGormEnhancer.registerEntity API) and drop the duplicate, package-local DetachedCriteriaQuerySpec. - Move the shared TestEntity fixture out of DetachedCriteriaCloneSpec into its own file so AbstractDetachedCriteriaSpec no longer depends on another spec's inline class. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
🚨 TestLens detected 1 failed test 🚨Here is what you can do:
Test SummaryCI / Functional Tests (Java 25, indy=false, shard 1) > :grails-test-examples-gsp-sitemesh3:integrationTest
🏷️ Commit: 8a544b4 Test FailuresEndToEndSpec > async multiple levels of layouts (:grails-test-examples-gsp-sitemesh3:integrationTest in CI / Functional Tests (Java 25, indy=false, shard 1))
Rerun ControlsSelect tests to mute in this pull request:
Reuse successful test results:
Click the checkbox to trigger a rerun:
Learn more about TestLens at testlens.app/docs. |
Summary
AbstractDetachedCriteria(viagrails.gorm.DetachedCriteria) and for the reactivegrails.gorm.rx.DetachedCriteria, taking both from ~0% to full line/method coverage without needing a real datastore.buildQueryableCriteria()cast the builtDetachedCriteriatoQueryableCriteria, but the rx class never implemented that interface, so every closure-based subquery (in,inList,notIn,eqAll/gtAll/ltAll/geAll/leAll,gtSome/geSome/ltSome/leSome) threw aClassCastExceptionat runtime. Fixed with a smallSubqueryAdapterthat extends the sharedAbstractDetachedCriteriabase directly, since the reactive class's ownfind()/list()returnObservableand can't coexist withQueryableCriteria'sT/List<T>signatures on the same type.prepareQuery()applied fetch strategies (join/select) twice: once viaDynamicFinder.applyDetachedCriteria(), then again via a redundant hand-rolled loop that also ignored customJoinTypes. Removed the dead duplicate.getAtwarnings inAbstractDetachedCriteria(uninitializedpropincreateAlias, a local variable namedcriteriashadowing the instance field of the same name inclone(), and negative-indexListaccess replaced withgetLast()).Test plan
:grails-datamapping-core:test— 71 new tests inAbstractDetachedCriteriaSpec, all pass:grails-datamapping-rx:test— 45 new tests acrossDetachedCriteriaSpecandDetachedCriteriaQuerySpec, all pass:grails-datamapping-core:codeStyleand:grails-datamapping-rx:codeStylecleanjacocoTestReportthatAbstractDetachedCriteriaandgrails.gorm.rx.DetachedCriteria(plus its newSubqueryAdapter) are at ~100% line/method coverage🤖 Generated with Claude Code