This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 42612d37550bc748580b06cd073380329408f559
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Thu Aug 13 14:36:47 2026 -0500

    Close test coverage gaps in the Hibernate5/7 and MongoDB datastore 
initializers
    
    Adds tests for branches of the three production AbstractDatastoreInitializer
    subclasses that were previously unexercised: the OSIV interceptor
    registration branch and configureForDataSource(DataSource) for both
    Hibernate5 and Hibernate7, Hibernate7's IllegalStateException guard when
    hibernateDatastore fails to register, and the Map/Collection<Class>
    constructor form for both.
    
    For MongoDB, covers the mongo != null branch (reusing a pre-existing
    MongoClient) in both configure() and getBeanDefinitions(), the
    package-scanning constructor, and adds a new Docker-free
    MongoDbDataStoreSpringInitializerUnitSpec covering the isMappedClass/
    collectMappedClasses mixed-entity filtering and the deprecated setters
    that the Docker-backed spec never reaches.
    
    grails-data-neo4j was excluded: it is not part of the root build, its
    README states it hasn't been updated for the current release, and its
    standalone build is broken, so no tests could be written or verified
    there.
    
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
---
 .../HibernateDatastoreSpringInitializerSpec.groovy |  65 +++++++++++
 .../HibernateDatastoreSpringInitializerSpec.groovy |  80 ++++++++++++++
 .../MongoDbDataStoreSpringInitializerSpec.groovy   |  46 ++++++++
 ...ongoDbDataStoreSpringInitializerUnitSpec.groovy | 120 +++++++++++++++++++++
 4 files changed, 311 insertions(+)

diff --git 
a/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
 
b/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
index 88fb4c628d..f2e542589b 100644
--- 
a/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
+++ 
b/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
@@ -19,17 +19,28 @@
 package grails.orm.bootstrap
 
 import grails.gorm.annotation.Entity
+import org.grails.orm.hibernate.HibernateDatastore
 import org.hibernate.Session
 import org.hibernate.SessionFactory
 import org.hibernate.dialect.H2Dialect
+import org.springframework.beans.factory.support.RootBeanDefinition
+import org.springframework.context.ConfigurableApplicationContext
+import org.springframework.context.support.GenericApplicationContext
+import org.springframework.jdbc.datasource.DriverManagerDataSource
 import org.springframework.transaction.PlatformTransactionManager
+import spock.lang.AutoCleanup
 import spock.lang.Specification
 
