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
+}