Stabilize scrolling by example API tests.
This commit is contained in:
@@ -23,6 +23,8 @@ import java.util.function.Function;
|
||||
|
||||
import org.assertj.core.data.Index;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.neo4j.driver.Driver;
|
||||
@@ -46,6 +48,7 @@ import org.springframework.data.neo4j.test.Neo4jExtension;
|
||||
import org.springframework.data.neo4j.test.Neo4jImperativeTestConfiguration;
|
||||
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
|
||||
import org.springframework.data.support.WindowIterator;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@@ -57,174 +60,197 @@ class ScrollingIT {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
|
||||
@Nested
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@DisplayName("Scroll with derived finder method")
|
||||
class ScrollWithDerivedFinderMethod {
|
||||
|
||||
@BeforeAll
|
||||
static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
|
||||
try (
|
||||
var session = driver.session(bookmarkCapture.createSessionConfig());
|
||||
var transaction = session.beginTransaction()
|
||||
) {
|
||||
ScrollingEntity.createTestData(transaction);
|
||||
transaction.commit();
|
||||
bookmarkCapture.seedWith(session.lastBookmarks());
|
||||
@BeforeAll
|
||||
static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
|
||||
try (
|
||||
var session = driver.session(bookmarkCapture.createSessionConfig());
|
||||
var transaction = session.beginTransaction()
|
||||
) {
|
||||
ScrollingEntity.createTestData(transaction);
|
||||
transaction.commit();
|
||||
bookmarkCapture.seedWith(session.lastBookmarks());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneColumnSortNoScroll(@Autowired ScrollingRepository repository) {
|
||||
|
||||
var topN = repository.findTop4ByOrderByB();
|
||||
assertThat(topN)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0", "C0", "D0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void forwardWithDuplicatesManualIteration(@Autowired ScrollingRepository repository) {
|
||||
|
||||
var duplicates = repository.findAllByAOrderById("D0");
|
||||
assertThat(duplicates).hasSize(2);
|
||||
|
||||
var window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.keyset());
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(0).getId()), Index.atIndex(3))
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0", "C0", "D0");
|
||||
|
||||
window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1));
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(1).getId()), Index.atIndex(0))
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("D0", "E0", "F0", "G0");
|
||||
|
||||
window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1));
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("H0", "I0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void forwardWithDuplicatesIteratorIteration(@Autowired ScrollingRepository repository) {
|
||||
|
||||
var it = WindowIterator.of(pos -> repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos))
|
||||
.startingAt(ScrollPosition.keyset());
|
||||
var content = new ArrayList<ScrollingEntity>();
|
||||
while (it.hasNext()) {
|
||||
var next = it.next();
|
||||
content.add(next);
|
||||
}
|
||||
|
||||
assertThat(content).hasSize(10);
|
||||
assertThat(content.stream().map(ScrollingEntity::getId)
|
||||
.distinct().toList()).hasSize(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void backwardWithDuplicatesManualIteration(@Autowired ScrollingRepository repository) {
|
||||
|
||||
// Recreate the last position
|
||||
var last = repository.findFirstByA("I0");
|
||||
var keys = Map.of(
|
||||
"foobar", Values.value(last.getA()),
|
||||
"b", Values.value(last.getB()),
|
||||
Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString())
|
||||
);
|
||||
|
||||
var duplicates = repository.findAllByAOrderById("D0");
|
||||
assertThat(duplicates).hasSize(2);
|
||||
|
||||
var window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.backward(keys));
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("F0", "G0", "H0", "I0");
|
||||
|
||||
var pos = ((KeysetScrollPosition) window.positionAt(0));
|
||||
pos = ScrollPosition.backward(pos.getKeys());
|
||||
window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos);
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("C0", "D0", "D0", "E0");
|
||||
|
||||
pos = ((KeysetScrollPosition) window.positionAt(0));
|
||||
pos = ScrollPosition.backward(pos.getKeys());
|
||||
window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos);
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneColumnSortNoScroll(@Autowired ScrollingRepository repository) {
|
||||
@Nested
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@DisplayName("ScrollWithExampleApi")
|
||||
class ScrollWithExampleApi {
|
||||
|
||||
var topN = repository.findTop4ByOrderByB();
|
||||
assertThat(topN)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0", "C0", "D0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void forwardWithDuplicatesManualIteration(@Autowired ScrollingRepository repository) {
|
||||
|
||||
var duplicates = repository.findAllByAOrderById("D0");
|
||||
assertThat(duplicates).hasSize(2);
|
||||
|
||||
var window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.keyset());
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(0).getId()), Index.atIndex(3))
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0", "C0", "D0");
|
||||
|
||||
window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1));
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(1).getId()), Index.atIndex(0))
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("D0", "E0", "F0", "G0");
|
||||
|
||||
window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1));
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("H0", "I0");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("GH-2726")
|
||||
void forwardWithFluentQueryByExample(@Autowired ScrollingRepository scrollingRepository) {
|
||||
ScrollingEntity scrollingEntity = new ScrollingEntity();
|
||||
Example<ScrollingEntity> example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues());
|
||||
|
||||
var window = scrollingRepository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.keyset()));
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0", "C0", "D0");
|
||||
|
||||
ScrollPosition newPosition = ScrollPosition.forward(((KeysetScrollPosition) window.positionAt(window.size() - 1)).getKeys());
|
||||
window = scrollingRepository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(newPosition));
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("D0", "E0", "F0", "G0");
|
||||
|
||||
window = scrollingRepository.findTop4By(ScrollingEntity.SORT_BY_C, window.positionAt(window.size() - 1));
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("H0", "I0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void forwardWithDuplicatesIteratorIteration(@Autowired ScrollingRepository repository) {
|
||||
|
||||
var it = WindowIterator.of(pos -> repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos))
|
||||
.startingAt(ScrollPosition.keyset());
|
||||
var content = new ArrayList<ScrollingEntity>();
|
||||
while (it.hasNext()) {
|
||||
var next = it.next();
|
||||
content.add(next);
|
||||
@BeforeAll
|
||||
static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
|
||||
try (
|
||||
var session = driver.session(bookmarkCapture.createSessionConfig());
|
||||
var transaction = session.beginTransaction()
|
||||
) {
|
||||
ScrollingEntity.createTestDataWithoutDuplicates(transaction);
|
||||
transaction.commit();
|
||||
bookmarkCapture.seedWith(session.lastBookmarks());
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(content).hasSize(10);
|
||||
assertThat(content.stream().map(ScrollingEntity::getId)
|
||||
.distinct().toList()).hasSize(10);
|
||||
}
|
||||
@Test
|
||||
@Tag("GH-2726")
|
||||
void forwardWithFluentQueryByExample(@Autowired ScrollingRepository scrollingRepository) {
|
||||
ScrollingEntity scrollingEntity = new ScrollingEntity();
|
||||
Example<ScrollingEntity> example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues());
|
||||
|
||||
@Test
|
||||
void backwardWithDuplicatesManualIteration(@Autowired ScrollingRepository repository) {
|
||||
var window = scrollingRepository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.keyset()));
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0", "C0", "D0");
|
||||
|
||||
// Recreate the last position
|
||||
var last = repository.findFirstByA("I0");
|
||||
var keys = Map.of(
|
||||
"foobar", Values.value(last.getA()),
|
||||
"b", Values.value(last.getB()),
|
||||
Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString())
|
||||
);
|
||||
ScrollPosition newPosition = ScrollPosition.forward(((KeysetScrollPosition) window.positionAt(window.size() - 1)).getKeys());
|
||||
window = scrollingRepository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(newPosition));
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("E0", "F0", "G0", "H0");
|
||||
|
||||
var duplicates = repository.findAllByAOrderById("D0");
|
||||
assertThat(duplicates).hasSize(2);
|
||||
window = scrollingRepository.findTop4By(ScrollingEntity.SORT_BY_C, window.positionAt(window.size() - 1));
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("I0");
|
||||
}
|
||||
|
||||
var window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.backward(keys));
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("F0", "G0", "H0", "I0");
|
||||
@Test
|
||||
void backwardWithFluentQueryByExample(@Autowired ScrollingRepository repository) {
|
||||
|
||||
var pos = ((KeysetScrollPosition) window.positionAt(0));
|
||||
pos = ScrollPosition.backward(pos.getKeys());
|
||||
window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos);
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("C0", "D0", "D0", "E0");
|
||||
ScrollingEntity scrollingEntity = new ScrollingEntity();
|
||||
Example<ScrollingEntity> example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues());
|
||||
|
||||
pos = ((KeysetScrollPosition) window.positionAt(0));
|
||||
pos = ScrollPosition.backward(pos.getKeys());
|
||||
window = repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos);
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0");
|
||||
}
|
||||
var last = repository.findFirstByA("I0");
|
||||
var keys = Map.of(
|
||||
"c", last.getC(),
|
||||
Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString())
|
||||
);
|
||||
|
||||
@Test
|
||||
void backwardWithFluentQueryByExample(@Autowired ScrollingRepository repository) {
|
||||
var window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.backward(keys)));
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("F0", "G0", "H0", "I0");
|
||||
|
||||
ScrollingEntity scrollingEntity = new ScrollingEntity();
|
||||
Example<ScrollingEntity> example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues());
|
||||
var pos = ((KeysetScrollPosition) window.positionAt(0));
|
||||
var nextPos = ScrollPosition.backward(pos.getKeys());
|
||||
window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextPos));
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("B0", "C0", "D0", "E0");
|
||||
|
||||
var last = repository.findFirstByA("I0");
|
||||
var keys = Map.of(
|
||||
"c", last.getC(),
|
||||
Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString())
|
||||
);
|
||||
|
||||
var window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.backward(keys)));
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("F0", "G0", "H0", "I0");
|
||||
|
||||
var pos = ((KeysetScrollPosition) window.positionAt(0));
|
||||
var nextPos = ScrollPosition.backward(pos.getKeys());
|
||||
window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextPos));
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("C0", "D0", "D0", "E0");
|
||||
|
||||
var nextNextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys());
|
||||
window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextPos));
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0");
|
||||
var nextNextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys());
|
||||
window = repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextPos));
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -24,8 +24,13 @@ import java.util.function.Function;
|
||||
|
||||
import org.assertj.core.data.Index;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.neo4j.driver.Driver;
|
||||
import org.neo4j.driver.Values;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -48,6 +53,7 @@ import org.springframework.data.neo4j.test.BookmarkCapture;
|
||||
import org.springframework.data.neo4j.test.Neo4jExtension;
|
||||
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
|
||||
import org.springframework.data.neo4j.test.Neo4jReactiveTestConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.transaction.ReactiveTransactionManager;
|
||||
|
||||
import reactor.test.StepVerifier;
|
||||
@@ -56,221 +62,252 @@ import reactor.test.StepVerifier;
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Neo4jIntegrationTest
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class ReactiveScrollingIT {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
|
||||
|
||||
@BeforeAll
|
||||
static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
|
||||
try (
|
||||
var session = driver.session(bookmarkCapture.createSessionConfig());
|
||||
var transaction = session.beginTransaction()
|
||||
) {
|
||||
ScrollingEntity.createTestData(transaction);
|
||||
transaction.commit();
|
||||
bookmarkCapture.seedWith(session.lastBookmarks());
|
||||
@Nested
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@DisplayName("Scroll with derived finder method")
|
||||
class ScrollWithDerivedFinderMethod {
|
||||
|
||||
@BeforeAll
|
||||
static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
|
||||
try (
|
||||
var session = driver.session(bookmarkCapture.createSessionConfig());
|
||||
var transaction = session.beginTransaction()
|
||||
) {
|
||||
ScrollingEntity.createTestData(transaction);
|
||||
transaction.commit();
|
||||
bookmarkCapture.seedWith(session.lastBookmarks());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void oneColumnSortNoScroll(@Autowired ReactiveScrollingRepository repository) {
|
||||
|
||||
repository.findTop4ByOrderByB()
|
||||
.map(ScrollingEntity::getA)
|
||||
.as(StepVerifier::create)
|
||||
.expectNext("A0", "B0", "C0", "D0")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Order(2)
|
||||
@Test
|
||||
void forwardWithDuplicatesManualIteration(@Autowired ReactiveScrollingRepository repository) {
|
||||
|
||||
var duplicates = new ArrayList<ScrollingEntity>();
|
||||
repository.findAllByAOrderById("D0").as(StepVerifier::create)
|
||||
.recordWith(() -> duplicates)
|
||||
.expectNextCount(2)
|
||||
.verifyComplete();
|
||||
|
||||
var windowContainer = new AtomicReference<Window<ScrollingEntity>>();
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.keyset())
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
var window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(0).getId()), Index.atIndex(3))
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0", "C0", "D0");
|
||||
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(1).getId()), Index.atIndex(0))
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("D0", "E0", "F0", "G0");
|
||||
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("H0", "I0");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
void backwardWithDuplicatesManualIteration(@Autowired ReactiveScrollingRepository repository) {
|
||||
|
||||
// Recreate the last position
|
||||
var last = repository.findFirstByA("I0").block();
|
||||
var keys = Map.of(
|
||||
"foobar", Values.value(last.getA()),
|
||||
"b", Values.value(last.getB()),
|
||||
Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString())
|
||||
);
|
||||
|
||||
var duplicates = new ArrayList<ScrollingEntity>();
|
||||
repository.findAllByAOrderById("D0").as(StepVerifier::create)
|
||||
.recordWith(() -> duplicates)
|
||||
.expectNextCount(2)
|
||||
.verifyComplete();
|
||||
|
||||
var windowContainer = new AtomicReference<Window<ScrollingEntity>>();
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.backward(keys))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
var window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("F0", "G0", "H0", "I0");
|
||||
|
||||
var pos = ((KeysetScrollPosition) window.positionAt(0));
|
||||
pos = ScrollPosition.backward(pos.getKeys());
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos)
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("C0", "D0", "D0", "E0");
|
||||
|
||||
pos = ((KeysetScrollPosition) window.positionAt(0));
|
||||
pos = ScrollPosition.backward(pos.getKeys());
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos)
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneColumnSortNoScroll(@Autowired ReactiveScrollingRepository repository) {
|
||||
@Nested
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@DisplayName("ScrollWithExampleApi")
|
||||
class ScrollWithExampleApi {
|
||||
|
||||
repository.findTop4ByOrderByB()
|
||||
.map(ScrollingEntity::getA)
|
||||
.as(StepVerifier::create)
|
||||
.expectNext("A0", "B0", "C0", "D0");
|
||||
}
|
||||
@BeforeAll
|
||||
static void setupTestData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
|
||||
try (
|
||||
var session = driver.session(bookmarkCapture.createSessionConfig());
|
||||
var transaction = session.beginTransaction()
|
||||
) {
|
||||
ScrollingEntity.createTestDataWithoutDuplicates(transaction);
|
||||
transaction.commit();
|
||||
bookmarkCapture.seedWith(session.lastBookmarks());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void forwardWithDuplicatesManualIteration(@Autowired ReactiveScrollingRepository repository) {
|
||||
@Test
|
||||
@Order(4)
|
||||
@Tag("GH-2726")
|
||||
void forwardWithFluentQueryByExample(@Autowired ReactiveScrollingRepository repository) {
|
||||
ScrollingEntity scrollingEntity = new ScrollingEntity();
|
||||
Example<ScrollingEntity> example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues());
|
||||
|
||||
var duplicates = new ArrayList<ScrollingEntity>();
|
||||
repository.findAllByAOrderById("D0").as(StepVerifier::create)
|
||||
.recordWith(() -> duplicates)
|
||||
.expectNextCount(2)
|
||||
.verifyComplete();
|
||||
var windowContainer = new AtomicReference<Window<ScrollingEntity>>();
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.keyset()))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
var window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0", "C0", "D0");
|
||||
|
||||
var windowContainer = new AtomicReference<Window<ScrollingEntity>>();
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.keyset())
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
var window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(0).getId()), Index.atIndex(3))
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0", "C0", "D0");
|
||||
ScrollPosition nextScrollPosition = window.positionAt(window.size() - 1);
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextScrollPosition))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("E0", "F0", "G0", "H0");
|
||||
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.satisfies(e -> assertThat(e.getId()).isEqualTo(duplicates.get(1).getId()), Index.atIndex(0))
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("D0", "E0", "F0", "G0");
|
||||
ScrollPosition nextNextScrollPosition = window.positionAt(window.size() - 1);
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextScrollPosition))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("I0");
|
||||
}
|
||||
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, window.positionAt(window.size() - 1))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("H0", "I0");
|
||||
}
|
||||
@Test
|
||||
@Order(5)
|
||||
@Tag("GH-2726")
|
||||
void backwardWithFluentQueryByExample(@Autowired ReactiveScrollingRepository repository) {
|
||||
|
||||
@Test
|
||||
void backwardWithDuplicatesManualIteration(@Autowired ReactiveScrollingRepository repository) {
|
||||
Example<ScrollingEntity> example = Example.of(new ScrollingEntity(), ExampleMatcher.matchingAll().withIgnoreNullValues());
|
||||
|
||||
// Recreate the last position
|
||||
var last = repository.findFirstByA("I0").block();
|
||||
var keys = Map.of(
|
||||
"foobar", Values.value(last.getA()),
|
||||
"b", Values.value(last.getB()),
|
||||
Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString())
|
||||
);
|
||||
// Recreate the last position
|
||||
var last = repository.findFirstByA("I0").block();
|
||||
var keys = Map.of(
|
||||
"c", Values.value(last.getC()),
|
||||
Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString())
|
||||
);
|
||||
|
||||
var duplicates = new ArrayList<ScrollingEntity>();
|
||||
repository.findAllByAOrderById("D0").as(StepVerifier::create)
|
||||
.recordWith(() -> duplicates)
|
||||
.expectNextCount(2)
|
||||
.verifyComplete();
|
||||
var windowContainer = new AtomicReference<Window<ScrollingEntity>>();
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.backward(keys)))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
var window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("F0", "G0", "H0", "I0");
|
||||
|
||||
var windowContainer = new AtomicReference<Window<ScrollingEntity>>();
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, ScrollPosition.backward(keys))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
var window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("F0", "G0", "H0", "I0");
|
||||
var nextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys());
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextPos))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("B0", "C0", "D0", "E0");
|
||||
|
||||
var pos = ((KeysetScrollPosition) window.positionAt(0));
|
||||
pos = ScrollPosition.backward(pos.getKeys());
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos)
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("C0", "D0", "D0", "E0");
|
||||
|
||||
pos = ((KeysetScrollPosition) window.positionAt(0));
|
||||
pos = ScrollPosition.backward(pos.getKeys());
|
||||
repository.findTop4By(ScrollingEntity.SORT_BY_B_AND_A, pos)
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("GH-2726")
|
||||
void forwardWithFluentQueryByExample(@Autowired ReactiveScrollingRepository repository) {
|
||||
ScrollingEntity scrollingEntity = new ScrollingEntity();
|
||||
Example<ScrollingEntity> example = Example.of(scrollingEntity, ExampleMatcher.matchingAll().withIgnoreNullValues());
|
||||
|
||||
var windowContainer = new AtomicReference<Window<ScrollingEntity>>();
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.keyset()))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
var window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0", "C0", "D0");
|
||||
|
||||
ScrollPosition nextScrollPosition = window.positionAt(window.size() - 1);
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextScrollPosition))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("D0", "E0", "F0", "G0");
|
||||
|
||||
ScrollPosition nextNextScrollPosition = window.positionAt(window.size() - 1);
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextScrollPosition))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("H0", "I0");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("GH-2726")
|
||||
void backwardWithFluentQueryByExample(@Autowired ReactiveScrollingRepository repository) {
|
||||
|
||||
Example<ScrollingEntity> example = Example.of(new ScrollingEntity(), ExampleMatcher.matchingAll().withIgnoreNullValues());
|
||||
|
||||
// Recreate the last position
|
||||
var last = repository.findFirstByA("I0").block();
|
||||
var keys = Map.of(
|
||||
"c", Values.value(last.getC()),
|
||||
Constants.NAME_OF_ADDITIONAL_SORT, Values.value(last.getId().toString())
|
||||
);
|
||||
|
||||
var windowContainer = new AtomicReference<Window<ScrollingEntity>>();
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(ScrollPosition.backward(keys)))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
var window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("F0", "G0", "H0", "I0");
|
||||
|
||||
var nextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys());
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextPos))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.hasNext()).isTrue();
|
||||
assertThat(window)
|
||||
.hasSize(4)
|
||||
.extracting(Function.identity())
|
||||
.extracting(ScrollingEntity::getA)
|
||||
.containsExactly("C0", "D0", "D0", "E0");
|
||||
|
||||
var nextNextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys());
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextPos))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0", "B0");
|
||||
var nextNextPos = ScrollPosition.backward(((KeysetScrollPosition) window.positionAt(0)).getKeys());
|
||||
repository.findBy(example, q -> q.sortBy(ScrollingEntity.SORT_BY_C).limit(4).scroll(nextNextPos))
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(windowContainer::set)
|
||||
.verifyComplete();
|
||||
window = windowContainer.get();
|
||||
assertThat(window.isLast()).isTrue();
|
||||
assertThat(window).extracting(ScrollingEntity::getA)
|
||||
.containsExactly("A0");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -40,7 +40,7 @@ public class ScrollingEntity {
|
||||
public static final Sort SORT_BY_C = Sort.by(Sort.Order.asc("c"));
|
||||
|
||||
public static void createTestData(QueryRunner queryRunner) {
|
||||
queryRunner.run("MATCH (n) DETACH DELETE n");
|
||||
queryRunner.run("MATCH (n) DETACH DELETE n").consume();
|
||||
queryRunner.run("""
|
||||
UNWIND (range(0, 8) + [3]) AS i WITH i, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' AS letters
|
||||
CREATE (n:ScrollingEntity {
|
||||
@@ -50,7 +50,21 @@ public class ScrollingEntity {
|
||||
c: (localdatetime() + duration({ days: i }) + duration({ seconds: i * toInteger(rand()*10) }))
|
||||
})
|
||||
RETURN n
|
||||
""");
|
||||
""").consume();
|
||||
}
|
||||
|
||||
public static void createTestDataWithoutDuplicates(QueryRunner queryRunner) {
|
||||
queryRunner.run("MATCH (n) DETACH DELETE n").consume();
|
||||
queryRunner.run("""
|
||||
UNWIND (range(0, 8)) AS i WITH i, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' AS letters
|
||||
CREATE (n:ScrollingEntity {
|
||||
id: randomUUID(),
|
||||
foobar: (substring(letters, (toInteger(i) % 26), 1) + (i / 26)),
|
||||
b: i,
|
||||
c: (localdatetime() + duration({ days: i }) + duration({ seconds: i * toInteger(rand()*10) }))
|
||||
})
|
||||
RETURN n
|
||||
""").consume();
|
||||
}
|
||||
|
||||
@Id
|
||||
|
||||
Reference in New Issue
Block a user