This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch test/document-datamapping-core-transformers in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit ee5cdd186753e50aced942cf793428aef8978901 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Thu Aug 13 10:56:50 2026 -0500 Add test coverage for JpaGormEntityTransformation's local transform entry point JpaGormEntityTransformation sat at 14% coverage. The existing spec only exercised it indirectly via GlobalJpaEntityTransform, which applies it to classes that already carry @jakarta.persistence.Entity - so the class's own local, @grails.gorm.annotation.JpaEntity-driven entry point (visit(ASTNode[], SourceUnit)) and the branch that actually adds the missing @Entity annotation never ran. Add three specs: a class annotated @JpaEntity without @Entity (proving the local path adds the annotation and applies GORM entity enhancement), a class already carrying both annotations (proving the annotation isn't added twice), and priority() ordering. Coverage moves 14% -> 60% instruction, ~0% -> 58% branch. The two remaining uncovered lines are defensive guards (malformed astNodes array, non-matching annotation type) unreachable through any class Groovy's own local-transform dispatch would actually produce. Co-Authored-By: Claude Sonnet 5 <[email protected]> --- .../compiler/gorm/JpaEntityTransformSpec.groovy | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/grails-datamapping-core/src/test/groovy/org/grails/compiler/gorm/JpaEntityTransformSpec.groovy b/grails-datamapping-core/src/test/groovy/org/grails/compiler/gorm/JpaEntityTransformSpec.groovy index fbb195b385..227ca43806 100644 --- a/grails-datamapping-core/src/test/groovy/org/grails/compiler/gorm/JpaEntityTransformSpec.groovy +++ b/grails-datamapping-core/src/test/groovy/org/grails/compiler/gorm/JpaEntityTransformSpec.groovy @@ -26,10 +26,22 @@ import spock.lang.Specification import org.springframework.validation.annotation.Validated +import org.apache.grails.common.compiler.GroovyTransformOrder import org.grails.datastore.gorm.GormEntity import org.grails.datastore.mapping.model.config.GormProperties import org.grails.datastore.mapping.reflect.ClassPropertyFetcher +/** + * {@link org.grails.compiler.gorm.GlobalJpaEntityTransform} applies {@link JpaGormEntityTransformation} + * to every {@code @jakarta.persistence.Entity}-annotated class automatically, exercising its + * {@code visit(ClassNode, SourceUnit)} entry point - but only ever on a class that already carries the + * JPA entity annotation, so the branch that adds it never runs that way. The local, + * {@code @grails.gorm.annotation.JpaEntity}-driven entry point (the other {@code visit} overload, + * {@code visit(ASTNode[], SourceUnit)}) is a separate, otherwise-untested code path: it is what a class + * takes when it is annotated {@code @JpaEntity} directly without also being a JPA entity, and it is what + * actually adds the missing {@code @jakarta.persistence.Entity} annotation before delegating to the same + * {@code visit(ClassNode, SourceUnit)} logic. + */ class JpaEntityTransformSpec extends Specification { void 'test the JPA entity transform the entity correctly'() { @@ -72,5 +84,45 @@ class JpaEntityTransformSpec extends Specification { customerClass.getDeclaredMethod('removeFromRelated', Object) customerClass.getDeclaredMethod('removeFromRelated', Object).isAnnotationPresent(Generated) } + + void 'a class annotated with @JpaEntity but not @jakarta.persistence.Entity is made a GORM entity and gets the JPA annotation added'() { + given: 'a class using the local, annotation-driven transform entry point instead of the global one' + def productClass = new GroovyClassLoader().parseClass(''' + import grails.gorm.annotation.JpaEntity + + @JpaEntity + class Product { + Long myId + String name + } + ''') + + expect: 'the transform added the missing @jakarta.persistence.Entity annotation itself' + productClass.getAnnotation(jakarta.persistence.Entity) + + and: 'the same GORM entity enhancement as the globally-triggered path was applied' + GormEntity.isAssignableFrom(productClass) + } + + void 'a class already annotated with @jakarta.persistence.Entity is not annotated a second time'() { + given: 'a class carrying both annotations, so the local transform entry point runs but the class already has the JPA annotation' + def alreadyJpaClass = new GroovyClassLoader().parseClass(''' + import grails.gorm.annotation.JpaEntity + + @JpaEntity + @jakarta.persistence.Entity + class AlreadyJpaProduct { + Long myId + } + ''') + + expect: + alreadyJpaClass.getDeclaredAnnotations().findAll { it.annotationType() == jakarta.persistence.Entity }.size() == 1 + } + + void 'priority orders the transform relative to the dirty-check transform'() { + expect: + new JpaGormEntityTransformation().priority() == GroovyTransformOrder.JPA_GORM_ENTITY_ORDER + } }