+import javax.sql.DataSource
+
 /**
  * Created by graemerocher on 29/01/14.
  */
 class HibernateDatastoreSpringInitializerSpec extends Specification{
 
+    @AutoCleanup
+    ConfigurableApplicationContext applicationContext
+
     void "Test configure multiple data sources"() {
         given:"An initializer instance"
         Map config = [
@@ -90,6 +101,60 @@ class HibernateDatastoreSpringInitializerSpec extends 
Specification{
         }
 
     }
+
+    void "Test the Map/Collection<Class> constructor bootstraps GORM"() {
+        given: "An initializer built from a Collection of persistent classes"
+        def datastoreInitializer = new HibernateDatastoreSpringInitializer(
+                ['dataSource.url': 
'jdbc:h2:mem:collectionCtor;LOCK_TIMEOUT=10000', 'hibernate.hbm2ddl.auto': 
'create'],
+                [Person] as Collection<Class>)
+
+        when: "the application is configured"
+        applicationContext = (ConfigurableApplicationContext) 
datastoreInitializer.configure()
+
+        then: "GORM is bootstrapped with the given entity"
+        
applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name)
 != null
+        Person.withNewSession { Person.count() == 0 }
+    }
+
+    void "Test configureForDataSource bootstraps GORM around a pre-existing 
DataSource"() {
+        given: "a DataSource created ahead of time"
+        def dataSource = new 
DriverManagerDataSource('jdbc:h2:mem:configureForDataSource;LOCK_TIMEOUT=10000',
 'sa', '')
+        dataSource.driverClassName = 'org.h2.Driver'
+        def datastoreInitializer = new 
HibernateDatastoreSpringInitializer(['hibernate.hbm2ddl.auto': 'create'], 
Person)
+
+        when: "the initializer is configured around that DataSource"
+        applicationContext = (ConfigurableApplicationContext) 
datastoreInitializer.configureForDataSource(dataSource)
+
+        then: "the pre-existing DataSource is registered and reused rather 
than a new one being built"
+        
applicationContext.getBean(HibernateDatastoreSpringInitializer.DEFAULT_DATA_SOURCE_NAME,
 DataSource).is(dataSource)
+        Person.withNewSession { Person.count() == 0 }
+    }
+
+    void "Test the OSIV interceptor is registered when the registry is a web 
application"() {
+        given: "a registry that signals it belongs to a web application"
+        def datastoreInitializer = new 
HibernateDatastoreSpringInitializer(['dataSource.url': 
'jdbc:h2:mem:osivEnabled;LOCK_TIMEOUT=10000'], Person)
+        def registry = new GenericApplicationContext()
+        registry.registerBeanDefinition('grailsControllerHelper', new 
RootBeanDefinition(Object))
+
+        when:
+        datastoreInitializer.configureForBeanDefinitionRegistry(registry)
+        registry.refresh()
+        applicationContext = registry
+
+        then:
+        registry.containsBean('openSessionInViewInterceptor')
+    }
+
+    void "Test the OSIV interceptor is not registered for a non-web 
application registry"() {
+        given: "An initializer instance configured against a plain, non-web 
registry"
+        def datastoreInitializer = new 
HibernateDatastoreSpringInitializer(['dataSource.url': 
'jdbc:h2:mem:osivDisabled;LOCK_TIMEOUT=10000'], Person)
+
+        when:
+        applicationContext = (ConfigurableApplicationContext) 
datastoreInitializer.configure()
+
+        then:
+        !applicationContext.containsBean('openSessionInViewInterceptor')
+    }
 }
 @Entity
 class Person {
diff --git 
a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
 
b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
index 9549d17d68..afa580120f 100644
--- 
a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
+++ 
b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
@@ -23,11 +23,17 @@ import org.grails.orm.hibernate.HibernateDatastore
 import org.hibernate.Session
 import org.hibernate.SessionFactory
 import org.hibernate.dialect.H2Dialect
+import org.springframework.beans.factory.support.BeanDefinitionRegistry
+import org.springframework.beans.factory.support.RootBeanDefinition
 import org.springframework.context.ConfigurableApplicationContext
+import org.springframework.context.support.GenericApplicationContext
+import org.springframework.jdbc.datasource.DriverManagerDataSource
 import org.springframework.transaction.PlatformTransactionManager
 import spock.lang.AutoCleanup
 import spock.lang.Specification
 
+import javax.sql.DataSource
+
 /**
  * Created by graemerocher on 29/01/14.
  */
@@ -93,6 +99,80 @@ class HibernateDatastoreSpringInitializerSpec extends 
Specification{
             return true
         }
     }
+
+    void "Test the Map/Collection<Class> constructor bootstraps GORM"() {
+        given: "An initializer built from a Collection of persistent classes"
+        def datastoreInitializer = new HibernateDatastoreSpringInitializer(
+                ['dataSource.url': 
'jdbc:h2:mem:collectionCtor;LOCK_TIMEOUT=10000', 'hibernate.hbm2ddl.auto': 
'create'],
+                [Person] as Collection<Class>)
+
+        when: "the application is configured"
+        applicationContext = (ConfigurableApplicationContext) 
datastoreInitializer.configure()
+
+        then: "GORM is bootstrapped with the given entity"
+        
applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name)
 != null
+        Person.withNewSession { Person.count() == 0 }
+    }
+
+    void "Test configureForDataSource bootstraps GORM around a pre-existing 
DataSource"() {
+        given: "a DataSource created ahead of time"
+        def dataSource = new 
DriverManagerDataSource('jdbc:h2:mem:configureForDataSource;LOCK_TIMEOUT=10000',
 'sa', '')
+        dataSource.driverClassName = 'org.h2.Driver'
+        def datastoreInitializer = new 
HibernateDatastoreSpringInitializer(['hibernate.hbm2ddl.auto': 'create'], 
Person)
+
+        when: "the initializer is configured around that DataSource"
+        applicationContext = (ConfigurableApplicationContext) 
datastoreInitializer.configureForDataSource(dataSource)
+
+        then: "the pre-existing DataSource is registered and reused rather 
than a new one being built"
+        
applicationContext.getBean(HibernateDatastoreSpringInitializer.DEFAULT_DATA_SOURCE_NAME,
 DataSource).is(dataSource)
