/*
* Copyright 2016 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.gemfire.search.lucene;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import org.apache.geode.cache.lucene.LuceneQueryProvider;
import org.apache.geode.cache.lucene.LuceneResultStruct;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.Region;
import org.springframework.data.projection.ProjectionFactory;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* Unit tests for {@link ProjectingLuceneTemplate}.
*
* @author John Blum
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.Spy
* @see org.mockito.junit.MockitoJUnitRunner
* @since 1.1.0
*/
@RunWith(MockitoJUnitRunner.class)
public class ProjectingLuceneTemplateUnitTests {
@Mock
private LuceneQueryProvider mockQueryProvider;
// SUT
private ProjectingLuceneTemplate luceneTemplate;
@Mock
private ProjectionFactory mockProjectionFactory;
@Before
public void setup() {
luceneTemplate = spy(new ProjectingLuceneTemplate());
luceneTemplate.setProjectionFactory(mockProjectionFactory);
}
protected LuceneResultStruct<Long, String> mockLuceneResultStruct(Book book) {
return mockLuceneResultStruct(book.getIsbn(), book.getTitle());
}
@SuppressWarnings("unchecked")
protected <K, V> LuceneResultStruct<K, V> mockLuceneResultStruct(K key, V value) {
LuceneResultStruct<K, V> mockLuceneResultStruct =
mock(LuceneResultStruct.class, String.format("MockLuceneResultStruct%s", key));
when(mockLuceneResultStruct.getValue()).thenReturn(value);
return mockLuceneResultStruct;
}
@Test
public void queryWithString() {
List<Book> books = asList(
Book.newBook(4L, "Star Wars - Episode IV New Hope"),
Book.newBook(5L, "Star Wars - Episode V Empire Strikes Back"),
Book.newBook(6L, "Star Wars - Episode VI Return of the Jedi")
);
List<LuceneResultStruct<Long, String>> queryResults = asList(
mockLuceneResultStruct(books.get(0)),
mockLuceneResultStruct(books.get(1)),
mockLuceneResultStruct(books.get(2))
);
doReturn(queryResults).when(luceneTemplate).query(anyString(), anyString(), anyInt());
when(mockProjectionFactory.createProjection(eq(Book.class), anyString())).thenAnswer( invocationOnMock ->
books.stream().filter(book ->
book.getTitle().equals(invocationOnMock.getArgument(1)))
.findFirst().orElse(null)
);
assertThat(luceneTemplate.query("title : Star Wars Episode *V*", "title", Book.class))
.containsAll(books);
verify(luceneTemplate, times(1))
.query(eq("title : Star Wars Episode *V*"), eq("title"),
eq(ProjectingLuceneTemplate.DEFAULT_RESULT_LIMIT), eq(Book.class));
verify(mockProjectionFactory, times(1))
.createProjection(eq(Book.class), eq(books.get(0).getTitle()));
verify(mockProjectionFactory, times(1))
.createProjection(eq(Book.class), eq(books.get(1).getTitle()));
verify(mockProjectionFactory, times(1))
.createProjection(eq(Book.class), eq(books.get(2).getTitle()));
}
@Test
public void queryWithQueryProvider() {
List<Book> books = asList(
Book.newBook(1L, "Star Wars - Episode I Phantom Menace"),
Book.newBook(2L, "Star Wars - Episode II Attack of the Clones"),
Book.newBook(3L, "Star Wars - Episode III Revenge of the Sith")
);
List<LuceneResultStruct<Long, String>> queryResults = asList(
mockLuceneResultStruct(books.get(0)),
mockLuceneResultStruct(books.get(1)),
mockLuceneResultStruct(books.get(2))
);
doReturn(queryResults).when(luceneTemplate).query(any(LuceneQueryProvider.class), anyInt());
when(mockProjectionFactory.createProjection(eq(Book.class), anyString())).thenAnswer( invocationOnMock ->
books.stream().filter(book ->
book.getTitle().equals(invocationOnMock.getArgument(1)))
.findFirst().orElse(null)
);
assertThat(luceneTemplate.query(mockQueryProvider, Book.class)).containsAll(books);
verify(luceneTemplate, times(1))
.query(eq(mockQueryProvider), eq(ProjectingLuceneTemplate.DEFAULT_RESULT_LIMIT), eq(Book.class));
verify(mockProjectionFactory, times(1))
.createProjection(eq(Book.class), eq(books.get(0).getTitle()));
verify(mockProjectionFactory, times(1))
.createProjection(eq(Book.class), eq(books.get(1).getTitle()));
verify(mockProjectionFactory, times(1))
.createProjection(eq(Book.class), eq(books.get(2).getTitle()));
}
@Data
@Region("Books")
@RequiredArgsConstructor(staticName = "newBook")
static class Book {
@NonNull @Id Long isbn;
@NonNull String title;
}
}