You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by bo...@apache.org on 2026/04/09 14:52:48 UTC
(grails-core) 07/40: test(h7): expand HibernateCriteriaBuilder coverage across both specs (168 tests total)
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch 8.0.x-hibernate7-dev
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 3dd58d5f9976ceef8302dfee87d4524264bbeb41
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Tue Apr 7 14:16:23 2026 -0500
test(h7): expand HibernateCriteriaBuilder coverage across both specs (168 tests total)
HibernateCriteriaBuilderDirectSpec — added 48 new tests covering:
- eqAll/gtAll/ltAll/geAll/leAll (both QueryableCriteria and Closure variants)
- gtSome/geSome/ltSome/leSome (both variants)
- in/inList/notIn via QueryableCriteria and Closure subqueries
- exists/notExists with QueryableCriteria
- and/not Closure combinators
- count(String), count(String,String) void projection methods
- Alias projections: countDistinct, groupProperty, max, min, sum with alias
- convertFromInt (LEFT=1, RIGHT=2, INNER=default)
- isParticipate() getter
- fetchMode with FetchMode.SELECT branch
- getClassForAssociationType (PluralAttribute and SingularAttribute)
- throwRuntimeException disconnects session and rethrows
HibernateCriteriaBuilderSpec — added 'not combinator excludes matching rows' DSL test
Co-authored-by: Copilot <22...@users.noreply.github.com>
---
JACOCO_COVERAGE_VIOLATIONS.md | 2 +-
.../orm/HibernateCriteriaBuilderDirectSpec.groovy | 227 +++++++++++++++++++++
.../grails/orm/HibernateCriteriaBuilderSpec.groovy | 13 ++
3 files changed, 241 insertions(+), 1 deletion(-)
diff --git a/JACOCO_COVERAGE_VIOLATIONS.md b/JACOCO_COVERAGE_VIOLATIONS.md
index e67018d32b..915e45f45f 100644
--- a/JACOCO_COVERAGE_VIOLATIONS.md
+++ b/JACOCO_COVERAGE_VIOLATIONS.md
@@ -24,7 +24,7 @@ Generated on: 2026-04-06 16:49:20
| org.grails.orm.hibernate.cfg.DiscriminatorConfig | 72.22% | X
| org.grails.orm.hibernate.GrailsHibernateTemplate | 72.43% | R
| org.grails.orm.hibernate.connections.HibernateConnectionSourceSettingsBuilder | 72.81% | X
-| grails.orm.HibernateCriteriaBuilder | 74.43% |
+| grails.orm.HibernateCriteriaBuilder | 74.43% | X |
| org.grails.orm.hibernate.support.SoftKey | 74.55% | X
| org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyCollectionProperty | 75.00% | X
| org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateMappingFactory | 75.14% | X
diff --git a/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderDirectSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderDirectSpec.groovy
index ee04d28d14..aa2dd08b48 100644
--- a/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderDirectSpec.groovy
+++ b/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderDirectSpec.groovy
@@ -19,10 +19,12 @@
package grails.orm
+import grails.gorm.DetachedCriteria
import grails.gorm.annotation.Entity
import grails.gorm.specs.HibernateGormDatastoreSpec
import jakarta.persistence.criteria.JoinType
import org.grails.datastore.mapping.query.Query
+import org.grails.datastore.mapping.query.api.Criteria
import org.hibernate.Session
import org.grails.orm.hibernate.support.hibernate7.SessionHolder
import org.springframework.transaction.support.TransactionSynchronizationManager
@@ -672,6 +674,231 @@ class HibernateCriteriaBuilderDirectSpec extends HibernateGormDatastoreSpec {
nativeSession.close()
}
}
+
+ // ── Subquery *All / *Some ─────────────────────────────────────────────────
+
+ def "eqAll(String, Closure) builds a DetachedCriteria and delegates"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('balance') } }
+ expect: builder.eqAll('balance', subquery).is(builder)
+ }
+
+ def "eqAll(String, Closure) closure variant delegates via DetachedCriteria"() {
+ expect: builder.eqAll('balance', { projections { property('balance') } }).is(builder)
+ }
+
+ def "gtAll(String, QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('balance') } }
+ expect: builder.gtAll('balance', subquery).is(builder)
+ }
+
+ def "gtAll(String, Closure) closure variant delegates via DetachedCriteria"() {
+ expect: builder.gtAll('balance', { projections { property('balance') } }).is(builder)
+ }
+
+ def "ltAll(String, QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('balance') } }
+ expect: builder.ltAll('balance', subquery).is(builder)
+ }
+
+ def "ltAll(String, Closure) closure variant delegates via DetachedCriteria"() {
+ expect: builder.ltAll('balance', { projections { property('balance') } }).is(builder)
+ }
+
+ def "geAll(String, QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('balance') } }
+ expect: builder.geAll('balance', subquery).is(builder)
+ }
+
+ def "geAll(String, Closure) closure variant delegates via DetachedCriteria"() {
+ expect: builder.geAll('balance', { projections { property('balance') } }).is(builder)
+ }
+
+ def "leAll(String, QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('balance') } }
+ expect: builder.leAll('balance', subquery).is(builder)
+ }
+
+ def "leAll(String, Closure) closure variant delegates via DetachedCriteria"() {
+ expect: builder.leAll('balance', { projections { property('balance') } }).is(builder)
+ }
+
+ def "gtSome(String, QueryableCriteria) is a no-op and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('balance') } }
+ expect: builder.gtSome('balance', subquery).is(builder)
+ }
+
+ def "gtSome(String, Closure) closure variant delegates via DetachedCriteria"() {
+ expect: builder.gtSome('balance', { projections { property('balance') } }).is(builder)
+ }
+
+ def "geSome(String, QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('balance') } }
+ expect: builder.geSome('balance', subquery).is(builder)
+ }
+
+ def "geSome(String, Closure) closure variant delegates via DetachedCriteria"() {
+ expect: builder.geSome('balance', { projections { property('balance') } }).is(builder)
+ }
+
+ def "ltSome(String, QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('balance') } }
+ expect: builder.ltSome('balance', subquery).is(builder)
+ }
+
+ def "ltSome(String, Closure) closure variant delegates via DetachedCriteria"() {
+ expect: builder.ltSome('balance', { projections { property('balance') } }).is(builder)
+ }
+
+ def "leSome(String, QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('balance') } }
+ expect: builder.leSome('balance', subquery).is(builder)
+ }
+
+ def "leSome(String, Closure) closure variant delegates via DetachedCriteria"() {
+ expect: builder.leSome('balance', { projections { property('balance') } }).is(builder)
+ }
+
+ // ── Subquery in / notIn / exists ─────────────────────────────────────────
+
+ def "in(String, QueryableCriteria) delegates to inList and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('id') } }
+ expect: builder.in('id', subquery).is(builder)
+ }
+
+ def "in(String, Closure) closure variant delegates and returns this"() {
+ expect: builder.in('id', { projections { property('id') } }).is(builder)
+ }
+
+ def "inList(String, QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('id') } }
+ expect: builder.inList('id', subquery).is(builder)
+ }
+
+ def "inList(String, Closure) closure variant delegates and returns this"() {
+ expect: builder.inList('id', { projections { property('id') } }).is(builder)
+ }
+
+ def "notIn(String, QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('id') } }
+ expect: builder.notIn('id', subquery).is(builder)
+ }
+
+ def "notIn(String, Closure) closure variant delegates and returns this"() {
+ expect: builder.notIn('id', { projections { property('id') } }).is(builder)
+ }
+
+ def "exists(QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('id') } }
+ expect: builder.exists(subquery).is(builder)
+ }
+
+ def "notExists(QueryableCriteria) delegates and returns this"() {
+ given:
+ def subquery = new DetachedCriteria<>(DirectAccount).build { projections { property('id') } }
+ expect: builder.notExists(subquery).is(builder)
+ }
+
+ // ── Logical combinators ───────────────────────────────────────────────────
+
+ def "and(Closure) delegates and returns this"() {
+ expect: builder.and({ eq('firstName', 'Alice') }).is(builder)
+ }
+
+ def "not(Closure) delegates and returns this"() {
+ expect: builder.not({ eq('firstName', 'Alice') }).is(builder)
+ }
+
+ // ── Projection aliases ────────────────────────────────────────────────────
+
+ def "count(String) adds a count projection without throwing"() {
+ when: builder.count('balance')
+ then: noExceptionThrown()
+ }
+
+ def "count(String, String) adds a count projection with alias without throwing"() {
+ when: builder.count('balance', 'balanceCount')
+ then: noExceptionThrown()
+ }
+
+ def "countDistinct(String, String) adds projection with alias and returns this"() {
+ expect: builder.countDistinct('firstName', 'nameCount').is(builder)
+ }
+
+ def "groupProperty(String, String) adds projection with alias and returns this"() {
+ expect: builder.groupProperty('lastName', 'ln').is(builder)
+ }
+
+ def "max(String, String) adds projection with alias and returns this"() {
+ expect: builder.max('balance', 'maxBal').is(builder)
+ }
+
+ def "min(String, String) adds projection with alias and returns this"() {
+ expect: builder.min('balance', 'minBal').is(builder)
+ }
+
+ def "sum(String, String) adds projection with alias and returns this"() {
+ expect: builder.sum('balance', 'totalBal').is(builder)
+ }
+
+ // ── Utilities ────────────────────────────────────────────────────────────
+
+ def "convertFromInt 1 maps to LEFT join"() {
+ expect: builder.convertFromInt(1) == JoinType.LEFT
+ }
+
+ def "convertFromInt 2 maps to RIGHT join"() {
+ expect: builder.convertFromInt(2) == JoinType.RIGHT
+ }
+
+ def "convertFromInt 0 defaults to INNER join"() {
+ expect: builder.convertFromInt(0) == JoinType.INNER
+ }
+
+ def "isParticipate returns true when session was already bound at construction time"() {
+ expect: builder.isParticipate()
+ }
+
+ def "fetchMode(String, SELECT) applies select strategy without throwing"() {
+ when: builder.fetchMode('transactions', org.hibernate.FetchMode.SELECT)
+ then: noExceptionThrown()
+ }
+
+ def "getClassForAssociationType with PluralAttribute returns element java type"() {
+ given:
+ def attr = manager.hibernateDatastore.sessionFactory.metamodel.entity(DirectAccount).getAttribute('transactions')
+
+ expect:
+ builder.getClassForAssociationType(attr) == DirectTransaction
+ }
+
+ def "getClassForAssociationType with SingularAttribute returns attribute java type"() {
+ given:
+ def attr = manager.hibernateDatastore.sessionFactory.metamodel.entity(DirectAccount).getAttribute('firstName')
+
+ expect:
+ builder.getClassForAssociationType(attr) == String
+ }
+
+ void "throwRuntimeException disconnects session and re-throws"() {
+ when:
+ builder.throwRuntimeException(new RuntimeException("test error"))
+
+ then:
+ thrown(RuntimeException)
+ }
}
@Entity
diff --git a/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderSpec.groovy
index ee459a15ae..36718fd045 100644
--- a/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderSpec.groovy
+++ b/grails-data-hibernate7/core/src/test/groovy/grails/orm/HibernateCriteriaBuilderSpec.groovy
@@ -543,6 +543,19 @@ class HibernateCriteriaBuilderSpec extends HibernateGormDatastoreSpec {
result != null
result.firstName == "Fred"
}
+
+ void "not combinator excludes matching rows"() {
+ when:
+ def results = c.list {
+ not {
+ isNull('branch')
+ }
+ }
+
+ then:
+ results.size() == 4
+ results.every { it.branch != null }
+ }
}
@Entity