From b4c5d98fc9f045ffce5a540b26d50cb116b6f2e6 Mon Sep 17 00:00:00 2001 From: Nathan Rauh Date: Mon, 4 May 2026 11:27:29 -0500 Subject: [PATCH 1/3] Issue #532 TCK tests for First annotation (rebased) Co-authored-by-AI: IBM Bob 1.0.2 --- .../data/framework/read/only/Countries.java | 37 +++ .../data/standalone/entity/FirstTests.java | 271 ++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java diff --git a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java index f0c10caee..135b3c45d 100644 --- a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java +++ b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java @@ -23,6 +23,7 @@ import jakarta.data.Limit; import jakarta.data.Order; +import jakarta.data.Sort; import jakarta.data.Sort.Nulls; import jakarta.data.constraint.AtLeast; import jakarta.data.constraint.AtMost; @@ -43,8 +44,10 @@ import jakarta.data.repository.By; import jakarta.data.repository.CrudRepository; import jakarta.data.repository.Find; +import jakarta.data.repository.First; import jakarta.data.repository.Is; import jakarta.data.repository.OrderBy; +import jakarta.data.repository.Query; import jakarta.data.repository.Repository; import jakarta.data.repository.Select; import jakarta.data.restrict.Restriction; @@ -57,6 +60,12 @@ @Repository public interface Countries extends CrudRepository { + @Find + @First(3) + @OrderBy(value = _Country.NAME) + List alphabetizedAfter( + @By(_Country.NAME) @Is(GreaterThan.class) String startAfter); + @Find @OrderBy(value = _Country.DAYLIGHTTIMEENDS, nullOrdering = Nulls.LAST) @@ -122,6 +131,12 @@ List findLargest( @By(_Country.POPULATION) @Is(AtLeast.class) long minPopulation, Order order); + @Find + @First(4) + @OrderBy(value = _Country.POPULATION, descending = true) + List fourMostPopulousOf( + @By(_Country.CODE) @Is(In.class) List codes); + @Find List highlyPopulousInRegion( @By(_Country.REGION) EqualTo region, @@ -136,10 +151,32 @@ Stream inRegion( List inSomeOtherRegionThan( @By(_Country.REGION) NotEqualTo exclude); + @Find + @First(15) + List largest15Matching(@By(_Country.NAME) Like pattern, + Order order); + + @First + @Query("WHERE code IS NOT NULL ORDER BY area DESC") + Optional largestByArea(); + + @Find + @First + Optional leastBy(Sort sort); + @Find List lessPopulousThan( @By(_Country.POPULATION) LessThan threshold); + @Find + @First + @OrderBy(value = _Country.POPULATION, descending = true) + Country mostPopulous(); + + @First(5) + @Query("WHERE region = ?1 ORDER BY population DESC") + List mostPopulousIn(Region region); + @Find List namedAnyOf( @By(_Country.NAME) In names); diff --git a/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java b/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java new file mode 100644 index 000000000..d95a4a9fe --- /dev/null +++ b/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package ee.jakarta.tck.data.standalone.entity; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.logging.Logger; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.jupiter.api.BeforeEach; + +import ee.jakarta.tck.data.framework.junit.anno.AnyEntity; +import ee.jakarta.tck.data.framework.junit.anno.Assertion; +import ee.jakarta.tck.data.framework.junit.anno.ReadOnlyTest; +import ee.jakarta.tck.data.framework.junit.anno.Standalone; +import ee.jakarta.tck.data.framework.read.only.Countries; +import ee.jakarta.tck.data.framework.read.only.Country; +import ee.jakarta.tck.data.framework.read.only.CountryPopulator; +import ee.jakarta.tck.data.framework.read.only.Region; +import ee.jakarta.tck.data.framework.read.only._Country; +import ee.jakarta.tck.data.framework.utilities.DatabaseType; +import ee.jakarta.tck.data.framework.utilities.TestProperty; +import jakarta.data.Order; +import jakarta.data.constraint.Like; +import jakarta.inject.Inject; + +/** + * Tests for the First annotation on repository @Find and @Query methods. + */ +@AnyEntity +@ReadOnlyTest +@Standalone +public class FirstTests { + + public static final Logger log = + Logger.getLogger(FirstTests.class.getCanonicalName()); + + @Inject + Countries countries; + + // Inject doesn't happen until after BeforeClass, so this is necessary + // before each test + @BeforeEach + public void beforeEach() { + CountryPopulator.get().populate(countries); + } + + @Deployment + public static WebArchive createDeployment() { + return ShrinkWrap.create(WebArchive.class) + .addClasses(FirstTests.class); + } + + private final DatabaseType type = + TestProperty.databaseType.getDatabaseType(); + + @Assertion(id = "532", strategy = """ + Use @First annotation without a value (defaults to 1) on a @Find + method with @OrderBy to retrieve the first result. Verify that + exactly one result is returned and it is the first according to + the sort order. + """) + public void testFindFirstDefaulted() { + Country found; + try { + found = countries.mostPopulous(); + } catch (UnsupportedOperationException x) { + if (type.capableOfQueryWithoutWhere() && + type.capableOfSingleSort()) { + throw x; + } else { + return; + } + } + + assertEquals("IN", found.getCode()); + assertEquals("India", found.getName()); + } + + @Assertion(id = "532", strategy = """ + Use @First annotation with value 15 on a @Find method with Order + and constraint parameters to retrieve the first 15 results from a + filtered set. Verify that exactly 15 results are returned in the + correct sort order from the filtered results. + """) + public void testFindFirstFifteenWithConstraint() { + List found; + try { + found = countries.largest15Matching(Like.suffix("ia"), + Order.by(_Country.region.asc(), + _Country.code.asc())); + } catch (UnsupportedOperationException x) { + if (type.capableOfConstraintsOnNonIdAttributes() && + type.capableOfLike() && + type.capableOfMultipleSort()) { + throw x; + } else { + return; + } + } + + assertEquals(List.of("DZ: Algeria", + "ET: Ethiopia", + "GM: Gambia", + "LR: Liberia", + "MR: Mauritania", + "NA: Namibia", + "NG: Nigeria", + "SO: Somalia", + "TN: Tunisia", + "TZ: Tanzania", + "ZM: Zambia", + "AM: Armenia", + "GE: Georgia", + "ID: Indonesia", + "IN: India"), + found.stream() + .map(c -> c.getCode() + ": " + c.getName()) + .toList()); + } + + @Assertion(id = "532", strategy = """ + Use @First annotation with value 4 on a @Find method with an + Is(In) constraint on the Id attribute. Verify that exactly + 4 results are returned in the correct sort order. + """) + public void testFindFirstFour() { + List found; + try { + found = countries.fourMostPopulousOf(List.of("JO", + "GR", + "SS", + "ZW", + "HT", + "RO")); + } catch (UnsupportedOperationException x) { + if (type.capableOfIn() && + type.capableOfSingleSort()) { + throw x; + } else { + return; + } + } + + assertEquals(List.of("RO: Romania", + "ZW: Zimbabwe", + "SS: South Sudan", + "HT: Haiti"), + found.stream() + .map(c -> c.getCode() + ": " + c.getName()) + .toList()); + } + + @Assertion(id = "532", strategy = """ + Use @First annotation with value 1 on a @Find method with a given + Sort to retrieve the first result. Verify that exactly one result + is returned and it is the first according to the sort order. + """) + public void testFindFirstOne() { + Country found; + try { + found = countries.leastBy(_Country.area.asc()) + .orElseThrow(); + } catch (UnsupportedOperationException x) { + if (type.capableOfQueryWithoutWhere() && + type.capableOfSingleSort()) { + throw x; + } else { + return; + } + } + + assertEquals("VA", found.getCode()); + assertEquals("Vatican City State", found.getName()); + } + + @Assertion(id = "532", strategy = """ + Use @First annotation with value 3 on a @Find method with @OrderBy + to retrieve the first 3 results. Verify that exactly 3 results are + returned in the correct sort order. + """) + public void testFindFirstThree() { + List found; + try { + found = countries.alphabetizedAfter("Dominican Republic"); + } catch (UnsupportedOperationException x) { + if (type.capableOfConstraintsOnNonIdAttributes() && + type.capableOfGreaterThan() && + type.capableOfSingleSort()) { + throw x; + } else { + return; + } + } + + assertEquals(List.of("TL: East Timor", + "EC: Ecuador", + "EG: Egypt"), + found.stream() + .map(c -> c.getCode() + ": " + c.getName()) + .toList()); + } + + @Assertion(id = "532", strategy = """ + Use @First annotation on a @Query method with ORDER BY clause + to retrieve the first result. Verify that exactly one result is + returned and it is the first according to the sort order. + """) + public void testQueryFirst() { + Country found; + try { + found = countries.largestByArea() + .orElseThrow(); + } catch (UnsupportedOperationException x) { + if (type.capableOfNotNull() && + type.capableOfSingleSort()) { + throw x; + } else { + return; + } + } + + assertEquals("RU", found.getCode()); + assertEquals("Russia", found.getName()); + } + + @Assertion(id = "532", strategy = """ + Use @First annotation with value 5 on a @Query method with ORDER BY + to retrieve the first 5 results. Verify that exactly 5 results are + returned in the correct sort order. + """) + public void testQueryFirstFive() { + List found; + try { + found = countries.mostPopulousIn(Region.SOUTH_AMERICA); + } catch (UnsupportedOperationException x) { + if (type.capableOfConstraintsOnNonIdAttributes() && + type.capableOfSingleSort()) { + throw x; + } else { + return; + } + } + + assertEquals(List.of("BR: Brazil", + "CO: Colombia", + "AR: Argentina", + "PE: Peru", + "VE: Venezuela"), + found.stream() + .map(c -> c.getCode() + ": " + c.getName()) + .toList()); + } + +} From 30f8f166796903e603e8bf6aa9a20167eac9fc28 Mon Sep 17 00:00:00 2001 From: Nathan Rauh Date: Mon, 4 May 2026 11:58:39 -0500 Subject: [PATCH 2/3] Issue #532 TCK tests for First annotation where fewer results are found Co-authored-by-AI: IBM Bob 1.0.2 --- .../data/framework/read/only/Countries.java | 6 +++ .../data/standalone/entity/FirstTests.java | 48 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java index 135b3c45d..bb2ee7d73 100644 --- a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java +++ b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java @@ -197,6 +197,12 @@ Stream namedUnlike( List namedUpTo( @Is(AtMost.class) String name); + @Query("FROM Country WHERE code > ?1") + @Select(_Country.CODE) + @First + @OrderBy(_Country.CODE) + Optional nextCode(String currentCode); + @Find List notInRegions( @By(_Country.REGION) NotIn excluded); diff --git a/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java b/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java index d95a4a9fe..8a54ea9a6 100644 --- a/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java +++ b/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java @@ -18,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; +import java.util.Optional; import java.util.logging.Logger; import org.jboss.arquillian.container.test.api.Deployment; @@ -217,6 +218,32 @@ public void testFindFirstThree() { .toList()); } + @Assertion(id = "532", strategy = """ + Use @First annotation with value 4 on a @Find method, but + where only 2 results match. Verify that exactly 2 results are + returned in the correct sort order. + """) + public void testFindTwoOfFirstFourMax() { + List found; + try { + found = countries.fourMostPopulousOf(List.of("PA", + "PH")); + } catch (UnsupportedOperationException x) { + if (type.capableOfIn() && + type.capableOfSingleSort()) { + throw x; + } else { + return; + } + } + + assertEquals(List.of("PH: Philippines", + "PA: Panama"), + found.stream() + .map(c -> c.getCode() + ": " + c.getName()) + .toList()); + } + @Assertion(id = "532", strategy = """ Use @First annotation on a @Query method with ORDER BY clause to retrieve the first result. Verify that exactly one result is @@ -268,4 +295,25 @@ public void testQueryFirstFive() { .toList()); } + @Assertion(id = "532", strategy = """ + Use @First annotation on a @Query method with clause + to retrieve the first result. Verify that exactly one result is + returned and it is the first according to the sort order. + """) + public void testQueryFirstNoneFound() { + Optional found; + try { + found = countries.nextCode("ZW"); + } catch (UnsupportedOperationException x) { + if (type.capableOfGreaterThan() && + type.capableOfSingleSort()) { + throw x; + } else { + return; + } + } + + assertEquals(true, found.isEmpty()); + } + } From de9ae6c5c65fb3c2fb693e01e30f35c160ea451a Mon Sep 17 00:00:00 2001 From: Nathan Rauh Date: Thu, 7 May 2026 16:04:17 -0500 Subject: [PATCH 3/3] Address review comments to improve tests --- .../data/framework/read/only/Countries.java | 6 +++- .../data/standalone/entity/FirstTests.java | 31 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java index bb2ee7d73..ea7fefb7c 100644 --- a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java +++ b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/Countries.java @@ -63,7 +63,7 @@ public interface Countries extends CrudRepository { @Find @First(3) @OrderBy(value = _Country.NAME) - List alphabetizedAfter( + Country[] alphabetizedAfter( @By(_Country.NAME) @Is(GreaterThan.class) String startAfter); @Find @@ -131,6 +131,10 @@ List findLargest( @By(_Country.POPULATION) @Is(AtLeast.class) long minPopulation, Order order); + @Find + @First + List firstOf(Order order); + @Find @First(4) @OrderBy(value = _Country.POPULATION, descending = true) diff --git a/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java b/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java index 8a54ea9a6..313576ded 100644 --- a/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java +++ b/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/FirstTests.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Optional; import java.util.logging.Logger; +import java.util.stream.Stream; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.shrinkwrap.api.ShrinkWrap; @@ -94,6 +95,32 @@ public void testFindFirstDefaulted() { assertEquals("India", found.getName()); } + @Assertion(id = "532", strategy = """ + Use @First annotation defaulting to a value of 1 on a @Find method + with a given Order to retrieve the first result as a List. Verify + that the resulting List contains exactly one result and it is the + first according to the sort order. + """) + public void testFindFirstEntityAsList() { + List found; + try { + found = countries.firstOf(Order.by(_Country.region.asc(), + _Country.area.desc())); + } catch (UnsupportedOperationException x) { + if (type.capableOfQueryWithoutWhere() && + type.capableOfMultipleSort()) { + throw x; + } else { + return; + } + } + + assertEquals(List.of("DZ: Algeria"), + found.stream() + .map(c -> c.getCode() + ": " + c.getName()) + .toList()); + } + @Assertion(id = "532", strategy = """ Use @First annotation with value 15 on a @Find method with Order and constraint parameters to retrieve the first 15 results from a @@ -197,7 +224,7 @@ public void testFindFirstOne() { returned in the correct sort order. """) public void testFindFirstThree() { - List found; + Country[] found; try { found = countries.alphabetizedAfter("Dominican Republic"); } catch (UnsupportedOperationException x) { @@ -213,7 +240,7 @@ public void testFindFirstThree() { assertEquals(List.of("TL: East Timor", "EC: Ecuador", "EG: Egypt"), - found.stream() + Stream.of(found) .map(c -> c.getCode() + ": " + c.getName()) .toList()); }