From 45968812961f768ede5ad7be6cc5cdcde61077de Mon Sep 17 00:00:00 2001 From: vasiliy-mikhailov Date: Fri, 26 Jun 2026 23:14:51 +0300 Subject: [PATCH] Add unit tests for Queryable Extends the existing approvaltests-util-tests/QueryableTest with additive tests covering where, all/any, max/min (with selectors and ties), sum/average, skip/take, split, orderBy, firstOrDefault, the of/as factory variants, and empty/null/single-element edge cases. No production code changed. Signed-off-by: vasiliy-mikhailov --- .../java/org/lambda/query/QueryableTest.java | 721 ++++++++++++++++++ 1 file changed, 721 insertions(+) diff --git a/approvaltests-util-tests/src/test/java/org/lambda/query/QueryableTest.java b/approvaltests-util-tests/src/test/java/org/lambda/query/QueryableTest.java index b4fbac19..d3a59831 100644 --- a/approvaltests-util-tests/src/test/java/org/lambda/query/QueryableTest.java +++ b/approvaltests-util-tests/src/test/java/org/lambda/query/QueryableTest.java @@ -5,15 +5,23 @@ import org.junit.jupiter.api.Test; import org.lambda.Extendable; import org.lambda.utils.Range; +import org.lambda.query.OrderBy.Order; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; class QueryableTest { @@ -260,4 +268,717 @@ void testFirstOrThrow() Approvals.verifyException(() -> queryable.firstOrThrow(i -> i == 4, () -> new RuntimeException("4 not found")), new Options().inline(expected)); } + + @Test + void testDefaultConstructor() + { + Queryable q = new Queryable<>(); + assertEquals(0, q.size()); + } + + @Test + void testConstructorWithType() + { + Queryable q = new Queryable<>(Integer.class); + assertEquals(Integer.class, q.getType()); + } + + @Test + void testCreateEmptyWithNull() + { + Queryable empty = Queryable.createEmpty(null); + assertEquals(Object.class, empty.getType()); + assertEquals(0, empty.size()); + } + + @Test + void testCreateEmptyWithArray() + { + Queryable empty = Queryable.createEmpty(new Integer[]{}); + assertEquals(Integer.class, empty.getType()); + assertEquals(0, empty.size()); + } + + @Test + void testCreateEmptyWithNonEmptyArray() + { + Queryable empty = Queryable.createEmpty(new Integer[]{1, 2, 3}); + assertEquals(Integer.class, empty.getType()); + assertEquals(0, empty.size()); + } + + @Test + void testOfVarargs() + { + Queryable q = Queryable.of(1, 2, 3); + assertEquals(3, q.size()); + assertEquals(1, q.get(0).intValue()); + assertEquals(2, q.get(1).intValue()); + assertEquals(3, q.get(2).intValue()); + } + + @Test + void testOfList() + { + List list = Arrays.asList("a", "b", "c"); + Queryable q = Queryable.of(list); + assertEquals(3, q.size()); + assertEquals("a", q.get(0)); + } + + @Test + void testOfListWithType() + { + List list = Arrays.asList(1, 2.5, 3); + Queryable q = Queryable.of(list, Number.class); + assertEquals(3, q.size()); + assertEquals(Number.class, q.getType()); + } + + @Test + void testOfSet() + { + Set set = new HashSet<>(Arrays.asList(1, 2, 3)); + Queryable q = Queryable.of(set); + assertEquals(3, q.size()); + } + + @Test + void testOfSetWithType() + { + Set set = new HashSet<>(Arrays.asList(1, 2.5, 3)); + Queryable q = Queryable.of(set, Number.class); + assertEquals(3, q.size()); + assertEquals(Number.class, q.getType()); + } + + @Test + void testOfStream() + { + Queryable q = Queryable.of(Stream.of(1, 2, 3)); + assertEquals(3, q.size()); + } + + @Test + void testAsList() + { + List list = Arrays.asList("x", "y"); + Queryable q = Queryable.as(list); + assertEquals(2, q.size()); + } + + @Test + void testAsListAlreadyQueryable() + { + Queryable original = Queryable.as("a", "b"); + Queryable result = Queryable.as(original); + assertSameOrEqual(original, result); + } + + @Test + void testAsSet() + { + Set set = new HashSet<>(Arrays.asList("x", "y")); + Queryable q = Queryable.as(set); + assertEquals(2, q.size()); + } + + @Test + void testAsSetWithType() + { + Set set = new HashSet<>(Arrays.asList(1, 2.5)); + Queryable q = Queryable.as(set, Number.class); + assertEquals(2, q.size()); + assertEquals(Number.class, q.getType()); + } + + @Test + void testAsArray() + { + Queryable q = Queryable.as("a", "b", "c"); + assertEquals(3, q.size()); + } + + @Test + void testAsStream() + { + Queryable q = Queryable.as(Stream.of(10, 20, 30)); + assertEquals(3, q.size()); + } + + @Test + void testSelect() + { + Queryable q = Queryable.as(1, 2, 3); + Queryable result = q.select(i -> "num-" + i); + assertEquals(3, result.size()); + assertEquals("num-1", result.get(0)); + assertEquals("num-2", result.get(1)); + assertEquals("num-3", result.get(2)); + } + + @Test + void testSelectEmpty() + { + Queryable q = new Queryable<>(); + Queryable result = q.select(i -> "num-" + i); + assertEquals(0, result.size()); + } + + @Test + void testWhere() + { + Queryable q = Queryable.as(1, 2, 3, 4, 5, 6); + Queryable result = q.where(i -> i > 3); + assertEquals(3, result.size()); + assertEquals(4, result.get(0).intValue()); + assertEquals(5, result.get(1).intValue()); + assertEquals(6, result.get(2).intValue()); + } + + @Test + void testWhereNoneMatch() + { + Queryable q = Queryable.as(1, 2, 3); + Queryable result = q.where(i -> i > 100); + assertEquals(0, result.size()); + } + + @Test + void testWhereAllMatch() + { + Queryable q = Queryable.as(1, 2, 3); + Queryable result = q.where(i -> i > 0); + assertEquals(3, result.size()); + } + + @Test + void testFirst() + { + Queryable q = Queryable.as(10, 20, 30); + assertEquals(10, q.first().intValue()); + } + + @Test + void testFirstWithFilter() + { + Queryable q = Queryable.as(1, 2, 3, 4, 5); + assertEquals(3, q.first(i -> i > 2).intValue()); + } + + @Test + void testFirstWithFilterNoMatch() + { + Queryable q = Queryable.as(1, 2, 3); + Integer result = q.first(i -> i > 100); + assertNull(result); + } + + @Test + void testFirstOnEmpty() + { + Queryable q = new Queryable<>(); + Integer result = q.first(); + assertNull(result); + } + + @Test + void testFirstOrDefaultWithDefault() + { + Queryable q = new Queryable<>(); + assertEquals(42, q.firstOrDefault(42).intValue()); + } + + @Test + void testFirstOrDefaultWithElements() + { + Queryable q = Queryable.as(10, 20); + assertEquals(10, q.firstOrDefault(42).intValue()); + } + + @Test + void testFirstOrThrowFinds() + { + Queryable q = Queryable.as(1, 2, 3); + assertEquals(3, q.firstOrThrow(i -> i > 2, () -> new RuntimeException("not found")).intValue()); + } + + @Test + void testFirstOrThrowThrows() + { + Queryable q = Queryable.as(1, 2, 3); + RuntimeException ex = assertThrows(RuntimeException.class, + () -> q.firstOrThrow(i -> i > 100, () -> new RuntimeException("not found"))); + assertEquals("not found", ex.getMessage()); + } + + @Test + void testLast() + { + Queryable q = Queryable.as(10, 20, 30); + assertEquals(30, q.last().intValue()); + } + + @Test + void testLastSingleElement() + { + Queryable q = Queryable.as(42); + assertEquals(42, q.last().intValue()); + } + + @Test + void testAllTrue() + { + Queryable q = Queryable.as(2, 4, 6); + assertTrue(q.all(i -> i % 2 == 0)); + } + + @Test + void testAllFalse() + { + Queryable q = Queryable.as(2, 3, 6); + assertFalse(q.all(i -> i % 2 == 0)); + } + + @Test + void testAllEmpty() + { + Queryable q = new Queryable<>(); + assertTrue(q.all(i -> true)); + } + + @Test + void testAnyTrue() + { + Queryable q = Queryable.as(1, 2, 3); + assertTrue(q.any(i -> i > 2)); + } + + @Test + void testAnyFalse() + { + Queryable q = Queryable.as(1, 2, 3); + assertFalse(q.any(i -> i > 100)); + } + + @Test + void testAnyEmpty() + { + Queryable q = new Queryable<>(); + assertFalse(q.any(i -> true)); + } + + @Test + void testMaxWithSelector() + { + Queryable q = Queryable.as("hi", "hello", "hey"); + String result = q.max(s -> s.length()); + assertEquals("hello", result); + } + + @Test + void testMinWithSelector() + { + Queryable q = Queryable.as("hi", "hello", "hey"); + String result = q.min(s -> s.length()); + assertEquals("hi", result); + } + + @Test + void testMaxDirect() + { + Queryable q = Queryable.as(3, 1, 4, 1, 5, 9); + assertEquals(9, q.max().intValue()); + } + + @Test + void testMinDirect() + { + Queryable q = Queryable.as(3, 1, 4, 1, 5, 9); + assertEquals(1, q.min().intValue()); + } + + @Test + void testAverage() + { + Queryable q = Queryable.as(1, 2, 3, 4, 5); + assertEquals(3.0, q.average(i -> i), 0.001); + } + + @Test + void testSumWithSelector() + { + Queryable q = Queryable.as("1", "2", "3"); + assertEquals(6.0, q.sum(s -> Integer.parseInt(s)), 0.001); + } + + @Test + void testSumDirect() + { + Queryable q = Queryable.as(1, 2, 3, 4); + assertEquals(10.0, q.sum(), 0.001); + } + + @Test + void testOrderByAscending() + { + Queryable q = Queryable.as(3, 1, 2); + Queryable result = q.orderBy(i -> i); + assertEquals(1, result.get(0).intValue()); + assertEquals(2, result.get(1).intValue()); + assertEquals(3, result.get(2).intValue()); + } + + @Test + void testOrderByDescending() + { + Queryable q = Queryable.as(3, 1, 2); + Queryable result = q.orderBy(Order.Descending, i -> i); + assertEquals(3, result.get(0).intValue()); + assertEquals(2, result.get(1).intValue()); + assertEquals(1, result.get(2).intValue()); + } + + @Test + void testDistinctEmpty() + { + Queryable q = new Queryable<>(); + Queryable result = q.distinct(); + assertEquals(0, result.size()); + } + + @Test + void testAsArrayBasic() + { + Character[] letters = Queryable.as('L', 'a', 'r', 's').asArray(); + assertArrayEquals(new Character[]{'L', 'a', 'r', 's'}, letters); + } + + @Test + void testAsArrayFromEmpty() + { + Queryable q = Queryable.as(new String[0]); + q.add("Lars"); + String[] arr = q.asArray(); + assertArrayEquals(new String[]{"Lars"}, arr); + } + + @Test + void testSelectManyArray() + { + Queryable q = Queryable.as("ab", "cd"); + Queryable result = q + .selectManyArray(s -> s.chars().mapToObj(c -> (char) c).toArray(Character[]::new)); + assertEquals(4, result.size()); + assertEquals('a', result.get(0)); + assertEquals('b', result.get(1)); + assertEquals('c', result.get(2)); + assertEquals('d', result.get(3)); + } + + @Test + void testJoinEmpty() + { + String result = Queryable.as(new String[0]).join(","); + assertEquals("", result); + } + + @Test + void testSplit() + { + Queryable q = Queryable.as(1, 2, 0, 3, 4, 0, 5); + Queryable> result = q.split(i -> i == 0); + // split at 0: [1,2], [3,4], trailing [5] is NOT added since no trailing split point + // Actually: when split point hit, add current part and reset. After loop, trailing part is NOT added. + assertEquals(2, result.size()); + assertEquals(2, result.get(0).size()); + assertEquals(1, result.get(0).get(0).intValue()); + assertEquals(2, result.get(0).get(1).intValue()); + assertEquals(2, result.get(1).size()); + assertEquals(3, result.get(1).get(0).intValue()); + assertEquals(4, result.get(1).get(1).intValue()); + } + + @Test + void testSplitNoSplitPoints() + { + Queryable q = Queryable.as(1, 2, 3); + Queryable> result = q.split(i -> i == 0); + assertEquals(0, result.size()); + } + + @Test + void testSplitAllSplitPoints() + { + Queryable q = Queryable.as(0, 0, 0); + Queryable> result = q.split(i -> i == 0); + assertEquals(3, result.size()); + for (Queryable part : result) + { + assertEquals(0, part.size()); + } + } + + @Test + void testSplitEmpty() + { + Queryable q = new Queryable<>(); + Queryable> result = q.split(i -> true); + assertEquals(0, result.size()); + } + + @Test + void testSkip() + { + Queryable q = Queryable.as(1, 2, 3, 4, 5); + Queryable result = q.skip(2); + assertEquals(3, result.size()); + assertEquals(3, result.get(0).intValue()); + } + + @Test + void testSkipMoreThanSize() + { + Queryable q = Queryable.as(1, 2, 3); + Queryable result = q.skip(10); + assertEquals(0, result.size()); + } + + @Test + void testTake() + { + Queryable q = Queryable.as(1, 2, 3, 4, 5); + Queryable result = q.take(3); + assertEquals(3, result.size()); + assertEquals(1, result.get(0).intValue()); + } + + @Test + void testTakeMoreThanSize() + { + Queryable q = Queryable.as(1, 2, 3); + Queryable result = q.take(10); + assertEquals(3, result.size()); + } + + @Test + void testTakeAndSkipCombined() + { + Queryable q = Queryable.as(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + Queryable result = q.skip(3).take(4); + assertEquals(4, result.size()); + assertEquals(4, result.get(0).intValue()); + assertEquals(7, result.get(3).intValue()); + } + + @Test + void testCombine() + { + Queryable q1 = Queryable.as(1, 2); + Queryable q2 = Queryable.as(3, 4); + Queryable result = q1.combine(q2); + assertEquals(4, result.size()); + assertEquals(1, result.get(0).intValue()); + assertEquals(4, result.get(3).intValue()); + } + + @Test + void testSelectRecursivelyUntil() + { + Queryable q = Queryable.as(48, 8); + Queryable result = q.selectRecursivelyUntil(i -> i / 2, i -> i <= 1); + // 48: do{add 48; item=24}while(24<=1?no) -> do{add 24; item=12}while(12<=1?no) -> do{add 12; item=6}while(6<=1?no) -> do{add 6; item=3}while(3<=1?no) -> do{add 3; item=1}while(1<=1?yes,stop) = 5 items + // 8: do{add 8; item=4}while(4<=1?no) -> do{add 4; item=2}while(2<=1?no) -> do{add 2; item=1}while(1<=1?yes,stop) = 3 items + // Total: 8 items + assertEquals(8, result.size()); + assertEquals(48, result.get(0).intValue()); + assertEquals(24, result.get(1).intValue()); + assertEquals(3, result.get(4).intValue()); + assertEquals(8, result.get(5).intValue()); + assertEquals(2, result.get(7).intValue()); + } + + @Test + void testSelectRecursivelyUntilSingleElement() + { + Queryable q = Queryable.as(16); + Queryable result = q.selectRecursivelyUntil(i -> i / 2, i -> i <= 1); + // 16: do{add 16; item=8}while(8<=1?no) -> do{add 8; item=4}while(4<=1?no) -> do{add 4; item=2}while(2<=1?no) -> do{add 2; item=1}while(1<=1?yes,stop) = 4 items + assertEquals(4, result.size()); + assertEquals(16, result.get(0).intValue()); + assertEquals(8, result.get(1).intValue()); + assertEquals(4, result.get(2).intValue()); + assertEquals(2, result.get(3).intValue()); + } + + @Test + void testUseExtendable() + { + Queryable list = Queryable.as("One fish", "two fish", "red fish", "blue fish"); + Queryable result = list.select(String::toUpperCase).use(CustomQuery.class).findFirstWordsOnly(); + assertEquals(4, result.size()); + assertEquals("ONE", result.get(0)); + assertEquals("TWO", result.get(1)); + assertEquals("RED", result.get(2)); + assertEquals("BLUE", result.get(3)); + } + + @Test + void testMaxOnEmpty() + { + Queryable q = new Queryable<>(); + Integer result = q.max(i -> i); + assertNull(result); + } + + @Test + void testMinOnEmpty() + { + Queryable q = new Queryable<>(); + Integer result = q.min(i -> i); + assertNull(result); + } + + @Test + void testSumOnEmpty() + { + Queryable q = new Queryable<>(); + assertEquals(0.0, q.sum(), 0.001); + } + + @Test + void testAverageOnEmpty() + { + Queryable q = new Queryable<>(); + // average on empty divides by zero -> Infinity or NaN + Double avg = q.average(i -> i); + assertTrue(Double.isNaN(avg) || Double.isInfinite(avg)); + } + + @Test + void testOrderByEmpty() + { + Queryable q = new Queryable<>(); + Queryable result = q.orderBy(i -> i); + assertEquals(0, result.size()); + } + + @Test + void testWhereOnEmpty() + { + Queryable q = new Queryable<>(); + Queryable result = q.where(i -> true); + assertEquals(0, result.size()); + } + + @Test + void testSelectOnEmpty() + { + Queryable q = new Queryable<>(); + Queryable result = q.select(i -> "x"); + assertEquals(0, result.size()); + } + + @Test + void testEmptyListWithType() + { + List strings = new ArrayList<>(); + Queryable as = Queryable.as(strings, String.class); + as.add("Lars"); + String[] asArray = as.asArray(); + assertArrayEquals(new String[]{"Lars"}, asArray); + } + + @Test + void testEmptyArray() + { + String[] strings = new String[0]; + Queryable as = Queryable.as(strings); + as.add("Lars"); + String[] asArray = as.asArray(); + assertArrayEquals(new String[]{"Lars"}, asArray); + } + + @Test + void testMaxSingleElement() + { + Queryable q = Queryable.as("hello"); + String result = q.max(s -> s.length()); + assertEquals("hello", result); + } + + @Test + void testMinSingleElement() + { + Queryable q = Queryable.as("hello"); + String result = q.min(s -> s.length()); + assertEquals("hello", result); + } + + @Test + void testAllSingleElementTrue() + { + Queryable q = Queryable.as(2); + assertTrue(q.all(i -> i % 2 == 0)); + } + + @Test + void testAllSingleElementFalse() + { + Queryable q = Queryable.as(3); + assertFalse(q.all(i -> i % 2 == 0)); + } + + @Test + void testAnySingleElementTrue() + { + Queryable q = Queryable.as(2); + assertTrue(q.any(i -> i % 2 == 0)); + } + + @Test + void testAnySingleElementFalse() + { + Queryable q = Queryable.as(3); + assertFalse(q.any(i -> i % 2 == 0)); + } + + @Test + void testSelectWithNullElements() + { + Queryable q = Queryable.as("a", null, "b"); + Queryable result = q.select(s -> s == null ? 0 : s.length()); + assertEquals(1, result.get(0).intValue()); + assertEquals(0, result.get(1).intValue()); + assertEquals(1, result.get(2).intValue()); + } + + @Test + void testWhereWithNullElements() + { + Queryable q = Queryable.as("a", null, "b"); + Queryable result = q.where(s -> s != null); + assertEquals(2, result.size()); + } + + @Test + void testMaxWithTies() + { + Queryable q = Queryable.as("hi", "ok", "no"); + String result = q.max(s -> s.length()); + assertEquals("hi", result); // first one wins + } + + @Test + void testMinWithTies() + { + Queryable q = Queryable.as("hi", "ok", "no"); + String result = q.min(s -> s.length()); + assertEquals("hi", result); // first one wins + } + + private void assertSameOrEqual(Object a, Object b) + { + assertTrue(a == b || a.equals(b)); + } }