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

jamesfredley pushed a commit to branch test/expand-integration-test-coverage
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 3c062589ddc9b1eb7729dff28c5e534c7fb26d3c
Author: James Fredley <[email protected]>
AuthorDate: Sun Jan 25 22:07:57 2026 -0500

    Add GORM advanced query and relationship tests to app1
    
    - Add GormAdvancedSpec with 53 tests for GORM queries
    - Tests criteria queries, HQL, dynamic finders
    - Tests projections, aggregations, and pagination
    - Tests domain relationships (one-to-many, many-to-many)
    - Includes cascade operations and orphan removal testing
---
 .../domain/functionaltests/gorm/Author.groovy      |  62 ++
 .../domain/functionaltests/gorm/GormBook.groovy    |  70 ++
 .../functionaltests/gorm/GormAdvancedSpec.groovy   | 716 +++++++++++++++++++++
 3 files changed, 848 insertions(+)

diff --git 
a/grails-test-examples/app1/grails-app/domain/functionaltests/gorm/Author.groovy
 
b/grails-test-examples/app1/grails-app/domain/functionaltests/gorm/Author.groovy
new file mode 100644
index 0000000000..0370b5be13
--- /dev/null
+++ 
b/grails-test-examples/app1/grails-app/domain/functionaltests/gorm/Author.groovy
@@ -0,0 +1,62 @@
+/*
+ *  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 functionaltests.gorm
+
+/**
+ * Domain class for advanced GORM query tests.
+ */
+class Author {
+
+    String name
+    String country
+    Integer birthYear
+    boolean active = true
+    
+    Date dateCreated
+    Date lastUpdated
+
+    static hasMany = [books: GormBook]
+
+    static constraints = {
+        name blank: false, size: 1..100
+        country nullable: true
+        birthYear nullable: true, min: 1800, max: 2100
+    }
+
+    static mapping = {
+        books cascade: 'all-delete-orphan'
+    }
+
+    static namedQueries = {
+        activeAuthors {
+            eq 'active', true
+        }
+        fromCountry { String countryName ->
+            eq 'country', countryName
+        }
+        bornAfter { Integer year ->
+            gt 'birthYear', year
+        }
+        prolificAuthors {
+            // Authors with more than 2 books
+            sizeGt 'books', 2
+        }
+    }
+}
diff --git 
a/grails-test-examples/app1/grails-app/domain/functionaltests/gorm/GormBook.groovy
 
b/grails-test-examples/app1/grails-app/domain/functionaltests/gorm/GormBook.groovy
new file mode 100644
index 0000000000..48fc078725
--- /dev/null
+++ 
b/grails-test-examples/app1/grails-app/domain/functionaltests/gorm/GormBook.groovy
@@ -0,0 +1,70 @@
+/*
+ *  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 functionaltests.gorm
+
+/**
+ * Domain class for advanced GORM query tests - Book entity.
+ */
+class GormBook {
+
+    String title
+    String genre
+    BigDecimal price
+    Integer pageCount
+    Integer publicationYear
+    boolean inPrint = true
+    Double rating
+    
+    Date dateCreated
+    Date lastUpdated
+
+    static belongsTo = [author: Author]
+
+    static constraints = {
+        title blank: false, size: 1..200
+        genre inList: ['Fiction', 'Non-Fiction', 'Science', 'History', 
'Biography', 'Fantasy', 'Mystery', 'Romance']
+        price min: 0.0
+        pageCount nullable: true, min: 1
+        publicationYear nullable: true, min: 1450, max: 2100
+        rating nullable: true, min: 0.0d, max: 5.0d
+    }
+
+    static mapping = {
+        table 'gorm_books'
+    }
+
+    static namedQueries = {
+        inGenre { String genreName ->
+            eq 'genre', genreName
+        }
+        publishedAfter { Integer year ->
+            gt 'publicationYear', year
+        }
+        pricedBetween { BigDecimal min, BigDecimal max ->
+            between 'price', min, max
+        }
+        highlyRated {
+            ge 'rating', 4.0d
+        }
+        availableInPrint {
+            eq 'inPrint', true
+        }
+    }
+}
diff --git 
a/grails-test-examples/app1/src/integration-test/groovy/functionaltests/gorm/GormAdvancedSpec.groovy
 
