/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed 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
*
* http://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 org.springframework.data.mongodb.repository;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.observers.TestObserver;
import lombok.Data;
import lombok.NoArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import rx.Observable;
import rx.Single;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.ImportResource;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
import org.springframework.data.repository.reactive.RxJava1SortingRepository;
import org.springframework.data.repository.reactive.RxJava2SortingRepository;
import org.springframework.stereotype.Repository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Test for {@link ReactiveMongoRepository} using reactive wrapper type conversion.
*
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ConvertingReactiveMongoRepositoryTests.Config.class)
public class ConvertingReactiveMongoRepositoryTests {
@EnableReactiveMongoRepositories(includeFilters = @Filter(value = Repository.class),
considerNestedRepositories = true)
@ImportResource("classpath:reactive-infrastructure.xml")
static class Config {}
@Autowired MixedReactivePersonRepostitory reactiveRepository;
@Autowired ReactivePersonRepostitory reactivePersonRepostitory;
@Autowired RxJava1PersonRepostitory rxJava1PersonRepostitory;
@Autowired RxJava2PersonRepostitory rxJava2PersonRepostitory;
ReactivePerson dave, oliver, carter, boyd, stefan, leroi, alicia;
@Before
public void setUp() {
StepVerifier.create(reactiveRepository.deleteAll()).verifyComplete();
dave = new ReactivePerson("Dave", "Matthews", 42);
oliver = new ReactivePerson("Oliver August", "Matthews", 4);
carter = new ReactivePerson("Carter", "Beauford", 49);
boyd = new ReactivePerson("Boyd", "Tinsley", 45);
stefan = new ReactivePerson("Stefan", "Lessard", 34);
leroi = new ReactivePerson("Leroi", "Moore", 41);
alicia = new ReactivePerson("Alicia", "Keys", 30);
StepVerifier.create(reactiveRepository.saveAll(Arrays.asList(oliver, dave, carter, boyd, stefan, leroi, alicia))) //
.expectNextCount(7) //
.verifyComplete();
}
@Test // DATAMONGO-1444
public void reactiveStreamsMethodsShouldWork() {
StepVerifier.create(reactivePersonRepostitory.existsById(dave.getId())).expectNext(true).verifyComplete();
}
@Test // DATAMONGO-1444
public void reactiveStreamsQueryMethodsShouldWork() {
StepVerifier.create(reactivePersonRepostitory.findByLastname(boyd.getLastname())).expectNext(boyd).verifyComplete();
}
@Test // DATAMONGO-1444
public void simpleRxJava1MethodsShouldWork() throws Exception {
rxJava1PersonRepostitory.existsById(dave.getId()) //
.test() //
.awaitTerminalEvent() //
.assertValue(true) //
.assertNoErrors() //
.assertCompleted();
}
@Test // DATAMONGO-1444
public void existsWithSingleRxJava1IdMethodsShouldWork() throws Exception {
rxJava1PersonRepostitory.existsById(Single.just(dave.getId())) //
.test() //
.awaitTerminalEvent() //
.assertValue(true) //
.assertNoErrors() //
.assertCompleted();
}
@Test // DATAMONGO-1444
public void singleRxJava1QueryMethodShouldWork() throws Exception {
rxJava1PersonRepostitory.findByFirstnameAndLastname(dave.getFirstname(), dave.getLastname()) //
.test() //
.awaitTerminalEvent() //
.assertValue(dave) //
.assertNoErrors() //
.assertCompleted();
}
@Test // DATAMONGO-1444
public void singleProjectedRxJava1QueryMethodShouldWork() throws Exception {
List<ProjectedPerson> people = rxJava1PersonRepostitory.findProjectedByLastname(carter.getLastname()) //
.test() //
.awaitTerminalEvent() //
.assertValueCount(1) //
.assertNoErrors() //
.assertCompleted() //
.getOnNextEvents();
ProjectedPerson projectedPerson = people.get(0);
assertThat(projectedPerson.getFirstname(), is(equalTo(carter.getFirstname())));
}
@Test // DATAMONGO-1444
public void observableRxJava1QueryMethodShouldWork() throws Exception {
rxJava1PersonRepostitory.findByLastname(boyd.getLastname()) //
.test() //
.awaitTerminalEvent() //
.assertValue(boyd) //
.assertNoErrors() //
.assertCompleted() //
.getOnNextEvents();
}
@Test // DATAMONGO-1610
public void simpleRxJava2MethodsShouldWork() throws Exception {
TestObserver<Boolean> testObserver = rxJava2PersonRepostitory.existsById(dave.getId()).test();
testObserver.awaitTerminalEvent();
testObserver.assertComplete();
testObserver.assertNoErrors();
testObserver.assertValue(true);
}
@Test // DATAMONGO-1610
public void existsWithSingleRxJava2IdMethodsShouldWork() throws Exception {
TestObserver<Boolean> testObserver = rxJava2PersonRepostitory.existsById(io.reactivex.Single.just(dave.getId()))
.test();
testObserver.awaitTerminalEvent();
testObserver.assertComplete();
testObserver.assertNoErrors();
testObserver.assertValue(true);
}
@Test // DATAMONGO-1610
public void flowableRxJava2QueryMethodShouldWork() throws Exception {
io.reactivex.subscribers.TestSubscriber<ReactivePerson> testSubscriber = rxJava2PersonRepostitory
.findByFirstnameAndLastname(dave.getFirstname(), dave.getLastname()).test();
testSubscriber.awaitTerminalEvent();
testSubscriber.assertComplete();
testSubscriber.assertNoErrors();
testSubscriber.assertValue(dave);
}
@Test // DATAMONGO-1610
public void singleProjectedRxJava2QueryMethodShouldWork() throws Exception {
TestObserver<ProjectedPerson> testObserver = rxJava2PersonRepostitory
.findProjectedByLastname(Maybe.just(carter.getLastname())).test();
testObserver.awaitTerminalEvent();
testObserver.assertComplete();
testObserver.assertNoErrors();
testObserver.assertValue(actual -> {
assertThat(actual.getFirstname(), is(equalTo(carter.getFirstname())));
return true;
});
}
@Test // DATAMONGO-1610
public void observableProjectedRxJava2QueryMethodShouldWork() throws Exception {
TestObserver<ProjectedPerson> testObserver = rxJava2PersonRepostitory
.findProjectedByLastname(Single.just(carter.getLastname())).test();
testObserver.awaitTerminalEvent();
testObserver.assertComplete();
testObserver.assertNoErrors();
testObserver.assertValue(actual -> {
assertThat(actual.getFirstname(), is(equalTo(carter.getFirstname())));
return true;
});
}
@Test // DATAMONGO-1610
public void maybeRxJava2QueryMethodShouldWork() throws Exception {
TestObserver<ReactivePerson> testObserver = rxJava2PersonRepostitory.findByLastname(boyd.getLastname()).test();
testObserver.awaitTerminalEvent();
testObserver.assertComplete();
testObserver.assertNoErrors();
testObserver.assertValue(boyd);
}
@Test // DATAMONGO-1444
public void mixedRepositoryShouldWork() {
reactiveRepository.findByLastname(boyd.getLastname()) //
.test() //
.awaitTerminalEvent() //
.assertValue(boyd) //
.assertNoErrors() //
.assertCompleted() //
.getOnNextEvents();
}
@Test // DATAMONGO-1444
public void shouldFindOneBySingleOfLastName() {
StepVerifier.create(reactiveRepository.findByLastname(Single.just(carter.getLastname()))) //
.expectNext(carter) //
.verifyComplete();
}
@Test // DATAMONGO-1444
public void shouldFindByObservableOfLastNameIn() {
StepVerifier.create(reactiveRepository.findByLastnameIn(Observable.just(carter.getLastname(), dave.getLastname()))) //
.expectNextCount(3) //
.verifyComplete();
}
@Test // DATAMONGO-1444
public void shouldFindByPublisherOfLastNameInAndAgeGreater() {
List<ReactivePerson> people = reactiveRepository
.findByLastnameInAndAgeGreaterThan(Flux.just(carter.getLastname(), dave.getLastname()), 41).test() //
.awaitTerminalEvent() //
.assertValueCount(2) //
.assertNoErrors() //
.assertCompleted() //
.getOnNextEvents();
assertThat(people, hasItems(carter, dave));
}
@Repository
interface ReactivePersonRepostitory extends ReactiveSortingRepository<ReactivePerson, String> {
Publisher<ReactivePerson> findByLastname(String lastname);
}
@Repository
interface RxJava1PersonRepostitory extends RxJava1SortingRepository<ReactivePerson, String> {
Observable<ReactivePerson> findByFirstnameAndLastname(String firstname, String lastname);
Single<ReactivePerson> findByLastname(String lastname);
Single<ProjectedPerson> findProjectedByLastname(String lastname);
}
@Repository
interface RxJava2PersonRepostitory extends RxJava2SortingRepository<ReactivePerson, String> {
Flowable<ReactivePerson> findByFirstnameAndLastname(String firstname, String lastname);
Maybe<ReactivePerson> findByLastname(String lastname);
io.reactivex.Single<ProjectedPerson> findProjectedByLastname(Maybe<String> lastname);
io.reactivex.Observable<ProjectedPerson> findProjectedByLastname(Single<String> lastname);
}
@Repository
interface MixedReactivePersonRepostitory extends ReactiveMongoRepository<ReactivePerson, String> {
Single<ReactivePerson> findByLastname(String lastname);
Mono<ReactivePerson> findByLastname(Single<String> lastname);
Flux<ReactivePerson> findByLastnameIn(Observable<String> lastname);
Flux<ReactivePerson> findByLastname(String lastname, Sort sort);
Observable<ReactivePerson> findByLastnameInAndAgeGreaterThan(Flux<String> lastname, int age);
}
@Document
@Data
@NoArgsConstructor
static class ReactivePerson {
@Id String id;
String firstname;
String lastname;
int age;
public ReactivePerson(String firstname, String lastname, int age) {
this.firstname = firstname;
this.lastname = lastname;
this.age = age;
}
}
interface ProjectedPerson {
String getId();
String getFirstname();
}
}