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:53:07 UTC
(grails-core) 26/40: test(h7): expand CascadeBehaviorFetcher and GrailsPropertyBinder coverage
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 b8da3692172d140c0f256becf65063ec26635799
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Tue Apr 7 22:14:07 2026 -0500
test(h7): expand CascadeBehaviorFetcher and GrailsPropertyBinder coverage
CascadeBehaviorFetcherSpec (+4 new tests, 32 total):
- EmbeddedCollection → ALL branch (L99)
- ManyToOne correctly owned/non-circular scenario (L117 path via hasOne/isHasOne)
- getCascadeBehaviour throws MappingException for null associated entity (L104)
- getCascadeBehaviour throws MappingException for unrecognized association type (L124)
- Added UnrecognizedTestAssociation abstract helper class
- Added EmbeddedCollOwner/EmbeddedItem, MtoAllA/MtoAllB domain helpers
GrailsPropertyBinderSpec (+2 new tests, 10 total):
- Test bind valid hasOne OneToOne (L84 branch)
- Test bind FK one-to-one / isValidHibernateOneToOne=false (L86 branch)
- Added PropertyBinderSpecHasOneOwner/Profile, PropertyBinderSpecFKOwner/Child helpers
Co-authored-by: Copilot <22...@users.noreply.github.com>
---
.../CascadeBehaviorFetcherSpec.groovy | 83 +++++++++++++++++++++-
.../domainbinding/GrailsPropertyBinderSpec.groovy | 71 +++++++++++++++++-
2 files changed, 151 insertions(+), 3 deletions(-)
diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcherSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcherSpec.groovy
index abc8792b68..54b057fdc3 100644
--- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcherSpec.groovy
+++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcherSpec.groovy
@@ -19,6 +19,11 @@
package org.grails.orm.hibernate.cfg.domainbinding
import jakarta.persistence.Embeddable
+import org.grails.datastore.mapping.model.types.Association
+import org.grails.datastore.mapping.model.PersistentEntity
+import org.grails.orm.hibernate.cfg.PropertyConfig
+import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty
+import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToOneProperty
import org.hibernate.MappingException
import spock.lang.Shared
import spock.lang.Unroll
@@ -80,7 +85,13 @@ class CascadeBehaviorFetcherSpec extends HibernateGormDatastoreSpec {
["many-to-one in composite id", CompositeIdManyToOne, "parent", CompositeIdParent, ALL.getValue()],
// --- Embedded Association Scenario ---
- ["embedded association", EOwner, "address", EAddress, ALL.getValue()]
+ ["embedded association", EOwner, "address", EAddress, ALL.getValue()],
+
+ // --- EmbeddedCollection Scenario ---
+ ["embedded collection", EmbeddedCollOwner, "items", EmbeddedItem, ALL.getValue()],
+
+ // --- ManyToOne correctly owned (non-circular) → ALL ---
+ ["many-to-one (correctly owned, non-circular)", MtoAllA, "b", MtoAllB, ALL.getValue()]
]
@Shared CascadeBehaviorFetcher fetcher = new CascadeBehaviorFetcher()
@@ -113,6 +124,36 @@ class CascadeBehaviorFetcherSpec extends HibernateGormDatastoreSpec {
where:
[description, ownerClass, associationName, childClass, expectation] << cascadeMetadataTestData
}
+
+ def "getCascadeBehaviour throws MappingException when associated entity is null for non-basic association"() {
+ given:
+ def association = Mock(HibernateManyToOneProperty)
+ association.getMappedForm() >> null
+ association.getType() >> Object
+ association.getAssociatedEntity() >> null
+
+ when:
+ fetcher.getCascadeBehaviour(association)
+
+ then:
+ thrown(MappingException)
+ }
+
+ def "getCascadeBehaviour throws MappingException for unrecognized association type"() {
+ given:
+ def association = Mock(UnrecognizedTestAssociation)
+ association.getMappedForm() >> null
+ association.getType() >> Object
+ def mockEntity = Mock(PersistentEntity)
+ association.getAssociatedEntity() >> mockEntity
+ association.isHasOne() >> false
+
+ when:
+ fetcher.getCascadeBehaviour(association)
+
+ then:
+ thrown(MappingException)
+ }
}
// --- Test Domain Classes ---
@@ -290,4 +331,42 @@ class EOwner {
static embedded = ['address']
}
-@Embeddable class EAddress { String street }
\ No newline at end of file
+@Embeddable class EAddress { String street }
+
+// --- EmbeddedCollection scenario (L99) ---
+@Embeddable class EmbeddedItem { String name }
+
+@Entity
+class EmbeddedCollOwner {
+ static hasMany = [items: EmbeddedItem]
+ static embedded = ['items']
+}
+
+// --- ManyToOne correctly owned, non-circular → ALL (L117) ---
+// Mutual belongsTo: both entities own each other.
+// MtoAllA.b → HibernateManyToOneProperty
+// isCorrectlyOwned() = MtoAllB.isOwningEntity(MtoAllA) = (MtoAllA in MtoAllB.owners) = true
+// isCircular() = MtoAllB.isAssignableFrom(MtoAllA) = false
+// → returns ALL at L117
+@Entity
+class MtoAllA {
+ MtoAllB b
+ static belongsTo = [b: MtoAllB]
+}
+
+@Entity
+class MtoAllB {
+ MtoAllA a
+ static belongsTo = [a: MtoAllA]
+}
+
+// --- Abstract helper for unrecognized association type mock (L124) ---
+abstract class UnrecognizedTestAssociation extends org.grails.datastore.mapping.model.types.Association<PropertyConfig>
+ implements HibernatePersistentProperty {
+ UnrecognizedTestAssociation(
+ org.grails.datastore.mapping.model.PersistentEntity owner,
+ org.grails.datastore.mapping.model.MappingContext context,
+ java.beans.PropertyDescriptor descriptor) {
+ super(owner, context, descriptor)
+ }
+}
\ No newline at end of file
diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy
index 2b298dd64c..861c668c16 100644
--- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy
+++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy
@@ -121,7 +121,11 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec {
PropertyBinderSpecEmployee,
PropertyBinderSpecSerializableEntity,
PropertyBinderSpecCustomEntity,
- PropertyBinderSpecCustomUserTypeCollection
+ PropertyBinderSpecCustomUserTypeCollection,
+ PropertyBinderSpecHasOneOwner,
+ PropertyBinderSpecHasOneProfile,
+ PropertyBinderSpecFKOwner,
+ PropertyBinderSpecFKChild
])
}
@@ -266,6 +270,40 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec {
value instanceof BasicValue
!(value instanceof org.hibernate.mapping.Collection)
}
+
+ void "Test bind valid hasOne property (HibernateOneToOneProperty.isValidHibernateOneToOne = true)"() {
+ given:
+ def binder = getGrailsDomainBinder()
+ def propertyBinder = getBinders(binder).propertyBinder
+ def persistentEntity = getPersistentEntity(PropertyBinderSpecHasOneOwner) as GrailsHibernatePersistentEntity
+ def rootClass = new RootClass(binder.getMetadataBuildingContext())
+ rootClass.setTable(new Table("HAS_ONE_OWNER"))
+ persistentEntity.setPersistentClass(rootClass)
+
+ when:
+ def profileProp = persistentEntity.getPropertyByName("profile") as HibernatePersistentProperty
+ Value value = propertyBinder.bindProperty(profileProp, null, EMPTY_PATH)
+
+ then:
+ value instanceof OneToOne
+ }
+
+ void "Test bind FK one-to-one property (HibernateOneToOneProperty.isValidHibernateOneToOne = false)"() {
+ given:
+ def binder = getGrailsDomainBinder()
+ def propertyBinder = getBinders(binder).propertyBinder
+ def persistentEntity = getPersistentEntity(PropertyBinderSpecFKOwner) as GrailsHibernatePersistentEntity
+ def rootClass = new RootClass(binder.getMetadataBuildingContext())
+ rootClass.setTable(new Table("FK_OWNER"))
+ persistentEntity.setPersistentClass(rootClass)
+
+ when:
+ def childProp = persistentEntity.getPropertyByName("child") as HibernatePersistentProperty
+ Value value = propertyBinder.bindProperty(childProp, null, EMPTY_PATH)
+
+ then:
+ value instanceof ManyToOne
+ }
}
@Entity
@@ -330,3 +368,34 @@ class PropertyBinderSpecCustomUserTypeCollection {
categories type: 'org.hibernate.type.YesNoConverter'
}
}
+
+// --- hasOne (valid Hibernate one-to-one) for L84 ---
+@Entity
+class PropertyBinderSpecHasOneProfile {
+ Long id
+ String bio
+ PropertyBinderSpecHasOneOwner owner
+ static belongsTo = [owner: PropertyBinderSpecHasOneOwner]
+}
+
+@Entity
+class PropertyBinderSpecHasOneOwner {
+ Long id
+ static hasOne = [profile: PropertyBinderSpecHasOneProfile]
+}
+
+// --- FK one-to-one (isValidHibernateOneToOne = false) for L86 ---
+// PropertyBinderSpecFKChild has belongsTo PropertyBinderSpecFKOwner,
+// making PropertyBinderSpecFKOwner.child the owning side with isValidHibernateOneToOne = false
+@Entity
+class PropertyBinderSpecFKChild {
+ Long id
+ PropertyBinderSpecFKOwner owner
+ static belongsTo = [owner: PropertyBinderSpecFKOwner]
+}
+
+@Entity
+class PropertyBinderSpecFKOwner {
+ Long id
+ PropertyBinderSpecFKChild child
+}