b/grails-test-examples/app1/src/integration-test/groovy/functionaltests/gorm/GormAdvancedSpec.groovy
new file mode 100644
index 0000000000..2a70fd3930
--- /dev/null
+++ 
b/grails-test-examples/app1/src/integration-test/groovy/functionaltests/gorm/GormAdvancedSpec.groovy
@@ -0,0 +1,716 @@
+/*
+ *  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 functionaltests.gorm
+
+import functionaltests.Application
+import grails.testing.mixin.integration.Integration
+import grails.gorm.transactions.Rollback
+import spock.lang.Specification
+import spock.lang.Shared
+
+/**
+ * Integration tests for advanced GORM features:
+ * - Criteria queries
+ * - Where queries
+ * - Projections and aggregations
+ * - HQL queries
+ * - Named queries
+ * - Detached criteria
+ */
+@Integration(applicationClass = Application)
+@Rollback
+class GormAdvancedSpec extends Specification {
+
+    def setup() {
+        // Create test data
+        createTestData()
+    }
+
+    private void createTestData() {
+        // Author 1: American author with 3 books
+        def author1 = new Author(name: 'John Smith', country: 'USA', 
birthYear: 1970, active: true)
+        author1.addToBooks(new GormBook(title: 'The Great Adventure', genre: 
'Fiction', price: 19.99, pageCount: 350, publicationYear: 2020, rating: 4.5))
+        author1.addToBooks(new GormBook(title: 'Mystery Manor', genre: 
'Mystery', price: 14.99, pageCount: 280, publicationYear: 2021, rating: 4.2))
+        author1.addToBooks(new GormBook(title: 'Science Today', genre: 
'Science', price: 29.99, pageCount: 450, publicationYear: 2022, rating: 4.8))
+        author1.save(failOnError: true)
+
+        // Author 2: British author with 2 books
+        def author2 = new Author(name: 'Jane Doe', country: 'UK', birthYear: 
1985, active: true)
+        author2.addToBooks(new GormBook(title: 'British History', genre: 
'History', price: 24.99, pageCount: 500, publicationYear: 2019, rating: 4.0))
+        author2.addToBooks(new GormBook(title: 'Royal Biography', genre: 
'Biography', price: 21.99, pageCount: 320, publicationYear: 2020, rating: 3.8))
+        author2.save(failOnError: true)
+
+        // Author 3: French author with 1 book, inactive
+        def author3 = new Author(name: 'Pierre Martin', country: 'France', 
birthYear: 1960, active: false)
+        author3.addToBooks(new GormBook(title: 'French Cuisine', genre: 
'Non-Fiction', price: 34.99, pageCount: 400, publicationYear: 2018, inPrint: 
false, rating: 4.6))
+        author3.save(failOnError: true)
+
+        // Author 4: American author with 4 books (prolific)
+        def author4 = new Author(name: 'Sarah Johnson', country: 'USA', 
birthYear: 1975, active: true)
+        author4.addToBooks(new GormBook(title: 'Fantasy World', genre: 
'Fantasy', price: 16.99, pageCount: 600, publicationYear: 2021, rating: 4.9))
+        author4.addToBooks(new GormBook(title: 'Dragon Tales', genre: 
'Fantasy', price: 17.99, pageCount: 550, publicationYear: 2022, rating: 4.7))
+        author4.addToBooks(new GormBook(title: 'Magic Realm', genre: 
'Fantasy', price: 18.99, pageCount: 580, publicationYear: 2023, rating: 4.8))
+        author4.addToBooks(new GormBook(title: 'Love Story', genre: 'Romance', 
price: 12.99, pageCount: 250, publicationYear: 2020, rating: 3.5))
+        author4.save(failOnError: true, flush: true)
+    }
+
+    // ========== Criteria Query Tests ==========
+
+    def "criteria query - basic equals"() {
+        when: "querying with equals criterion"
+        def results = Author.createCriteria().list {
+            eq('country', 'USA')
+        }
+
+        then: "matching authors are returned"
+        results.size() == 2
+        results*.name.containsAll(['John Smith', 'Sarah Johnson'])
+    }
+
+    def "criteria query - like operator"() {
+        when: "querying with like criterion"
+        def results = GormBook.createCriteria().list {
+            like('title', '%History%')
+        }
+
+        then: "matching books are returned"
+        results.size() >= 1  // At least our British History book
+        results.every { it.title.contains('History') }
+        results.find { it.title == 'British History' } != null
+    }
+
+    def "criteria query - ilike (case insensitive)"() {
+        when: "querying with case-insensitive like"
+        def results = GormBook.createCriteria().list {
+            ilike('title', '%FANTASY%')
+        }
+
+        then: "matching books regardless of case"
+        results.size() >= 1  // At least our Fantasy World book
+        results.every { it.title.toLowerCase().contains('fantasy') }
+        results.find { it.title == 'Fantasy World' } != null
+    }
+
+    def "criteria query - between"() {
+        when: "querying with between criterion"
+        def results = GormBook.createCriteria().list {
+            between('price', 15.00, 25.00)
+        }
+
+        then: "books in price range are returned"
+        results.size() >= 4
+        results.every { it.price >= 15.00 && it.price <= 25.00 }
+    }
+
+    def "criteria query - greater than and less than"() {
+        when: "querying with gt and lt"
+        def results = GormBook.createCriteria().list {
+            gt('pageCount', 400)
+            lt('price', 30.00)
+        }
+
+        then: "matching books are returned"
+        results.every { it.pageCount > 400 && it.price < 30.00 }
+    }
+
+    def "criteria query - in list"() {
+        when: "querying with inList"
+        def results = GormBook.createCriteria().list {
+            inList('genre', ['Fantasy', 'Mystery'])
+        }
+
+        then: "books in specified genres are returned"
+        results.size() >= 4
+        results.every { it.genre in ['Fantasy', 'Mystery'] }
+    }
+
+    def "criteria query - is null and is not null"() {
+        when: "querying for non-null ratings"
+        def results = GormBook.createCriteria().list {
+            isNotNull('rating')
+        }
+
+        then: "all books have ratings"
+        results.every { it.rating != null }
+    }
+
+    def "criteria query - and/or logic"() {
+        when: "querying with or logic"
+        def results = GormBook.createCriteria().list {
+            or {
+                eq('genre', 'Fantasy')
+                ge('price', 30.00)
+            }
+        }
+
+        then: "books matching either condition are returned"
+        results.every { it.genre == 'Fantasy' || it.price >= 30.00 }
+    }
+
+    def "criteria query - nested and/or"() {
+        when: "querying with nested logic"
+        def results = GormBook.createCriteria().list {
+            and {
+                eq('inPrint', true)
+                or {
+                    eq('genre', 'Fiction')
+                    eq('genre', 'Science')
+                }
+            }
+        }
+
+        then: "books matching complex condition"
+        results.every { it.inPrint && (it.genre == 'Fiction' || it.genre == 
'Science') }
+    }
+
+    def "criteria query - association query"() {
+        when: "querying through association"
+        def results = GormBook.createCriteria().list {
+            author {
+                eq('country', 'USA')
+            }
+        }
+
+        then: "books by USA authors are returned"
+        results.size() >= 7
+    }
+
+    def "criteria query - order by"() {
+        when: "querying with ordering"
+        def results = GormBook.createCriteria().list {
+            order('price', 'desc')
+        }
+
+        then: "results are ordered by price descending"
+        for (int i = 0; i < results.size() - 1; i++) {
+            results[i].price >= results[i + 1].price
+        }
+    }
+
+    def "criteria query - multiple order by"() {
+        when: "querying with multiple orderings"
+        def results = GormBook.createCriteria().list {
+            order('genre', 'asc')
+            order('price', 'desc')
+        }
+
+        then: "results are ordered"
+        results.size() > 0
+    }
+
+    def "criteria query - pagination"() {
+        when: "querying with pagination"
+        def results = GormBook.createCriteria().list(max: 3, offset: 0) {
+            order('title', 'asc')
+        }
+
+        then: "paginated results are returned"
+        results.size() <= 3
+    }
+
+    def "criteria query - count"() {
+        when: "counting with criteria"
+        def count = Author.createCriteria().count {
+            eq('active', true)
+        }
+
+        then: "count is returned"
+        count >= 3  // At least our 3 active authors
+    }
+
+    def "criteria query - get single result"() {
+        when: "getting single result with unique constraint"
+        def result = Author.createCriteria().list {
+            eq('name', 'John Smith')
+            maxResults(1)
+        }
+
+        then: "author is returned"
+        result.size() >= 1
+        result[0].name == 'John Smith'
+    }
+
+    // ========== Where Query Tests ==========
+
+    def "where query - simple condition"() {
+        when: "using where query"
+        def results = Author.where {
+            country == 'UK'
+        }.list()
+
+        then: "matching authors are returned"
+        results.size() >= 1  // At least our UK test author
+        results.every { it.country == 'UK' }
+        results.find { it.name == 'Jane Doe' } != null
+    }
+
+    def "where query - multiple conditions"() {
+        when: "using where with multiple conditions"
+        def results = GormBook.where {
+            genre == 'Fantasy' && price < 18.00
+        }.list()
+
+        then: "matching books are returned"
+        results.every { it.genre == 'Fantasy' && it.price < 18.00 }
+    }
+
+    def "where query - or conditions"() {
+        when: "using where with or"
+        def results = GormBook.where {
+            genre == 'History' || genre == 'Biography'
+        }.list()
+
+        then: "books in either genre"
+        results.size() >= 2  // At least our 2 test books
+        results*.genre.every { it in ['History', 'Biography'] }
+    }
+
+    def "where query - comparison operators"() {
+        when: "using comparison operators"
+        def results = GormBook.where {
+            rating >= 4.5 && pageCount > 300
+        }.list()
+
+        then: "highly rated long books"
+        results.every { it.rating >= 4.5 && it.pageCount > 300 }
+    }
+
+    def "where query - like pattern"() {
+        when: "using like in where query"
+        def results = GormBook.where {
+            title =~ '%Tales%'
+        }.list()
+
+        then: "matching books are returned"
+        results.size() >= 1  // At least our Dragon Tales book
+        results.every { it.title.contains('Tales') }
+        results.find { it.title == 'Dragon Tales' } != null
+    }
+
+    def "where query - in list"() {
+        when: "using in operator"
+        def genres = ['Fantasy', 'Romance']
+        def results = GormBook.where {
+            genre in genres
+        }.list()
+
+        then: "books in specified genres"
+        results.every { it.genre in genres }
+    }
+
+    def "where query - association traversal"() {
+        when: "traversing association in where"
+        def results = GormBook.where {
+            author.country == 'USA'
+        }.list()
+
+        then: "books by USA authors"
+        results.size() >= 7
+    }
+
+    def "where query - detached for reuse"() {
+        given: "a detached where query"
+        def fantasyBooks = GormBook.where {
+            genre == 'Fantasy'
+        }
+
+        when: "reusing the query with additional conditions"
+        def highlyRatedFantasy = fantasyBooks.where {
+            rating >= 4.7
+        }.list()
+
+        then: "combined conditions apply"
+        highlyRatedFantasy.every { it.genre == 'Fantasy' && it.rating >= 4.7 }
+    }
+
+    def "where query - count"() {
+        when: "counting with where query"
+        def count = GormBook.where {
+            inPrint == true
+        }.count()
+
+        then: "count is correct"
+        count >= 9  // 9 books in print
+    }
+
+    // ========== Projection Tests ==========
+
+    def "projection - single property"() {
+        when: "projecting single property"
+        def titles = GormBook.createCriteria().list {
+            projections {
+                property('title')
+            }
+        }
+
+        then: "list of titles is returned"
+        titles.every { it instanceof String }
+    }
+
+    def "projection - multiple properties"() {
+        when: "projecting multiple properties"
+        def results = GormBook.createCriteria().list {
+            projections {
+                property('title')
+                property('price')
+            }
+        }
+
+        then: "list of arrays is returned"
+        results.every { it.size() == 2 }
+    }
+
+    def "projection - distinct"() {
+        when: "getting distinct values"
+        def genres = GormBook.createCriteria().list {
+            projections {
+                distinct('genre')
+            }
+        }
+
+        then: "distinct genres are returned"
+        genres.unique().size() == genres.size()
+    }
+
+    def "projection - count"() {
+        when: "counting with projection"
+        def count = GormBook.createCriteria().get {
+            projections {
+                count('id')
+            }
+        }
+
+        then: "count is returned"
+        count >= 10
+    }
+
+    def "projection - sum"() {
+        when: "summing with projection"
+        def totalPages = GormBook.createCriteria().get {
+            projections {
+                sum('pageCount')
+            }
+        }
+
+        then: "sum is returned"
+        totalPages > 0
+    }
+
+    def "projection - average"() {
+        when: "averaging with projection"
+        def avgPrice = GormBook.createCriteria().get {
+            projections {
+                avg('price')
+            }
+        }
+
+        then: "average is returned"
+        avgPrice > 0
+    }
+
+    def "projection - min and max"() {
+        when: "getting min and max"
+        def minMax = GormBook.createCriteria().get {
+            projections {
+                min('price')
+                max('price')
+            }
+        }
+
+        then: "min and max are returned"
+        minMax[0] < minMax[1]
+    }
+
+    def "projection - group by"() {
+        when: "grouping by genre with count"
+        def results = GormBook.createCriteria().list {
+            projections {
+                groupProperty('genre')
+                count('id')
+            }
+            order('genre', 'asc')
+        }
+
+        then: "grouped counts are returned"
+        results.every { it.size() == 2 }
+        def fantasyCount = results.find { it[0] == 'Fantasy' }
+        fantasyCount != null
+        fantasyCount[1] >= 3
+    }
+
+    def "projection - group by with aggregations"() {
+        when: "grouping with multiple aggregations"
+        def results = GormBook.createCriteria().list {
+            projections {
+                groupProperty('genre')
+                count('id')
+                avg('price')
+                sum('pageCount')
+            }
+        }
+
+        then: "aggregated results per genre"
+        results.every { it.size() == 4 }
+    }
+
+    // ========== HQL Query Tests ==========
+
+    def "HQL - simple select"() {
+        when: "executing simple HQL"
+        def results = GormBook.executeQuery("from GormBook where genre = 
:genre", [genre: 'Fantasy'])
+
+        then: "matching books are returned"
+        results.every { it.genre == 'Fantasy' }
+    }
+
+    def "HQL - select with projection"() {
+        when: "HQL with projection"
+        def titles = GormBook.executeQuery("select b.title from GormBook b 
where b.price > :price", [price: 20.00])
+
+        then: "titles are returned"
+        titles.every { it instanceof String }
+    }
+
+    def "HQL - join query"() {
+        when: "HQL with join"
+        def results = GormBook.executeQuery(
+            "select b from GormBook b join b.author a where a.country = 
:country",
+            [country: 'UK']
+        )
+
+        then: "books by UK authors"
+        results.size() >= 2  // At least our 2 UK books
+        results.every { it.author.country == 'UK' }
+    }
+
+    def "HQL - aggregate functions"() {
+        when: "HQL with aggregates"
+        def result = GormBook.executeQuery(
+            "select count(b), avg(b.price), sum(b.pageCount) from GormBook b"
+        )
+
+        then: "aggregates are returned"
+        result[0][0] >= 10  // count
+        result[0][1] > 0     // avg price
+        result[0][2] > 0     // sum pages
+    }
+
+    def "HQL - group by"() {
+        when: "HQL with group by"
+        def results = GormBook.executeQuery(
+            "select b.genre, count(b), avg(b.rating) from GormBook b group by 
b.genre order by b.genre"
+        )
+
+        then: "grouped results"
+        results.size() > 0
+    }
+
+    def "HQL - named parameters"() {
+        when: "HQL with named parameters"
+        def results = GormBook.executeQuery(
+            "from GormBook b where b.price between :minPrice and :maxPrice and 
b.rating >= :minRating",
+            [minPrice: 15.00, maxPrice: 25.00, minRating: 4.0d]  // Use 'd' 
suffix for Double type
+        )
+
+        then: "matching books"
+        results.every { it.price >= 15.00 && it.price <= 25.00 && it.rating >= 
4.0 }
+    }
+
+    def "HQL - pagination"() {
+        when: "HQL with pagination"
+        def results = GormBook.executeQuery(
+            "from GormBook b order by b.title",
+            [max: 5, offset: 0]
+        )
+
+        then: "paginated results"
+        results.size() <= 5
+    }
+
+    def "HQL - update query"() {
+        given: "a book to update"
+        def book = GormBook.findByTitle('The Great Adventure')
+        def originalPrice = book.price
+
+        when: "executing update HQL"
+        def updated = GormBook.executeUpdate(
+            "update GormBook b set b.price = b.price * 1.1 where b.genre = 
:genre",
+            [genre: 'Fiction']
+        )
+
+        then: "books are updated"
+        updated >= 1
+
+        cleanup:
+        GormBook.executeUpdate("update GormBook b set b.price = :price where 
b.title = :title",
+            [price: originalPrice, title: 'The Great Adventure'])
+    }
+
+    def "HQL - subquery"() {
+        when: "HQL with subquery"
+        def results = GormBook.executeQuery(
+            "from GormBook b where b.price > (select avg(b2.price) from 
GormBook b2)"
+        )
+
+        then: "books above average price"
+        results.size() > 0
+    }
+
+    // ========== Named Query Tests ==========
+
+    def "named query - simple"() {
+        when: "using named query"
+        def results = Author.activeAuthors.list()
+
+        then: "active authors are returned"
+        results.size() >= 3  // At least our 3 test authors
+        results.every { it.active }
+    }
+
+    def "named query - with parameter"() {
+        when: "using named query with parameter"
+        def results = Author.fromCountry('USA').list()
+
+        then: "USA authors are returned"
+        results.size() >= 2  // At least our 2 USA test authors
+        results.every { it.country == 'USA' }
+    }
+
+    def "named query - chained"() {
+        when: "chaining named queries"
+        def results = Author.activeAuthors.fromCountry('USA').list()
+
+        then: "active USA authors"
+        results.size() >= 2  // At least our 2 active USA test authors
+        results.every { it.active && it.country == 'USA' }
+    }
+
+    def "named query - with criteria"() {
+        when: "combining named query with criteria"
+        def results = Author.activeAuthors.list {
+            gt('birthYear', 1970)
+        }
+
+        then: "active authors born after 1970"
+        results.every { it.active && it.birthYear > 1970 }
+    }
+
+    def "named query - book queries"() {
+        when: "using book named queries"
+        def fantasyBooks = GormBook.inGenre('Fantasy').list()
+        def highRated = GormBook.highlyRated.list()
+        def affordable = GormBook.pricedBetween(10.00, 20.00).list()
+
+        then: "queries return correct results"
+        fantasyBooks.every { it.genre == 'Fantasy' }
+        highRated.every { it.rating >= 4.0 }
+        affordable.every { it.price >= 10.00 && it.price <= 20.00 }
+    }
+
+    def "named query - count"() {
+        when: "counting with named query"
+        def count = GormBook.availableInPrint.count()
+
+        then: "count is returned"
+        count >= 9
+    }
+
+    // ========== Detached Criteria Tests ==========
+
+    def "detached criteria - basic"() {
+        given: "a detached criteria"
+        def criteria = new grails.gorm.DetachedCriteria(GormBook).build {
+            eq('genre', 'Fantasy')
+        }
+
+        when: "executing detached criteria"
+        def results = criteria.list()
+
+        then: "matching books are returned"
+        results.every { it.genre == 'Fantasy' }
+    }
+
+    def "detached criteria - with projections"() {
+        given: "a detached criteria with projection"
+        def criteria = new grails.gorm.DetachedCriteria(GormBook).build {
+            projections {
+                property('title')
+            }
+            eq('inPrint', true)
+        }
+
+        when: "executing"
+        def titles = criteria.list()
+
+        then: "titles are returned"
+        titles.every { it instanceof String }
+    }
+
+    def "detached criteria - reusable"() {
+        given: "a base criteria"
+        def baseCriteria = new grails.gorm.DetachedCriteria(GormBook).build {
+            gt('rating', 4.0d)
+        }
+
+        when: "reusing with additional conditions"
+        def expensiveHighRated = baseCriteria.build {
+            gt('price', 20.00)
+        }.list()
+
+        def cheapHighRated = baseCriteria.build {
+            lt('price', 20.00)
+        }.list()
+
+        then: "both queries work independently"
+        expensiveHighRated.every { it.rating > 4.0 && it.price > 20.00 }
+        cheapHighRated.every { it.rating > 4.0 && it.price < 20.00 }
+    }
+
+    // ========== Batch Processing Tests ==========
+
+    def "batch processing with withSession"() {
+        when: "processing in batches"
+        def processedCount = 0
+        GormBook.withSession { session ->
+            GormBook.list().each { book ->
+                // Simulate batch processing
+                processedCount++
+                if (processedCount % 5 == 0) {
+                    session.flush()
+                    session.clear()
+                }
+            }
+        }
+
+        then: "all books processed"
+        processedCount >= 10
+    }
+
+    def "read-only query optimization"() {
+        when: "executing read-only query"
+        def results = GormBook.createCriteria().list {
+            readOnly(true)
+            eq('inPrint', true)
+        }
+
+        then: "results are returned (read-only mode)"
+        results.size() >= 9
+    }
+}

Reply via email to