+        Person.withNewSession { Person.count() == 0 }
+    }
+
+    void "Test configureForBeanDefinitionRegistry throws when the 
hibernateDatastore bean was not registered"() {
+        given: "an initializer whose bean definitions never register 
hibernateDatastore"
+        def datastoreInitializer = new 
HibernateDatastoreSpringInitializer([:], Person) {
+            @Override
+            Closure getBeanDefinitions(BeanDefinitionRegistry 
beanDefinitionRegistry) {
+                { -> }
+            }
+        }
+        def registry = new GenericApplicationContext()
+
+        when:
+        datastoreInitializer.configureForBeanDefinitionRegistry(registry)
+
+        then:
+        thrown(IllegalStateException)
+
+        cleanup:
+        registry.close()
+    }
+
+    void "Test the OSIV interceptor is registered when the registry is a web 
application"() {
+        given: "a registry that signals it belongs to a web application"
+        def datastoreInitializer = new 
HibernateDatastoreSpringInitializer(['dataSource.url': 
'jdbc:h2:mem:osivEnabled;LOCK_TIMEOUT=10000'], Person)
+        def registry = new GenericApplicationContext()
+        registry.registerBeanDefinition('grailsControllerHelper', new 
RootBeanDefinition(Object))
+
+        when:
+        datastoreInitializer.configureForBeanDefinitionRegistry(registry)
+        registry.refresh()
+        applicationContext = registry
+
+        then:
+        registry.containsBean('openSessionInViewInterceptor')
+    }
+
+    void "Test the OSIV interceptor is not registered for a non-web 
application registry"() {
+        given: "An initializer instance configured against a plain, non-web 
registry"
+        def datastoreInitializer = new 
HibernateDatastoreSpringInitializer(['dataSource.url': 
'jdbc:h2:mem:osivDisabled;LOCK_TIMEOUT=10000'], Person)
+
+        when:
+        applicationContext = (ConfigurableApplicationContext) 
datastoreInitializer.configure()
+
+        then:
+        !applicationContext.containsBean('openSessionInViewInterceptor')
+    }
 }
 @Entity
 class Person {
diff --git 
a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
 
b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
index e2666d9567..a764e9688a 100644
--- 
a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
+++ 
b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
@@ -19,6 +19,7 @@
 package grails.mongodb.bootstrap
 
 import com.mongodb.client.MongoClient
+import com.mongodb.client.MongoClients
 import grails.mongodb.MongoEntity
 import grails.mongodb.geo.Point
 import grails.persistence.Entity
@@ -26,6 +27,7 @@ import org.apache.grails.testing.mongo.AutoStartedMongoSpec
 import org.bson.Document
 import org.grails.datastore.gorm.mongo.Birthday
 import org.grails.datastore.gorm.mongo.BirthdayCodec
+import org.grails.datastore.mapping.core.DatastoreUtils
 import 
org.grails.datastore.mapping.engine.types.AbstractMappingAwareCustomTypeMarshaller
 import org.grails.datastore.mapping.model.MappingContext
 import org.grails.datastore.mapping.model.PersistentProperty
@@ -108,6 +110,50 @@ class MongoDbDataStoreSpringInitializerSpec extends 
AutoStartedMongoSpec {
         mongoDatastore.destroy()
     }
 
+    void "Test configure reuses a pre-existing MongoClient instead of creating 
a new one"() {
+        given: "a MongoClient created ahead of time"
+        def mongoClient = 
MongoClients.create("mongodb://${mongoHost}:${mongoPort}".toString())
+        def initializer = makeInitializer([
+                (MongoSettings.SETTING_DATABASE_NAME): 'foo',
+        ], Person)
+        initializer.setMongoClient(mongoClient)
+
+        when: "the initializer is configured"
+        def applicationContext = initializer.configure()
+
+        then: "the pre-existing client is registered and reused rather than a 
new one being built"
+        applicationContext.getBean('mongo', MongoClient).is(mongoClient)
+        
applicationContext.getBean(MongoDatastore).getMongoClient().is(mongoClient)
+
+        cleanup:
+        applicationContext.getBean(MongoDatastore).destroy()
+        mongoClient.close()
+    }
+
+    void "Test the package-scanning constructor discovers entities via 
classpath scan"() {
+        given: "an initializer configured with a package name rather than 
explicit classes"
+        def resolver = DatastoreUtils.createPropertyResolver([
+                (MongoSettings.SETTING_HOST): mongoHost,
+                (MongoSettings.SETTING_PORT): mongoPort,
+        ])
+        def initializer = new MongoDbDataStoreSpringInitializer(resolver, 
Person.package.name) {
+            @Override
+            protected Map<String, Class<?>> loadDataServices(String 
secondaryDatastore = null) {
+                [:]
+            }
+        }
+
+        when: "the application is configured"
+        def applicationContext = initializer.configure()
+        def mongoDatastore = applicationContext.getBean(MongoDatastore)
+
+        then: "the Person entity declared in the scanned package was 
discovered and mapped"
+        mongoDatastore.mappingContext.getPersistentEntity(Person.name) != null
+
+        cleanup:
+        mongoDatastore.destroy()
+    }
+
     @Issue('GPMONGODB-339')
     @Ignore
     // The MongoDB API for this test has been altered / removed with no 
