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/12 00:37:15 UTC

(grails-core) 04/17: docs: Update JACOCO_COVERAGE_VIOLATIONS.md and add HibernateAssociationSpec

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 985df2bff6770d7c1227e2776be29cc003b8abcc
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Sat Apr 11 16:32:27 2026 -0500

    docs: Update JACOCO_COVERAGE_VIOLATIONS.md and add HibernateAssociationSpec
---
 .../hibernate/HibernateAssociationSpec.groovy      | 216 +++++++++++++++++++++
 1 file changed, 216 insertions(+)

diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateAssociationSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateAssociationSpec.groovy
new file mode 100644
index 0000000000..d44098ae47
--- /dev/null
+++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateAssociationSpec.groovy
@@ -0,0 +1,216 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.orm.hibernate.cfg.domainbinding.hibernate
+
+import spock.lang.Specification
+import org.hibernate.MappingException
+import org.hibernate.mapping.Property
+import org.hibernate.mapping.ManyToOne
+import org.hibernate.mapping.BasicValue
+import org.hibernate.mapping.Value
+import org.grails.datastore.mapping.model.PersistentProperty
+import org.grails.datastore.mapping.model.PersistentEntity
+import org.grails.datastore.mapping.model.PropertyMapping
+import org.grails.datastore.mapping.model.ClassMapping
+import org.grails.datastore.mapping.reflect.EntityReflector
+import org.grails.orm.hibernate.cfg.PropertyConfig
+import org.grails.orm.hibernate.cfg.Mapping
+import java.lang.annotation.RetentionPolicy
+
+class HibernateAssociationSpec extends Specification {
+
+    def "isAssociationColumnNullable defaults to true"() {
+        given:
+        def assoc = new TestHibernateAssociation()
+
+        expect:
+        assoc.isAssociationColumnNullable()
+    }
+
+    def "getHibernateInverseSide returns casted inverse side"() {
+        given:
+        def inverse = Mock(HibernateAssociation)
+        def assoc = new TestHibernateAssociation(inverseSide: inverse)
+
+        expect:
+        assoc.getHibernateInverseSide() == inverse
+    }
+
+    def "getHibernateAssociatedEntity returns casted associated entity"() {
+        given:
+        def entity = Mock(GrailsHibernatePersistentEntity)
+        def assoc = new TestHibernateAssociation(associatedEntity: entity)
+
+        expect:
+        assoc.getHibernateAssociatedEntity() == entity
+    }
+
+    def "getReferencedEntityName returns entity name"() {
+        given:
+        def entity = Mock(GrailsHibernatePersistentEntity)
+        entity.getName() >> "Foo"
+        def assoc = new TestHibernateAssociation(associatedEntity: entity)
+
+        expect:
+        assoc.getReferencedEntityName() == "Foo"
+    }
+
+    def "validateAssociation throws exception if userType is present"() {
+        given:
+        def config = new PropertyConfig(type: Object)
+        def assoc = new TestHibernateAssociation(name: "myAssoc", type: List, mappedForm: config)
+
+        when:
+        assoc.validateAssociation()
+
+        then:
+        def e = thrown(MappingException)
+        e.message == "Cannot bind association property [myAssoc] of type [interface java.util.List] to a user type"
+    }
+
+    def "validateAssociation does not throw exception if userType is null"() {
+        given:
+        def assoc = new TestHibernateAssociation()
+
+        when:
+        assoc.validateAssociation()
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "isBidirectionalManyToOneWithListMapping returns true for bidirectional list mapping"() {
+        given:
+        def inverse = Mock(PersistentProperty)
+        def assoc = new TestHibernateAssociation(
+            bidirectional: true,
+            inverseSide: inverse,
+            type: List
+        )
+        def prop = Mock(Property)
+        def manyToOne = GroovyMock(ManyToOne)
+        prop.getValue() >> manyToOne
+
+        expect:
+        assoc.isBidirectionalManyToOneWithListMapping(prop)
+    }
+
+    def "isBidirectionalManyToOneWithListMapping returns false for various conditions"() {
+        expect:
+        assoc.isBidirectionalManyToOneWithListMapping(prop) == result
+
+        where:
+        assoc | prop | result
+        new TestHibernateAssociation(bidirectional: false) | createMockProperty(GroovyMock(ManyToOne)) | false
+        new TestHibernateAssociation(bidirectional: true, inverseSide: null) | createMockProperty(GroovyMock(ManyToOne)) | false
+        new TestHibernateAssociation(bidirectional: true, inverseSide: Mock(PersistentProperty), type: String) | createMockProperty(GroovyMock(ManyToOne)) | false
+        new TestHibernateAssociation(bidirectional: true, inverseSide: Mock(PersistentProperty), type: List) | null | false
+        new TestHibernateAssociation(bidirectional: true, inverseSide: Mock(PersistentProperty), type: List) | createMockProperty(GroovyMock(BasicValue)) | false
+    }
+
+    private Property createMockProperty(Value value) {
+        def prop = Mock(Property)
+        prop.getValue() >> value
+        return prop
+    }
+
+    def "getTypeName returns null if propertyType matches type and associated entity exists"() {
+        given:
+        def entity = Mock(GrailsHibernatePersistentEntity)
+        def assoc = new TestHibernateAssociation(type: List, associatedEntity: entity)
+
+        expect:
+        assoc.getTypeName(List, null, null) == null
+    }
+
+    def "getTypeName calls super if conditions not met"() {
+        given:
+        def assoc = new TestHibernateAssociation(type: List, associatedEntity: null)
+
+        expect: "It falls back to HibernatePersistentProperty.getTypeName which returns the class name for non-enums"
+        assoc.getTypeName(List, null, null) == List.name
+    }
+
+    // Additional tests for coverage of HibernatePersistentProperty methods through HibernateAssociation
+    def "isLazyAble returns true for HibernateAssociation"() {
+        given:
+        def assoc = new TestHibernateAssociation()
+
+        expect:
+        assoc.isLazyAble()
+    }
+
+    def "isUserButNotCollectionType coverage"() {
+        given:
+        def config = new PropertyConfig(type: Object)
+        def assoc = new TestHibernateAssociation(mappedForm: config)
+
+        expect:
+        assoc.isUserButNotCollectionType()
+    }
+
+    def "isEnumType coverage"() {
+        given:
+        def assoc = new TestHibernateAssociation(type: RetentionPolicy)
+
+        expect:
+        assoc.isEnumType()
+    }
+
+    // Stub implementation to test default methods of HibernateAssociation
+    static class TestHibernateAssociation implements HibernateAssociation {
+        String name
+        Class type
+        PersistentProperty<?> inverseSide
+        GrailsHibernatePersistentEntity associatedEntity
+        boolean bidirectional
+        boolean owningSide
+        boolean circular
+        boolean bidirectionalToManyMap
+        PropertyConfig mappedForm
+
+        @Override PersistentProperty<?> getInverseSide() { inverseSide }
+        @Override PersistentEntity getAssociatedEntity() { associatedEntity }
+        @Override boolean isBidirectional() { bidirectional }
+        @Override boolean isOwningSide() { owningSide }
+        @Override boolean isCircular() { circular }
+        @Override boolean isBidirectionalToManyMap() { bidirectionalToManyMap }
+        
+        @Override String getName() { name }
+        @Override String getCapitilizedName() { name?.capitalize() }
+        @Override Class getType() { type }
+        
+        @Override PropertyMapping<PropertyConfig> getMapping() {
+            return new PropertyMapping<PropertyConfig>() {
+                @Override ClassMapping getClassMapping() { null }
+                @Override PropertyConfig getMappedForm() { mappedForm }
+            }
+        }
+
+        @Override PropertyConfig getMappedForm() { mappedForm }
+        @Override PersistentEntity getOwner() { null }
+        @Override boolean isNullable() { true }
+        @Override boolean isInherited() { false }
+        @Override EntityReflector.PropertyReader getReader() { null }
+        @Override EntityReflector.PropertyWriter getWriter() { null }
+
+        @Override String getTypeName(PropertyConfig config, Mapping mapping) { "defaultType" }
+        @Override boolean supportsJoinColumnMapping() { true }
+    }
+}