apparent replacement for getting the number of pooled connections in use
diff --git 
a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerUnitSpec.groovy
 
b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerUnitSpec.groovy
new file mode 100644
index 0000000000..c708ac5c4e
--- /dev/null
+++ 
b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerUnitSpec.groovy
@@ -0,0 +1,120 @@
+/*
+ *  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 grails.mongodb.bootstrap
+
+import com.mongodb.MongoClientSettings
+import com.mongodb.client.MongoClient
+import grails.mongodb.MongoEntity
+import spock.lang.Specification
+
+/**
+ * Pure unit coverage for {@link MongoDbDataStoreSpringInitializer} that does 
not require a
+ * running MongoDB instance, covering the {@code isMappedClass} override and 
the deprecated
+ * bean-style setters that {@link MongoDbDataStoreSpringInitializerSpec} does 
not reach.
+ */
+class MongoDbDataStoreSpringInitializerUnitSpec extends Specification {
+
+    void 'isMappedClass and collectMappedClasses discriminate MongoEntity 
classes from unrelated ones for a secondary datastore'() {
+        given:
+        def initializer = new MongoDbDataStoreSpringInitializer([MappedThing, 
UnmappedThing])
+        initializer.setSecondaryDatastore(true)
+
+        expect:
+        initializer.isMappedClass('mongo', MappedThing)
+        !initializer.isMappedClass('mongo', UnmappedThing)
+        initializer.collectMappedClasses('mongo') == [MappedThing]
+    }
+
+    void 'setMongoBeanName updates the mongo bean name'() {
+        given:
+        def initializer = new MongoDbDataStoreSpringInitializer()
+
+        when:
+        initializer.setMongoBeanName('customMongo')
+
+        then:
+        initializer.mongoBeanName == 'customMongo'
+    }
+
+    void 'setMongoOptionsBeanName updates the mongo options bean name'() {
+        given:
+        def initializer = new MongoDbDataStoreSpringInitializer()
+
+        when:
+        initializer.setMongoOptionsBeanName('customMongoOptions')
+
+        then:
+        initializer.mongoOptionsBeanName == 'customMongoOptions'
+    }
+
+    void 'setDatabaseName updates the database name'() {
+        given:
+        def initializer = new MongoDbDataStoreSpringInitializer()
+
+        when:
+        initializer.setDatabaseName('customDb')
+
+        then:
+        initializer.databaseName == 'customDb'
+    }
+
+    void 'setDefaultMapping updates the default mapping closure'() {
+        given:
+        def initializer = new MongoDbDataStoreSpringInitializer()
+        def mapping = { -> }
+
+        when:
+        initializer.setDefaultMapping(mapping)
+
+        then:
+        initializer.defaultMapping.is(mapping)
+    }
+
+    void 'setMongoOptions updates the mongo client settings'() {
+        given:
+        def initializer = new MongoDbDataStoreSpringInitializer()
+        def settings = MongoClientSettings.builder().build()
+
+        when:
+        initializer.setMongoOptions(settings)
+
+        then:
+        initializer.mongoOptions.is(settings)
+    }
+
+    void 'setMongoClient records the pre-existing client to reuse'() {
+        given:
+        def initializer = new MongoDbDataStoreSpringInitializer()
+        def client = Mock(MongoClient)
+
+        when:
+        initializer.setMongoClient(client)
+
+        then:
+        initializer.mongo.is(client)
+    }
+}
+
+class MappedThing implements MongoEntity<MappedThing> {
+    Long id
+}
+
+class UnmappedThing {
+    static mapWith = 'sql'
+}

Reply via email to