@Test
public void testSet() {
    redisManager.set(testKey, testValue);
    redisManager.set(testKey, testValue, 0);
    verify(jedis, times(0)).expire(testKey, 0);
    redisManager.set(testKey, testValue, 700);
    verify(jedis, times(1)).expire(testKey, 700);
    redisManager.set(null, null);
}

@Test
public void testSet() {
    redisManager.set(testKey, testValue);
    redisManager.set(null, null);
}

@Override
public byte[] set(byte[] key, byte[] value, int expire) {
    if (key == null) {
        return null;
    }
    Jedis jedis = getJedis();
    try {
        jedis.set(key, value);
        if (expire != 0) {
            jedis.expire(key, expire);
        }
    } finally {
        jedis.close();
    }
    return value;
}


@Test
public void testGetCache() {
    Cache cache = redisCacheManager.getCache("testCache1");
    Cache cache1 = redisCacheManager.getCache("testCache1");
    assertThat(cache, is(cache1));
    redisCacheManager.setKeyPrefix("testRedisManager1:");
    Cache cache2 = redisCacheManager.getCache("testCache2");
    assertThat(cache2.getClass().getName(), is("org.crazycake.shiro.RedisCache"));
    RedisCache redisCache2 = (RedisCache) cache2;
    assertThat(redisCache2.getKeyPrefix(), is("testRedisManager1:testCache2:"));
}

@Test
public void testGetCache() {
    Cache cache = redisCacheManager.getCache("testCache1");
    Cache cache1 = redisCacheManager.getCache("testCache1");
    assertThat(cache, is(cache1));
    redisCacheManager.setKeyPrefix("testRedisManager1");
    Cache cache2 = redisCacheManager.getCache("testCache2");
    assertThat(cache2.getClass().getName(), is("org.crazycake.shiro.RedisCache"));
    RedisCache redisCache2 = (RedisCache) cache2;
    assertThat(redisCache2.getKeyPrefix(), is("testRedisManager1"));
}

@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
    logger.debug("get cache, name=" + name);
    Cache cache = caches.get(name);
    if (cache == null) {
        cache = new RedisCache<K, V>(redisManager, keySerializer, valueSerializer, keyPrefix + name + ":");
        caches.put(name, cache);
    }
    return cache;
}

@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
    logger.debug("get cache, name=" + name);
    Cache cache = caches.get(name);
    if (cache == null) {
        cache = new RedisCache<K, V>(redisManager, keySerializer, valueSerializer, keyPrefix);
        caches.put(name, cache);
    }
    return cache;
}


@Test
public void testGetCache() {
    Cache cache = redisCacheManager.getCache("testCache1");
    Cache cache1 = redisCacheManager.getCache("testCache1");
    assertThat(cache, is(cache1));
    redisCacheManager.setKeyPrefix("testRedisManager1");
    Cache cache2 = redisCacheManager.getCache("testCache2");
    assertThat(cache2.getClass().getName(), is("org.crazycake.shiro.RedisCache"));
    RedisCache redisCache2 = (RedisCache) cache2;
    assertThat(redisCache2.getKeyPrefix(), is("testRedisManager1"));
}

@Test
public void testGetCache() {
    Cache cache = redisCacheManager.getCache("testCache1");
    Cache cache1 = redisCacheManager.getCache("testCache1");
    assertThat(cache, is(cache1));
    redisCacheManager.setKeyPrefix("testRedisManager1:");
    Cache cache2 = redisCacheManager.getCache("testCache2");
    assertThat(cache2.getClass().getName(), is("org.crazycake.shiro.RedisCache"));
    RedisCache redisCache2 = (RedisCache) cache2;
    assertThat(redisCache2.getKeyPrefix(), is("testRedisManager1:testCache2:"));
}

@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
    logger.debug("get cache, name=" + name);
    Cache cache = caches.get(name);
    if (cache == null) {
        cache = new RedisCache<K, V>(redisManager, keySerializer, valueSerializer, keyPrefix);
        caches.put(name, cache);
    }
    return cache;
}

@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
    logger.debug("get cache, name=" + name);
    Cache cache = caches.get(name);
    if (cache == null) {
        cache = new RedisCache<K, V>(redisManager, keySerializer, valueSerializer, keyPrefix + name + ":");
        caches.put(name, cache);
    }
    return cache;
}


@Test
public void testAdvisor() throws Exception {
    WorldService worldService = new WorldServiceImpl();
    String expression = "execution(* org.springframework.test.service.WorldService.explode(..))";
    AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
    advisor.setExpression(expression);
    MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(new WorldServiceBeforeAdvice());
    advisor.setAdvice(methodInterceptor);
    ClassFilter classFilter = advisor.getPointcut().getClassFilter();
    if (classFilter.matches(worldService.getClass())) {
        ProxyFactory proxyFactory = new ProxyFactory();
        TargetSource targetSource = new TargetSource(worldService);
        proxyFactory.setTargetSource(targetSource);
        proxyFactory.addAdvisor(advisor);
        WorldService proxy = (WorldService) proxyFactory.getProxy();
        proxy.explode();
    }
}

@Test
public void testAdvisor() throws Exception {
    WorldService worldService = new WorldServiceImpl();
    String expression = "execution(* org.springframework.test.service.WorldService.explode(..))";
    AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
    advisor.setExpression(expression);
    MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(new WorldServiceBeforeAdvice());
    advisor.setAdvice(methodInterceptor);
    ClassFilter classFilter = advisor.getPointcut().getClassFilter();
    if (classFilter.matches(worldService.getClass())) {
        AdvisedSupport advisedSupport = new AdvisedSupport();
        TargetSource targetSource = new TargetSource(worldService);
        advisedSupport.setTargetSource(targetSource);
        advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
        advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
        WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
        proxy.explode();
    }
}

public void addAdvisor(Advisor advisor) {
    advisors.add(advisor);
}

@Test
public void testBeforeAdvice() throws Exception {
    String expression = "execution(* org.springframework.test.service.WorldService.explode(..))";
    AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
    advisor.setExpression(expression);
    MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(new WorldServiceBeforeAdvice());
    advisor.setAdvice(methodInterceptor);
    advisedSupport.addAdvisor(advisor);
    ProxyFactory factory = (ProxyFactory) advisedSupport;
    WorldService proxy = (WorldService) factory.getProxy();
    proxy.explode();
}

@Test
public void testBeforeAdvice() throws Exception {
    WorldServiceBeforeAdvice beforeAdvice = new WorldServiceBeforeAdvice();
    MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(beforeAdvice);
    advisedSupport.setMethodInterceptor(methodInterceptor);
    WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
    proxy.explode();
}

@Test
public void testProxyFactory() throws Exception {
    ProxyFactory factory = (ProxyFactory) advisedSupport;
    factory.setProxyTargetClass(false);
    WorldService proxy = (WorldService) factory.getProxy();
    proxy.explode();
    factory.setProxyTargetClass(true);
    proxy = (WorldService) factory.getProxy();
    proxy.explode();
}

@Test
public void testProxyFactory() throws Exception {
    advisedSupport.setProxyTargetClass(false);
    WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
    proxy.explode();
    advisedSupport.setProxyTargetClass(true);
    proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
    proxy.explode();
}

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object target = advised.getTargetSource().getTarget();
    Class<?> targetClass = target.getClass();
    Object retVal = null;
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    CglibMethodInvocation methodInvocation = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy);
    if (chain == null || chain.isEmpty()) {
        retVal = methodProxy.invoke(target, args);
    } else {
        retVal = methodInvocation.proceed();
    }
    return retVal;
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy);
    if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
        return advised.getMethodInterceptor().invoke(methodInvocation);
    }
    return methodInvocation.proceed();
}


@Test
void throwExceptionWhenTrustStorePathIsAbsentFromSystemProperty() {
    Map<String, String> properties = new HashMap<>();
    properties.put("javax.net.ssl.trustStorePassword", new String(TRUSTSTORE_PASSWORD));
    properties.put("javax.net.ssl.trustStoreType", "PKCS12");
    properties.forEach(System::setProperty);
    SSLFactory.Builder sslFactoryBuilder = SSLFactory.builder();
    assertThatThrownBy(sslFactoryBuilder::withSystemPropertyDerivedTrustMaterial).isInstanceOf(GenericKeyStoreException.class).hasMessageContaining("TrustStore details are empty, which are required to be present when SSL/TLS is enabled");
    properties.forEach((propertyName, propertyValue) -> System.clearProperty(propertyName));
}

@Test
void throwExceptionWhenTrustStorePathIsAbsentFromSystemProperty() {
    Map<String, String> properties = new HashMap<>();
    properties.put("javax.net.ssl.trustStorePassword", new String(TRUSTSTORE_PASSWORD));
    properties.put("javax.net.ssl.trustStoreType", "PKCS12");
    properties.forEach(System::setProperty);
    SSLFactory.Builder sslFactoryBuilder = SSLFactory.builder();
    assertThatThrownBy(sslFactoryBuilder::withSystemPropertyDerivedTrustMaterial).isInstanceOf(GenericKeyStoreException.class).hasMessageContaining("The value for the system property [javax.net.ssl.trustStore] is absent");
    properties.forEach((propertyName, propertyValue) -> System.clearProperty(propertyName));
}

public Builder withSystemPropertyDerivedTrustMaterial() {
    return withSystemPropertyDerivedMaterial("javax.net.ssl.trustStore", "javax.net.ssl.trustStorePassword", "javax.net.ssl.trustStoreType", "javax.net.ssl.trustStoreProvider", this::withTrustMaterial);
}

public Builder withSystemPropertyDerivedTrustMaterial() {
    KeyStore trustStore = KeyStoreUtils.loadSystemPropertyDerivedTrustStore();
    return withTrustMaterial(trustStore);
}


@Test
void returnNoBooksIfNoBooksInCustomShelf() {
    Set<Book> actual = customShelfUtils.getBooksInCustomShelf(customShelfWithNoBooks.getShelfName());
    assertThat(actual).isEmpty();
}

@Test
void returnNoBooksIfNoBooksInCustomShelf() {
    String customShelfWithoutBooks = customShelfWithNoBooks.getShelfName();
    Set<Book> actual = customShelfService.getBooksInCustomShelf(customShelfWithoutBooks);
    assertThat(actual).isEmpty();
}

public Set<Book> getBooksInCustomShelf(String shelfName) {
    CustomShelf customShelf = customShelfService.findByShelfNameAndLoggedInUser(shelfName);
    if (customShelf == null) {
        return new HashSet<>();
    }
    return customShelf.getBooks();
}

@Test
void returnNoBooksForNonExistentCustomShelf() {
    assertThat(customShelfUtils.getBooksInCustomShelf("InvalidShelf")).isEmpty();
}

@Test
void returnNoBooksForNonExistentCustomShelf() {
    String nonExistentShelf = "InvalidShelf";
    Set<Book> actual = customShelfService.getBooksInCustomShelf(nonExistentShelf);
    assertThat(actual).isEmpty();
}

@Test
void returnBooksSuccessfully() {
    Set<Book> actual = customShelfUtils.getBooksInCustomShelf(customShelf1.getShelfName());
    booksInCustomShelf1.forEach(book -> assertThat(actual).contains(book));
}

@Test
void returnBooksSuccessfully() {
    Set<Book> actual = customShelfService.getBooksInCustomShelf(customShelf1.getShelfName());
    booksInCustomShelf1.forEach(book -> assertThat(actual).contains(book));
}


@Test
void getAllPredefinedShelfNamesFromDatabase() {
    List<String> expectedShelfNames = List.of(TO_READ.toString(), READING.toString(), READ.toString(), DID_NOT_FINISH.toString());
    List<String> shelfNames = predefinedShelfUtils.getPredefinedShelfNamesAsStrings();
    assertThat(shelfNames).isEqualTo(expectedShelfNames);
}

@Test
void getAllPredefinedShelfNamesFromDatabase() {
    List<String> expectedShelfNames = List.of(TO_READ.toString(), READING.toString(), READ.toString(), DID_NOT_FINISH.toString());
    List<String> shelfNames = predefinedShelfService.getPredefinedShelfNamesAsStrings();
    assertThat(shelfNames).isEqualTo(expectedShelfNames);
}

public List<String> getPredefinedShelfNamesAsStrings() {
    return predefinedShelfService.findAllForLoggedInUser().stream().map(Shelf::getShelfName).collect(Collectors.toList());
}

@Test
void getBooksInOneChosenShelf() {
    Set<Book> expectedBooks = Set.of(book1, book2);
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInChosenPredefinedShelf("To read");
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

@Test
void getBooksInOneChosenShelf() {
    Set<Book> expectedBooks = Set.of(book1, book2);
    String shelf = TO_READ.toString();
    Set<Book> actualBooks = predefinedShelfService.getBooksInChosenPredefinedShelf(shelf);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

public Set<Book> getBooksInChosenPredefinedShelf(String chosenShelf) {
    Set<Book> books;
    if (isAllBooksShelf(chosenShelf)) {
        return getBooksInAllPredefinedShelves();
    }
    PredefinedShelf.ShelfName predefinedShelfName = getPredefinedShelfName(chosenShelf);
    PredefinedShelf predefinedShelf = predefinedShelfService.findByPredefinedShelfNameAndLoggedInUser(predefinedShelfName);
    if (predefinedShelf == null) {
        books = new HashSet<>();
    } else {
        books = predefinedShelf.getBooks();
    }
    return books;
}

@Test
void getAllBooksWhenChosenShelfIsAllShelves() {
    Set<Book> expectedBooks = Set.of(book1, book2, book3, book4);
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInChosenPredefinedShelf(ALL_BOOKS_SHELF);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

@Test
void getAllBooksWhenChosenShelfIsAllShelves() {
    Set<Book> expectedBooks = Set.of(book1, book2, book3, book4);
    Set<Book> actualBooks = predefinedShelfService.getBooksInChosenPredefinedShelf(ALL_BOOKS_SHELF);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}


@Test
void updateValuesInDatabaseForExistingBookWhenSaveEventIsCalled() {
    String newTitle = "IT";
    Author newAuthor = new Author("Stephen", "King");
    BookGenre newBookGenre = BookGenre.HORROR;
    bookForm = createBookForm(TO_READ, false);
    bookForm.addListener(BookForm.SaveEvent.class, event -> bookService.save(event.getBook()));
    bookForm.saveButton.click();
    Book savedBook = bookService.findAll().get(0);
    populateBookFormWithExistingBook(READ, savedBook);
    bookForm.bookTitle.setValue(newTitle);
    bookForm.authorFirstName.setValue(newAuthor.getFirstName());
    bookForm.authorLastName.setValue(newAuthor.getLastName());
    bookForm.bookGenre.setValue(newBookGenre);
    bookForm.saveButton.click();
    Book updatedBook = bookService.findAll().get(0);
    assertSoftly(softly -> {
        softly.assertThat(bookService.count()).isOne();
        softly.assertThat(newTitle).isEqualTo(updatedBook.getTitle());
        softly.assertThat(READ).isEqualTo(updatedBook.getPredefinedShelf().getPredefinedShelfName());
        softly.assertThat((newAuthor.getFirstName())).isEqualTo(updatedBook.getAuthor().getFirstName());
        softly.assertThat(newAuthor.getLastName()).isEqualTo(updatedBook.getAuthor().getLastName());
        softly.assertThat(newBookGenre).isEqualTo(updatedBook.getBookGenre());
        softly.assertThat(dateStarted).isEqualTo(updatedBook.getDateStartedReading());
        softly.assertThat(dateFinished).isEqualTo(updatedBook.getDateFinishedReading());
    });
}

@Test
void updateValuesInDatabaseForExistingBookWhenSaveEventIsCalled() {
    String newTitle = "IT";
    Author newAuthor = new Author("Stephen", "King");
    BookGenre newBookGenre = BookGenre.HORROR;
    bookForm = createBookForm(TO_READ, false);
    bookForm.addListener(BookForm.SaveEvent.class, event -> bookService.save(event.getBook()));
    bookForm.saveButton.click();
    Book savedBook = bookService.findAll().get(0);
    populateBookFormWithExistingBook(READ, savedBook);
    bookForm.bookTitle.getField().setValue(newTitle);
    bookForm.authorFirstName.setValue(newAuthor.getFirstName());
    bookForm.authorLastName.setValue(newAuthor.getLastName());
    bookForm.bookGenre.setValue(newBookGenre);
    bookForm.saveButton.click();
    Book updatedBook = bookService.findAll().get(0);
    assertSoftly(softly -> {
        softly.assertThat(bookService.count()).isOne();
        softly.assertThat(newTitle).isEqualTo(updatedBook.getTitle());
        softly.assertThat(READ).isEqualTo(updatedBook.getPredefinedShelf().getPredefinedShelfName());
        softly.assertThat((newAuthor.getFirstName())).isEqualTo(updatedBook.getAuthor().getFirstName());
        softly.assertThat(newAuthor.getLastName()).isEqualTo(updatedBook.getAuthor().getLastName());
        softly.assertThat(newBookGenre).isEqualTo(updatedBook.getBookGenre());
        softly.assertThat(dateStarted).isEqualTo(updatedBook.getDateStartedReading());
        softly.assertThat(dateFinished).isEqualTo(updatedBook.getDateFinishedReading());
    });
}

public T getField() {
    return field;
}

@Test
void notAllowEmptyBookTitle() {
    bookForm.bookTitle.setValue("");
    bookForm.saveButton.click();
    assertErrorShown(BookFormErrors.BOOK_TITLE_ERROR);
}

@Test
void notAllowEmptyBookTitle() {
    bookForm.bookTitle.getField().setValue("");
    bookForm.saveButton.click();
    assertErrorShown(BookFormErrors.BOOK_TITLE_ERROR);
}


@Test
void updateValuesInDatabaseForExistingBookWhenSaveEventIsCalled() {
    String newTitle = "IT";
    Author newAuthor = new Author("Stephen", "King");
    BookGenre newBookGenre = BookGenre.HORROR;
    bookForm = createBookForm(TO_READ, false);
    bookForm.addListener(BookForm.SaveEvent.class, event -> bookService.save(event.getBook()));
    bookForm.saveButton.click();
    Book savedBook = bookService.findAll().get(0);
    populateBookFormWithExistingBook(READ, savedBook);
    bookForm.bookTitle.getField().setValue(newTitle);
    bookForm.authorFirstName.getField().setValue(newAuthor.getFirstName());
    bookForm.authorLastName.getField().setValue(newAuthor.getLastName());
    bookForm.bookGenre.setValue(newBookGenre);
    bookForm.saveButton.click();
    Book updatedBook = bookService.findAll().get(0);
    assertSoftly(softly -> {
        softly.assertThat(bookService.count()).isOne();
        softly.assertThat(newTitle).isEqualTo(updatedBook.getTitle());
        softly.assertThat(READ).isEqualTo(updatedBook.getPredefinedShelf().getPredefinedShelfName());
        softly.assertThat((newAuthor.getFirstName())).isEqualTo(updatedBook.getAuthor().getFirstName());
        softly.assertThat(newAuthor.getLastName()).isEqualTo(updatedBook.getAuthor().getLastName());
        softly.assertThat(newBookGenre).isEqualTo(updatedBook.getBookGenre());
        softly.assertThat(dateStarted).isEqualTo(updatedBook.getDateStartedReading());
        softly.assertThat(dateFinished).isEqualTo(updatedBook.getDateFinishedReading());
    });
}

@Test
void notAllowEmptyAuthorLastName() {
    bookForm.authorLastName.setValue("");
    bookForm.saveButton.click();
    assertErrorShown(BookFormErrors.LAST_NAME_ERROR);
}

@Test
void notAllowEmptyAuthorLastName() {
    bookForm.authorLastName.getField().setValue("");
    bookForm.saveButton.click();
    assertErrorShown(BookFormErrors.LAST_NAME_ERROR);
}

@Test
void saveChangedAuthorName() {
    Book book = createBook(READ, false, "Title");
    bookService.save(book);
    bookForm.setBook(book);
    AtomicReference<Book> bookReference = new AtomicReference<>(null);
    bookForm.addListener(BookForm.SaveEvent.class, event -> bookReference.set(event.getBook()));
    bookForm.authorFirstName.setValue("James");
    bookForm.authorLastName.setValue("Dean");
    bookForm.saveButton.click();
    Book savedBook = bookReference.get();
    assertThat(savedBook.getId()).isEqualTo(book.getId());
    assertThat(savedBook.getAuthor().getFirstName()).isEqualTo("James");
    assertThat(savedBook.getAuthor().getLastName()).isEqualTo("Dean");
}

@Test
void saveChangedAuthorName() {
    Book book = createBook(READ, false, "Title");
    bookService.save(book);
    bookForm.setBook(book);
    AtomicReference<Book> bookReference = new AtomicReference<>(null);
    bookForm.addListener(BookForm.SaveEvent.class, event -> bookReference.set(event.getBook()));
    bookForm.authorFirstName.getField().setValue("James");
    bookForm.authorLastName.getField().setValue("Dean");
    bookForm.saveButton.click();
    Book savedBook = bookReference.get();
    assertThat(savedBook.getId()).isEqualTo(book.getId());
    assertThat(savedBook.getAuthor().getFirstName()).isEqualTo("James");
    assertThat(savedBook.getAuthor().getLastName()).isEqualTo("Dean");
}


@Test
void updateValuesInDatabaseForExistingBookWhenSaveEventIsCalled() {
    String newTitle = "IT";
    Author newAuthor = new Author("Stephen", "King");
    BookGenre newBookGenre = BookGenre.HORROR;
    bookForm = createBookForm(TO_READ, false);
    bookForm.addListener(BookForm.SaveEvent.class, event -> bookService.save(event.getBook()));
    bookForm.saveButton.click();
    Book savedBook = bookService.findAll().get(0);
    populateBookFormWithExistingBook(READ, savedBook);
    bookForm.bookTitle.getField().setValue(newTitle);
    bookForm.authorFirstName.getField().setValue(newAuthor.getFirstName());
    bookForm.authorLastName.getField().setValue(newAuthor.getLastName());
    bookForm.bookGenre.getField().setValue(newBookGenre);
    bookForm.saveButton.click();
    Book updatedBook = bookService.findAll().get(0);
    assertSoftly(softly -> {
        softly.assertThat(bookService.count()).isOne();
        softly.assertThat(newTitle).isEqualTo(updatedBook.getTitle());
        softly.assertThat(READ).isEqualTo(updatedBook.getPredefinedShelf().getPredefinedShelfName());
        softly.assertThat((newAuthor.getFirstName())).isEqualTo(updatedBook.getAuthor().getFirstName());
        softly.assertThat(newAuthor.getLastName()).isEqualTo(updatedBook.getAuthor().getLastName());
        softly.assertThat(newBookGenre).isEqualTo(updatedBook.getBookGenre());
        softly.assertThat(dateStarted).isEqualTo(updatedBook.getDateStartedReading());
        softly.assertThat(dateFinished).isEqualTo(updatedBook.getDateFinishedReading());
    });
}


@Test
void updateValuesInDatabaseForExistingBookWhenSaveEventIsCalled() {
    String newTitle = "IT";
    Author newAuthor = new Author("Stephen", "King");
    BookGenre newBookGenre = BookGenre.HORROR;
    bookForm = createBookForm(TO_READ, false);
    bookForm.addListener(BookForm.SaveEvent.class, event -> bookService.save(event.getBook()));
    bookForm.saveButton.click();
    Book savedBook = bookService.findAll().get(0);
    populateBookFormWithExistingBook(READ, savedBook);
    bookForm.bookTitle.getField().setValue(newTitle);
    bookForm.authorFirstName.getField().setValue(newAuthor.getFirstName());
    bookForm.authorLastName.setValue(newAuthor.getLastName());
    bookForm.bookGenre.setValue(newBookGenre);
    bookForm.saveButton.click();
    Book updatedBook = bookService.findAll().get(0);
    assertSoftly(softly -> {
        softly.assertThat(bookService.count()).isOne();
        softly.assertThat(newTitle).isEqualTo(updatedBook.getTitle());
        softly.assertThat(READ).isEqualTo(updatedBook.getPredefinedShelf().getPredefinedShelfName());
        softly.assertThat((newAuthor.getFirstName())).isEqualTo(updatedBook.getAuthor().getFirstName());
        softly.assertThat(newAuthor.getLastName()).isEqualTo(updatedBook.getAuthor().getLastName());
        softly.assertThat(newBookGenre).isEqualTo(updatedBook.getBookGenre());
        softly.assertThat(dateStarted).isEqualTo(updatedBook.getDateStartedReading());
        softly.assertThat(dateFinished).isEqualTo(updatedBook.getDateFinishedReading());
    });
}

@Test
void saveChangedAuthorName() {
    Book book = createBook(READ, false, "Title");
    bookService.save(book);
    bookForm.setBook(book);
    AtomicReference<Book> bookReference = new AtomicReference<>(null);
    bookForm.addListener(BookForm.SaveEvent.class, event -> bookReference.set(event.getBook()));
    bookForm.authorFirstName.getField().setValue("James");
    bookForm.authorLastName.setValue("Dean");
    bookForm.saveButton.click();
    Book savedBook = bookReference.get();
    assertThat(savedBook.getId()).isEqualTo(book.getId());
    assertThat(savedBook.getAuthor().getFirstName()).isEqualTo("James");
    assertThat(savedBook.getAuthor().getLastName()).isEqualTo("Dean");
}

@Test
void notAllowEmptyAuthorFirstName() {
    bookForm.authorFirstName.setValue("");
    bookForm.saveButton.click();
    assertErrorShown(BookFormErrors.FIRST_NAME_ERROR);
}

@Test
void notAllowEmptyAuthorFirstName() {
    bookForm.authorFirstName.getField().setValue("");
    bookForm.saveButton.click();
    assertErrorShown(BookFormErrors.FIRST_NAME_ERROR);
}


@Test
void overwriteExistingGoalOnSave() {
    assumeThat(goalService.count()).isOne();
    ReadingGoal newReadingGoal = new ReadingGoal(40, ReadingGoal.GoalType.PAGES);
    goalService.save(newReadingGoal);
    ReadingGoal actual = goalService.findAll().get(0);
    assertSoftly(softly -> {
        softly.assertThat(goalService.count()).isOne();
        softly.assertThat(newReadingGoal).isEqualToComparingFieldByField(actual);
    });
}

@Test
void overwriteExistingGoalOnSave() {
    assumeThat(goalService.count()).isOne();
    ReadingGoal newReadingGoal = new ReadingGoal(40, ReadingGoal.GoalType.PAGES);
    goalService.save(newReadingGoal);
    assertThat(goalService.count()).isOne();
    ReadingGoal actual = goalService.findAll().get(0);
    assertThat(newReadingGoal).isEqualToComparingFieldByField(actual);
}

public void save(@NonNull ReadingGoal goal) {
    overwritePreviouslySetGoal(goal);
}

public void save(ReadingGoal goal) {
    if (goal != null) {
        overwritePreviouslySetGoals(goal);
    }
}


@Test
void returnNoBooksIfNoBooksInCustomShelf() {
    String customShelfWithoutBooks = customShelfWithNoBooks.getShelfName();
    Set<Book> actual = customShelfService.getBooksInCustomShelf(customShelfWithoutBooks);
    assertThat(actual).isEmpty();
}

@Test
void returnNoBooksIfNoBooksInCustomShelf() {
    Set<Book> actual = customShelfUtils.getBooksInCustomShelf(customShelfWithNoBooks.getShelfName());
    assertThat(actual).isEmpty();
}

public Set<Book> getBooksInCustomShelf(String shelfName) {
    CustomShelf customShelf = customShelfService.findByShelfNameAndLoggedInUser(shelfName);
    if (customShelf == null) {
        return new HashSet<>();
    }
    return customShelf.getBooks();
}

@Test
void returnNoBooksForNonExistentCustomShelf() {
    String nonExistentShelf = "InvalidShelf";
    Set<Book> actual = customShelfService.getBooksInCustomShelf(nonExistentShelf);
    assertThat(actual).isEmpty();
}

@Test
void returnNoBooksForNonExistentCustomShelf() {
    assertThat(customShelfUtils.getBooksInCustomShelf("InvalidShelf")).isEmpty();
}

@Test
void returnBooksSuccessfully() {
    Set<Book> actual = customShelfService.getBooksInCustomShelf(customShelf1.getShelfName());
    booksInCustomShelf1.forEach(book -> assertThat(actual).contains(book));
}

@Test
void returnBooksSuccessfully() {
    Set<Book> actual = customShelfUtils.getBooksInCustomShelf(customShelf1.getShelfName());
    booksInCustomShelf1.forEach(book -> assertThat(actual).contains(book));
}


@Test
void getAllPredefinedShelfNamesFromDatabase() {
    List<String> expectedShelfNames = List.of(TO_READ.toString(), READING.toString(), READ.toString(), DID_NOT_FINISH.toString());
    List<String> shelfNames = predefinedShelfService.getPredefinedShelfNamesAsStrings();
    assertThat(shelfNames).isEqualTo(expectedShelfNames);
}

@Test
void getAllPredefinedShelfNamesFromDatabase() {
    List<String> expectedShelfNames = List.of(TO_READ.toString(), READING.toString(), READ.toString(), DID_NOT_FINISH.toString());
    List<String> shelfNames = predefinedShelfUtils.getPredefinedShelfNamesAsStrings();
    assertThat(shelfNames).isEqualTo(expectedShelfNames);
}

public List<String> getPredefinedShelfNamesAsStrings() {
    return predefinedShelfService.findAllForLoggedInUser().stream().map(Shelf::getShelfName).collect(Collectors.toList());
}

@Test
void getAllBooksInChosenShelves() {
    List<PredefinedShelf> predefinedShelves = List.of(toReadShelf, readShelf);
    Set<Book> expectedBooks = Set.of(book1, book2, book3);
    Set<Book> actualBooks = getBooksInPredefinedShelves(predefinedShelves);
    assertSoftly(softly -> {
        softly.assertThat(actualBooks).hasSize(expectedBooks.size());
        softly.assertThat(actualBooks).containsAll(expectedBooks);
    });
}

@Test
void getAllBooksInChosenShelves() {
    List<PredefinedShelf> predefinedShelves = List.of(toReadShelf, readShelf);
    Set<Book> expectedBooks = Set.of(book1, book2, book3);
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInPredefinedShelves(predefinedShelves);
    assertSoftly(softly -> {
        softly.assertThat(actualBooks).hasSize(expectedBooks.size());
        softly.assertThat(actualBooks).containsAll(expectedBooks);
    });
}

public static Set<Book> getBooksInPredefinedShelves(List<PredefinedShelf> predefinedShelves) {
    return predefinedShelves.stream().map(PredefinedShelf::getBooks).collect(HashSet::new, Set::addAll, Set::addAll);
}

public Set<Book> getBooksInPredefinedShelves(List<PredefinedShelf> predefinedShelves) {
    return predefinedShelves.stream().map(PredefinedShelf::getBooks).collect(HashSet::new, Set::addAll, Set::addAll);
}

@Test
void getBooksInOneChosenShelf() {
    Set<Book> expectedBooks = Set.of(book1, book2);
    String shelf = TO_READ.toString();
    Set<Book> actualBooks = predefinedShelfService.getBooksInChosenPredefinedShelf(shelf);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

@Test
void getBooksInOneChosenShelf() {
    Set<Book> expectedBooks = Set.of(book1, book2);
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInChosenPredefinedShelf("To read");
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

public Set<Book> getBooksInChosenPredefinedShelf(String chosenShelf) {
    Set<Book> books;
    if (isAllBooksShelf(chosenShelf)) {
        return getBooksInAllPredefinedShelves();
    }
    PredefinedShelf.ShelfName predefinedShelfName = getPredefinedShelfName(chosenShelf);
    PredefinedShelf predefinedShelf = predefinedShelfService.findByPredefinedShelfNameAndLoggedInUser(predefinedShelfName);
    if (predefinedShelf == null) {
        books = new HashSet<>();
    } else {
        books = predefinedShelf.getBooks();
    }
    return books;
}

@Test
void getAllBooksWhenChosenShelfIsAllShelves() {
    Set<Book> expectedBooks = Set.of(book1, book2, book3, book4);
    Set<Book> actualBooks = predefinedShelfService.getBooksInChosenPredefinedShelf(ALL_BOOKS_SHELF);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

@Test
void getAllBooksWhenChosenShelfIsAllShelves() {
    Set<Book> expectedBooks = Set.of(book1, book2, book3, book4);
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInChosenPredefinedShelf(ALL_BOOKS_SHELF);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}


@Test
@Disabled
void beRemovedWhenOrphan() {
    Author orphan = new Author("Jostein", "Gardner");
    Book book = new Book("Sophie's World", orphan, toRead);
    bookService.delete(book);
    assertSoftly(softly -> {
        softly.assertThatThrownBy(() -> authorService.findById(orphan.getId())).isInstanceOf(RuntimeException.class);
        softly.assertThat(authorService.findAll()).isEmpty();
    });
}

@Test
void beRemovedWhenOrphan() {
    assumeThat(authorService.findAll()).isEmpty();
    Author orphan = new Author("Jostein", "Gaarder");
    Book book = new Book("Sophie's World", orphan, toRead);
    bookService.save(book);
    bookService.delete(book);
    assertThat(authorService.findAll()).isEmpty();
}

public void delete(Book book) {
    if (book == null) {
        LOGGER.log(Level.SEVERE, "Cannot delete an null book.");
        return;
    }
    LOGGER.log(Level.INFO, "Deleting book. Book repository size = " + bookRepository.count());
    bookRepository.delete(book);
    List<Book> books = bookRepository.findAll();
    if (books.contains(book)) {
        LOGGER.log(Level.SEVERE, book.getTitle() + " not deleted");
    } else {
        LOGGER.log(Level.INFO, book.getTitle() + " deleted. Book repository size = " + bookRepository.count());
    }
}

public void delete(@NonNull Book book) {
    LOGGER.log(Level.INFO, "Deleting book. Book repository size = " + bookRepository.count());
    bookRepository.delete(book);
    List<Book> books = bookRepository.findAll();
    if (books.contains(book)) {
        LOGGER.log(Level.SEVERE, book.getTitle() + " not deleted");
    } else {
        LOGGER.log(Level.INFO, book.getTitle() + " deleted. Book repository size = " + bookRepository.count());
        Author author = book.getAuthor();
        removeBookFromAuthor(book, author);
        removeAuthorWithoutBooks(author);
    }
}


@Test
void saveAndConfirmDuplicateNameWithDifferentId() {
    Author author = new Author("Nyor", "Ja");
    authorService.save(author);
    Author authorCopy = author;
    authorService.save(authorCopy);
    List<Author> savedAuthors = authorService.findAll();
    assertEquals(2, savedAuthors.size());
}

@Test
void saveAndConfirmDuplicateNameWithDifferentId() {
    Author author = new Author("Nyor", "Ja");
    authorService.save(author);
    Author authorCopy = new Author(author.getFirstName(), author.getLastName());
    authorService.save(authorCopy);
    List<Author> savedAuthors = authorService.findAll();
    int expected = 2;
    assertThat(savedAuthors.size()).isEqualTo(expected);
}

public void save(@NonNull Author author) {
    if (author.getId() != null) {
        Author existingAuthor = findById(author.getId());
        if (existingAuthor != null) {
            LOGGER.log(Level.INFO, "Matching authorIds: " + existingAuthor.getId());
            author = new Author(author.getFirstName(), author.getLastName());
        }
    }
    authorRepository.save(author);
}

public void save(@NonNull Author author) {
    authorRepository.save(author);
}

@Transactional
@Test
void saveCorrectly() {
    Author author = new Author("daks", "oten");
    authorService.save(author);
    Author existingAuthor = authorService.findById(author.getId());
    authorService.save(existingAuthor);
    assertEquals(2, authorService.count());
}

@Transactional
@Test
void saveCorrectly() {
    Author author = new Author("daks", "oten");
    authorService.save(author);
    Author existingAuthor = authorService.findById(author.getId());
    authorService.save(existingAuthor);
    assertThat(authorService.count()).isOne();
}

@Test
void savedAuthorCanBeFound() {
    Author author = new Author("First", "Last");
    authorService.save(author);
    assertNotNull(authorService.findById(author.getId()));
}

@Test
void savedAuthorCanBeFound() {
    Author author = new Author("First", "Last");
    authorService.save(author);
    Author actual = authorService.findById(author.getId());
    assertThat(actual).isNotNull();
}


@Test
void returnNoBooksIfNoBooksInCustomShelf() {
    Set<Book> actual = customShelfUtils.getBooksInCustomShelf(customShelfWithNoBooks.getShelfName());
    assertThat(actual).isEmpty();
}

@Test
void returnNoBooksIfNoBooksInCustomShelf() {
    String customShelfWithoutBooks = customShelfWithNoBooks.getShelfName();
    Set<Book> actual = customShelfService.getBooksInCustomShelf(customShelfWithoutBooks);
    assertThat(actual).isEmpty();
}

@Test
void returnNoBooksForNonExistentCustomShelf() {
    assertThat(customShelfUtils.getBooksInCustomShelf("InvalidShelf")).isEmpty();
}

@Test
void returnNoBooksForNonExistentCustomShelf() {
    String nonExistentShelf = "InvalidShelf";
    Set<Book> actual = customShelfService.getBooksInCustomShelf(nonExistentShelf);
    assertThat(actual).isEmpty();
}

@Test
void returnBooksSuccessfully() {
    Set<Book> actual = customShelfUtils.getBooksInCustomShelf(customShelf1.getShelfName());
    booksInCustomShelf1.forEach(book -> assertThat(actual).contains(book));
}

@Test
void returnBooksSuccessfully() {
    Set<Book> actual = customShelfService.getBooksInCustomShelf(customShelf1.getShelfName());
    booksInCustomShelf1.forEach(book -> assertThat(actual).contains(book));
}


@Test
void getAllPredefinedShelfNamesFromDatabase() {
    List<String> expectedShelfNames = List.of(TO_READ.toString(), READING.toString(), READ.toString(), DID_NOT_FINISH.toString());
    List<String> shelfNames = predefinedShelfUtils.getPredefinedShelfNamesAsStrings();
    assertThat(shelfNames).isEqualTo(expectedShelfNames);
}

@Test
void getAllPredefinedShelfNamesFromDatabase() {
    List<String> expectedShelfNames = List.of(TO_READ.toString(), READING.toString(), READ.toString(), DID_NOT_FINISH.toString());
    List<String> shelfNames = predefinedShelfService.getPredefinedShelfNamesAsStrings();
    assertThat(shelfNames).isEqualTo(expectedShelfNames);
}

@Test
void getBooksInOneChosenShelf() {
    Set<Book> expectedBooks = Set.of(book1, book2);
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInChosenPredefinedShelf("To read");
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

@Test
void getBooksInOneChosenShelf() {
    Set<Book> expectedBooks = Set.of(book1, book2);
    String shelf = TO_READ.toString();
    Set<Book> actualBooks = predefinedShelfService.getBooksInChosenPredefinedShelf(shelf);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

@Test
void getAllBooksWhenChosenShelfIsAllShelves() {
    Set<Book> expectedBooks = Set.of(book1, book2, book3, book4);
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInChosenPredefinedShelf(ALL_BOOKS_SHELF);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

@Test
void getAllBooksWhenChosenShelfIsAllShelves() {
    Set<Book> expectedBooks = Set.of(book1, book2, book3, book4);
    Set<Book> actualBooks = predefinedShelfService.getBooksInChosenPredefinedShelf(ALL_BOOKS_SHELF);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}


@Test
void returnNoBooksIfNoBooksInCustomShelf() {
    Set<Book> actual = customShelfService.getBooksInCustomShelf(customShelfWithNoBooks.getShelfName());
    assertThat(actual).isEmpty();
}

@Test
void returnNoBooksForNonExistentCustomShelf() {
    assertThat(customShelfService.getBooksInCustomShelf("InvalidShelf")).isEmpty();
}


@Transactional
@Test
void saveCorrectly() {
    Author author = new Author("daks", "oten");
    authorService.save(author);
    Author existingAuthor = authorService.findById(author.getId());
    authorService.save(existingAuthor);
    assertThat(authorService.count()).isOne();
}

@Transactional
@Test
void saveCorrectly() {
    Author author = new Author("daks", "oten");
    authorService.save(author);
    Author existingAuthor = authorService.findById(author.getId());
    authorService.save(existingAuthor);
    assertEquals(2, authorService.count());
}

public void save(@NonNull Author author) {
    authorRepository.save(author);
}

public void save(@NonNull Author author) {
    if (author.getId() != null) {
        Author existingAuthor = findById(author.getId());
        if (existingAuthor != null) {
            LOGGER.log(Level.INFO, "Matching authorIds: " + existingAuthor.getId());
            author = new Author(author.getFirstName(), author.getLastName());
        }
    }
    authorRepository.save(author);
}


@Test
@DisplayName("throw an exception on an attempt to save a null predefined shelf")
void notSaveNullPredefinedShelf() {
    assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> predefinedShelfService.save(null));
}

@Test
void notSaveNullPredefinedShelf() {
    long initialCount = predefinedShelfService.count();
    predefinedShelfService.save(null);
    assertThat(predefinedShelfService.count()).isEqualTo(initialCount);
}

public void save(@NonNull PredefinedShelf shelf) {
    predefinedShelfRepository.save(shelf);
}

public void save(PredefinedShelf shelf) {
    if (shelf != null) {
        LOGGER.log(Level.INFO, "Saving shelf: " + shelf);
        predefinedShelfRepository.save(shelf);
    } else {
        LOGGER.log(Level.SEVERE, "Null Shelf");
    }
}


@Test
void returnNoBooksIfNoBooksInCustomShelf() {
    Set<Book> actual = customShelfService.getBooksInCustomShelf(customShelfWithNoBooks.getShelfName());
    assertThat(actual).isEmpty();
}

@Test
void returnNoBooksIfNoBooksInCustomShelf() {
    Set<Book> actual = customShelfUtils.getBooksInCustomShelf(customShelfWithNoBooks.getShelfName());
    assertThat(actual).isEmpty();
}

@Test
void returnNoBooksForNonExistentCustomShelf() {
    assertThat(customShelfService.getBooksInCustomShelf("InvalidShelf")).isEmpty();
}

@Test
void returnNoBooksForNonExistentCustomShelf() {
    assertThat(customShelfUtils.getBooksInCustomShelf("InvalidShelf")).isEmpty();
}

@Test
void returnBooksSuccessfully() {
    Set<Book> actual = customShelfService.getBooksInCustomShelf(customShelf1.getShelfName());
    booksInCustomShelf1.forEach(book -> assertThat(actual).contains(book));
}

@Test
void returnBooksSuccessfully() {
    Set<Book> actual = customShelfUtils.getBooksInCustomShelf(customShelf1.getShelfName());
    booksInCustomShelf1.forEach(book -> assertThat(actual).contains(book));
}


@Test
void getBooksInOneChosenShelf() {
    Set<Book> expectedBooks = Set.of(book1, book2);
    String shelf = TO_READ.toString();
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInChosenPredefinedShelf(shelf);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}


@Test
void getAllPredefinedShelfNamesFromDatabase() {
    List<String> expectedShelfNames = List.of(TO_READ.toString(), READING.toString(), READ.toString(), DID_NOT_FINISH.toString());
    List<String> shelfNames = predefinedShelfService.getPredefinedShelfNamesAsStrings();
    assertThat(shelfNames).isEqualTo(expectedShelfNames);
}

@Test
void getAllPredefinedShelfNamesFromDatabase() {
    List<String> expectedShelfNames = List.of(TO_READ.toString(), READING.toString(), READ.toString(), DID_NOT_FINISH.toString());
    List<String> shelfNames = predefinedShelfUtils.getPredefinedShelfNamesAsStrings();
    assertThat(shelfNames).isEqualTo(expectedShelfNames);
}

@Test
void getAllBooksInChosenShelves() {
    List<PredefinedShelf> predefinedShelves = List.of(toReadShelf, readShelf);
    Set<Book> expectedBooks = Set.of(book1, book2, book3);
    Set<Book> actualBooks = getBooksInPredefinedShelves(predefinedShelves);
    assertSoftly(softly -> {
        softly.assertThat(actualBooks).hasSize(expectedBooks.size());
        softly.assertThat(actualBooks).containsAll(expectedBooks);
    });
}

@Test
void getAllBooksInChosenShelves() {
    List<PredefinedShelf> predefinedShelves = List.of(toReadShelf, readShelf);
    Set<Book> expectedBooks = Set.of(book1, book2, book3);
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInPredefinedShelves(predefinedShelves);
    assertSoftly(softly -> {
        softly.assertThat(actualBooks).hasSize(expectedBooks.size());
        softly.assertThat(actualBooks).containsAll(expectedBooks);
    });
}

@Test
void getBooksInOneChosenShelf() {
    Set<Book> expectedBooks = Set.of(book1, book2);
    String shelf = TO_READ.toString();
    Set<Book> actualBooks = predefinedShelfService.getBooksInChosenPredefinedShelf(shelf);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

@Test
void getBooksInOneChosenShelf() {
    Set<Book> expectedBooks = Set.of(book1, book2);
    String shelf = TO_READ.toString();
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInChosenPredefinedShelf(shelf);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

@Test
void getAllBooksWhenChosenShelfIsAllShelves() {
    Set<Book> expectedBooks = Set.of(book1, book2, book3, book4);
    Set<Book> actualBooks = predefinedShelfService.getBooksInChosenPredefinedShelf(ALL_BOOKS_SHELF);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}

@Test
void getAllBooksWhenChosenShelfIsAllShelves() {
    Set<Book> expectedBooks = Set.of(book1, book2, book3, book4);
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInChosenPredefinedShelf(ALL_BOOKS_SHELF);
    assertThat(actualBooks).isEqualTo(expectedBooks);
}


@Test
void overwriteExistingGoalOnSave() {
    assumeThat(goalService.count()).isOne();
    ReadingGoal newReadingGoal = new ReadingGoal(40, ReadingGoal.GoalType.PAGES);
    goalService.save(newReadingGoal);
    ReadingGoal actual = goalService.findAll().get(0);
    assertSoftly(softly -> {
        softly.assertThat(goalService.count()).isOne();
        softly.assertThat(newReadingGoal).isEqualToComparingFieldByField(actual);
    });
}

@Test
void overwriteExistingGoalOnSave() {
    assumeThat(goalService.count()).isOne();
    ReadingGoal newReadingGoal = new ReadingGoal(40, ReadingGoal.GoalType.PAGES);
    goalService.save(newReadingGoal);
    assertThat(goalService.count()).isOne();
    ReadingGoal actual = goalService.findAll().get(0);
    assertThat(newReadingGoal).isEqualToComparingFieldByField(actual);
}


@Test
void returnNoBooksIfNoBooksInCustomShelf() {
    String customShelfWithoutBooks = customShelfWithNoBooks.getShelfName();
    Set<Book> actual = customShelfService.getBooksInCustomShelf(customShelfWithoutBooks);
    assertThat(actual).isEmpty();
}

@Test
void returnNoBooksForNonExistentCustomShelf() {
    String nonExistentShelf = "InvalidShelf";
    Set<Book> actual = customShelfService.getBooksInCustomShelf(nonExistentShelf);
    assertThat(actual).isEmpty();
}


@Test
void getBooksInOneChosenShelf() {
    Set<Book> expectedBooks = Set.of(book1, book2);
    Set<Book> actualBooks = predefinedShelfUtils.getBooksInChosenPredefinedShelf("To read");
    assertThat(actualBooks).isEqualTo(expectedBooks);
}


@Test
public void testReadTtFromFieldFromSimpleDateColumn() {
    BitemporalMapper mapper = bitemporalMapper("vtFrom", "vtTo", "ttFrom", "ttTo").pattern("timestamp").build("field");
    Columns columns = new Columns();
    columns.add(Column.builder("vtFrom").buildWithComposed(0, SimpleDateType.instance));
    columns.add(Column.builder("vtTo").buildWithComposed(0, SimpleDateType.instance));
    columns.add(Column.builder("ttFrom").buildWithComposed(5, SimpleDateType.instance));
    columns.add(Column.builder("ttTo").buildWithComposed(0, SimpleDateType.instance));
    assertEquals("Date parsing is wrong", new BitemporalDateTime(5L * 24L * 60L * 60L * 1000L), mapper.readBitemporalDate(columns, "ttFrom"));
}

@Test
public void testReadTtFromFieldFromSimpleDateColumn() {
    BitemporalMapper mapper = bitemporalMapper("vtFrom", "vtTo", "ttFrom", "ttTo").pattern("timestamp").build("field");
    Columns columns = new Columns();
    ByteBuffer bb = SimpleDateType.instance.fromString("2015-10-10");
    columns.add(Column.builder("ttFrom").buildWithDecomposed(bb, SimpleDateType.instance));
    assertEquals("Date parsing is wrong", new BitemporalDateTime(1444435200000L), mapper.readBitemporalDate(columns, "ttFrom"));
}

BitemporalDateTime readBitemporalDate(Columns columns, String fieldName) {
    Column<?> column = columns.getColumnsByFullName(fieldName).getFirst();
    if (column == null) {
        return null;
    }
    return parseBitemporalDate(column.getComposedValue());
}

BitemporalDateTime readBitemporalDate(Columns columns, String fieldName) {
    Column<?> column = columns.getColumnsByFullName(fieldName).getFirst();
    if (column == null) {
        return null;
    }
    return parseBitemporalDate(column);
}

@Test
public void testReadVtFromFieldFromSimpleDateColumn() {
    BitemporalMapper mapper = bitemporalMapper("vtFrom", "vtTo", "ttFrom", "ttTo").pattern("timestamp").build("field");
    Columns columns = new Columns();
    columns.add(Column.builder("vtFrom").buildWithComposed(5, SimpleDateType.instance));
    columns.add(Column.builder("vtTo").buildWithComposed(0, SimpleDateType.instance));
    columns.add(Column.builder("ttFrom").buildWithComposed(0, SimpleDateType.instance));
    columns.add(Column.builder("ttTo").buildWithComposed(0, SimpleDateType.instance));
    assertEquals("Date parsing is wrong", new BitemporalDateTime(5L * 24L * 60L * 60L * 1000L), mapper.readBitemporalDate(columns, "vtFrom"));
}

@Test
public void testReadVtFromFieldFromSimpleDateColumn() {
    BitemporalMapper mapper = bitemporalMapper("vtFrom", "vtTo", "ttFrom", "ttTo").pattern("timestamp").build("field");
    Columns columns = new Columns();
    ByteBuffer bb = SimpleDateType.instance.fromString("2015-10-10");
    columns.add(Column.builder("vtFrom").buildWithDecomposed(bb, SimpleDateType.instance));
    assertEquals("Date parsing is wrong", new BitemporalDateTime(1444435200000L), mapper.readBitemporalDate(columns, "vtFrom"));
}

@Test
public void testReadVtToFieldFromSimpleDateColumn() {
    BitemporalMapper mapper = bitemporalMapper("vtFrom", "vtTo", "ttFrom", "ttTo").pattern("timestamp").build("field");
    Columns columns = new Columns();
    columns.add(Column.builder("vtFrom").buildWithComposed(0, SimpleDateType.instance));
    columns.add(Column.builder("vtTo").buildWithComposed(5, SimpleDateType.instance));
    columns.add(Column.builder("ttFrom").buildWithComposed(0, SimpleDateType.instance));
    columns.add(Column.builder("ttTo").buildWithComposed(0, SimpleDateType.instance));
    assertEquals("Date parsing is wrong", new BitemporalDateTime(5L * 24L * 60L * 60L * 1000L), mapper.readBitemporalDate(columns, "vtTo"));
}

@Test
public void testReadVtToFieldFromSimpleDateColumn() {
    BitemporalMapper mapper = bitemporalMapper("vtFrom", "vtTo", "ttFrom", "ttTo").pattern("timestamp").build("field");
    Columns columns = new Columns();
    ByteBuffer bb = SimpleDateType.instance.fromString("2015-10-10");
    columns.add(Column.builder("vtTo").buildWithDecomposed(bb, SimpleDateType.instance));
    assertEquals("Date parsing is wrong", new BitemporalDateTime(1444435200000L), mapper.readBitemporalDate(columns, "vtTo"));
}

@Test
public void testReadTtToFieldFromSimpleDateColumn() {
    BitemporalMapper mapper = bitemporalMapper("vtFrom", "vtTo", "ttFrom", "ttTo").pattern("timestamp").build("field");
    Columns columns = new Columns();
    columns.add(Column.builder("vtFrom").buildWithComposed(0, SimpleDateType.instance));
    columns.add(Column.builder("vtTo").buildWithComposed(0, SimpleDateType.instance));
    columns.add(Column.builder("ttFrom").buildWithComposed(0, SimpleDateType.instance));
    columns.add(Column.builder("ttTo").buildWithComposed(5, SimpleDateType.instance));
    assertEquals("Date parsing is wrong", new BitemporalDateTime(5L * 24L * 60L * 60L * 1000L), mapper.readBitemporalDate(columns, "ttTo"));
}

@Test
public void testReadTtToFieldFromSimpleDateColumn() {
    BitemporalMapper mapper = bitemporalMapper("vtFrom", "vtTo", "ttFrom", "ttTo").pattern("timestamp").build("field");
    Columns columns = new Columns();
    ByteBuffer bb = SimpleDateType.instance.fromString("2015-10-10");
    columns.add(Column.builder("ttTo").buildWithDecomposed(bb, SimpleDateType.instance));
    assertEquals("Date parsing is wrong", new BitemporalDateTime(1444435200000L), mapper.readBitemporalDate(columns, "ttTo"));
}


@Test
public void testQuery() {
    AllCondition condition = new AllCondition(0.7f);
    Schema schema = schema().build();
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", MatchAllDocsQuery.class, query.getClass());
    assertEquals("Query boost is wrong", 0.7f, query.getBoost(), 0);
}

@Test
public void testQuery() {
    AllCondition condition = new AllCondition(0.7f);
    Schema schema = schema().build();
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", MatchAllDocsQuery.class, query.getClass());
}

@Override
public Query query(Schema schema) {
    Query query = new MatchAllDocsQuery();
    query.setBoost(boost);
    return query;
}


@Test
public void testQuery() {
    MapperBuilder<?, ?> mapperBuilder = bitemporalMapper("vtFrom", "vtTo", "ttFrom", "ttTo").pattern(TIMESTAMP_PATTERN);
    Schema schema = schema().mapper("name", mapperBuilder).build();
    BitemporalCondition condition = new BitemporalCondition(0.5f, "name", 1, 2, 3, 4);
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertTrue("Query type is wrong", query instanceof BooleanQuery);
}

@Test
public void testQuery() {
    MapperBuilder<?, ?> mapperBuilder = bitemporalMapper("vtFrom", "vtTo", "ttFrom", "ttTo").pattern(TIMESTAMP_PATTERN);
    Schema schema = schema().mapper("name", mapperBuilder).build();
    BitemporalCondition condition = new BitemporalCondition(0.5f, "name", 1, 2, 3, 4);
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertTrue("Query type is wrong", query instanceof BooleanQuery);
}

@Override
@SuppressWarnings("unchecked")
public Query query(Schema schema) {
    Mapper mapper = schema.getMapper(field);
    if (mapper == null) {
        throw new IndexException("No mapper found for field '%s'", field);
    } else if (!type.isAssignableFrom(mapper.getClass())) {
        throw new IndexException("Field '%s' requires a mapper of type '%s' but found '%s'", field, type, mapper);
    }
    return query((T) mapper, schema.getAnalyzer());
}


@Test
public void testQueryPureNot() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    BooleanCondition condition = bool().not(match("name", "jonathan")).boost(0.4).build();
    BooleanQuery query = (BooleanQuery) condition.query(schema);
    assertEquals("Query count clauses is wrong", 2, query.clauses().size());
    assertEquals("Query boost is wrong", 0.4f, query.getBoost(), 0f);
}

@Test
public void testQueryPureNot() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    BooleanCondition condition = bool().not(match("name", "jonathan")).boost(0.4).build();
    BooleanQuery query = (BooleanQuery) condition.doQuery(schema);
    assertEquals("Query count clauses is wrong", 2, query.clauses().size());
}

@Override
public Query query(Schema schema) {
    BooleanQuery.Builder builder = new BooleanQuery.Builder();
    for (Condition condition : must) {
        builder.add(condition.query(schema), MUST);
    }
    for (Condition condition : should) {
        builder.add(condition.query(schema), SHOULD);
    }
    for (Condition condition : not) {
        builder.add(condition.query(schema), MUST_NOT);
    }
    if (must.isEmpty() && should.isEmpty() && !not.isEmpty()) {
        logger.warn("Performing resource-intensive pure negation search");
        builder.add(new MatchAllDocsQuery(), FILTER);
    }
    Query query = builder.build();
    query.setBoost(boost);
    return query;
}

@Test
public void testQueryEmpty() {
    Schema schema = schema().build();
    BooleanCondition condition = bool().boost(0.4).build();
    BooleanQuery query = (BooleanQuery) condition.query(schema);
    assertEquals("Query count clauses is wrong", 0, query.clauses().size());
    assertEquals("Query boost is wrong", 0.4f, query.getBoost(), 0f);
}

@Test
public void testQueryEmpty() {
    Schema schema = schema().build();
    BooleanCondition condition = bool().boost(0.4).build();
    BooleanQuery query = (BooleanQuery) condition.doQuery(schema);
    assertEquals("Query count clauses is wrong", 0, query.clauses().size());
}

@Test
public void testToString() {
    BooleanCondition condition = bool().must(match("name", "jonathan"), match("age", 18)).should(match("color", "green")).not(match("country", "england")).boost(0.5f).build();
    assertEquals("Method #toString is wrong", "BooleanCondition{boost=0.5, " + "must=[MatchCondition{boost=1.0, field=name, value=jonathan}, " + "MatchCondition{boost=1.0, field=age, value=18}], " + "should=[MatchCondition{boost=1.0, field=color, value=green}], " + "not=[MatchCondition{boost=1.0, field=country, value=england}]}", condition.toString());
}

@Test
public void testToString() {
    BooleanCondition condition = bool().must(match("name", "jonathan"), match("age", 18)).should(match("color", "green")).not(match("country", "england").boost(0.7)).boost(0.5f).build();
    assertEquals("Method #toString is wrong", "BooleanCondition{boost=0.5, " + "must=[MatchCondition{boost=null, field=name, value=jonathan}, " + "MatchCondition{boost=null, field=age, value=18}], " + "should=[MatchCondition{boost=null, field=color, value=green}], " + "not=[MatchCondition{boost=0.7, field=country, value=england}]}", condition.toString());
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this).add("boost", boost).add("must", must).add("should", should).add("not", not).toString();
}

@Test
public void testQuery() {
    Schema schema = schema().mapper("name", stringMapper()).mapper("color", stringMapper()).mapper("country", stringMapper()).mapper("age", integerMapper()).defaultAnalyzer("default").build();
    BooleanCondition condition = bool().must(match("name", "jonathan"), range("age").lower(18).includeLower(true)).should(match("color", "green"), match("color", "blue")).not(match("country", "england")).boost(0.4f).build();
    BooleanQuery query = (BooleanQuery) condition.query(schema);
    assertEquals("Query count clauses is wrong", 5, query.clauses().size());
    assertEquals("Query boost is wrong", 0.4f, query.getBoost(), 0f);
}

@Test
public void testQuery() {
    Schema schema = schema().mapper("name", stringMapper()).mapper("color", stringMapper()).mapper("country", stringMapper()).mapper("age", integerMapper()).defaultAnalyzer("default").build();
    BooleanCondition condition = bool().must(match("name", "jonathan"), range("age").lower(18).includeLower(true)).should(match("color", "green"), match("color", "blue")).not(match("country", "england")).boost(0.4f).build();
    BooleanQuery query = (BooleanQuery) condition.doQuery(schema);
    assertEquals("Query count clauses is wrong", 5, query.clauses().size());
}


@Test
public void testQueryString() {
    Float boost = 0.7f;
    Object[] values = new Object[] { "houses", "cats" };
    Schema schema = schema().mapper("name", stringMapper()).build();
    ContainsCondition condition = new ContainsCondition(boost, "name", values);
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", BooleanQuery.class, query.getClass());
    BooleanQuery booleanQuery = (BooleanQuery) query;
    assertEquals("Query boost is wrong", 0.7f, query.getBoost(), 0);
    List<BooleanClause> clauses = booleanQuery.clauses();
    TermQuery termQuery1 = (TermQuery) clauses.get(0).getQuery();
    TermQuery termQuery2 = (TermQuery) clauses.get(1).getQuery();
    assertEquals("Query is wrong", "houses", termQuery1.getTerm().bytes().utf8ToString());
    assertEquals("Query is wrong", "cats", termQuery2.getTerm().bytes().utf8ToString());
}

@Test
public void testQueryString() {
    Float boost = 0.7f;
    Object[] values = new Object[] { "houses", "cats" };
    Schema schema = schema().mapper("name", stringMapper()).build();
    ContainsCondition condition = new ContainsCondition(boost, "name", values);
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", BooleanQuery.class, query.getClass());
    BooleanQuery booleanQuery = (BooleanQuery) query;
    List<BooleanClause> clauses = booleanQuery.clauses();
    TermQuery termQuery1 = (TermQuery) clauses.get(0).getQuery();
    TermQuery termQuery2 = (TermQuery) clauses.get(1).getQuery();
    assertEquals("Query is wrong", "houses", termQuery1.getTerm().bytes().utf8ToString());
    assertEquals("Query is wrong", "cats", termQuery2.getTerm().bytes().utf8ToString());
}

@Override
@SuppressWarnings("unchecked")
public final Query query(Schema schema) {
    Mapper mapper = schema.getMapper(field);
    if (mapper == null) {
        throw new IndexException("No mapper found for field '%s'", field);
    } else if (!SingleColumnMapper.class.isAssignableFrom(mapper.getClass())) {
        throw new IndexException("Field '%s' requires a mapper of type '%s' but found '%s'", field, SingleColumnMapper.class.getSimpleName(), mapper);
    }
    return query((SingleColumnMapper<?>) mapper, schema.getAnalyzer());
}

@Test
public void testQueryText() {
    Float boost = 0.7f;
    Object[] values = new Object[] { "houses", "cats" };
    Schema schema = schema().mapper("name", textMapper()).defaultAnalyzer("english").build();
    ContainsCondition condition = new ContainsCondition(boost, "name", values);
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", BooleanQuery.class, query.getClass());
    BooleanQuery booleanQuery = (BooleanQuery) query;
    assertEquals("Query boost is wrong", 0.7f, query.getBoost(), 0);
    List<BooleanClause> clauses = booleanQuery.clauses();
    TermQuery termQuery1 = (TermQuery) clauses.get(0).getQuery();
    TermQuery termQuery2 = (TermQuery) clauses.get(1).getQuery();
    assertEquals("Query is wrong", "hous", termQuery1.getTerm().bytes().utf8ToString());
    assertEquals("Query is wrong", "cat", termQuery2.getTerm().bytes().utf8ToString());
}

@Test
public void testQueryText() {
    Float boost = 0.7f;
    Object[] values = new Object[] { "houses", "cats" };
    Schema schema = schema().mapper("name", textMapper()).defaultAnalyzer("english").build();
    ContainsCondition condition = new ContainsCondition(boost, "name", values);
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", BooleanQuery.class, query.getClass());
    BooleanQuery booleanQuery = (BooleanQuery) query;
    List<BooleanClause> clauses = booleanQuery.clauses();
    TermQuery termQuery1 = (TermQuery) clauses.get(0).getQuery();
    TermQuery termQuery2 = (TermQuery) clauses.get(1).getQuery();
    assertEquals("Query is wrong", "hous", termQuery1.getTerm().bytes().utf8ToString());
    assertEquals("Query is wrong", "cat", termQuery2.getTerm().bytes().utf8ToString());
}

@Test
public void testQueryNumeric() {
    Float boost = 0.7f;
    Object[] values = new Object[] { 1, 2, 3 };
    Schema schema = schema().mapper("name", integerMapper()).build();
    ContainsCondition condition = new ContainsCondition(boost, "name", values);
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", BooleanQuery.class, query.getClass());
    BooleanQuery booleanQuery = (BooleanQuery) query;
    assertEquals("Boost is not set", 0.7f, query.getBoost(), 0);
    List<BooleanClause> clauses = booleanQuery.clauses();
    assertEquals("Query is wrong", values.length, clauses.size());
    for (int i = 0; i < values.length; i++) {
        NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) clauses.get(i).getQuery();
        assertEquals("Query is wrong", values[i], numericRangeQuery.getMin());
        assertEquals("Query is wrong", values[i], numericRangeQuery.getMax());
    }
}

@Test
public void testQueryNumeric() {
    Float boost = 0.7f;
    Object[] values = new Object[] { 1, 2, 3 };
    Schema schema = schema().mapper("name", integerMapper()).build();
    ContainsCondition condition = new ContainsCondition(boost, "name", values);
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", BooleanQuery.class, query.getClass());
    BooleanQuery booleanQuery = (BooleanQuery) query;
    List<BooleanClause> clauses = booleanQuery.clauses();
    assertEquals("Query is wrong", values.length, clauses.size());
    for (int i = 0; i < values.length; i++) {
        NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) clauses.get(i).getQuery();
        assertEquals("Query is wrong", values[i], numericRangeQuery.getMin());
        assertEquals("Query is wrong", values[i], numericRangeQuery.getMax());
    }
}


@Test
public void testQuery() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    FuzzyCondition condition = new FuzzyCondition(0.5f, "name", "tr", 1, 2, 49, true);
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", FuzzyQuery.class, query.getClass());
    FuzzyQuery fuzzyQuery = (FuzzyQuery) query;
    assertEquals("Query field is wrong", "name", fuzzyQuery.getField());
    assertEquals("Query term is wrong", "tr", fuzzyQuery.getTerm().text());
    assertEquals("Query max edits is wrong", 1, fuzzyQuery.getMaxEdits());
    assertEquals("Query prefix length is wrong", 2, fuzzyQuery.getPrefixLength());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testQuery() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    FuzzyCondition condition = new FuzzyCondition(0.5f, "name", "tr", 1, 2, 49, true);
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", FuzzyQuery.class, query.getClass());
    FuzzyQuery fuzzyQuery = (FuzzyQuery) query;
    assertEquals("Query field is wrong", "name", fuzzyQuery.getField());
    assertEquals("Query term is wrong", "tr", fuzzyQuery.getTerm().text());
    assertEquals("Query max edits is wrong", 1, fuzzyQuery.getMaxEdits());
    assertEquals("Query prefix length is wrong", 2, fuzzyQuery.getPrefixLength());
}


@Test
public void testQuery() {
    Schema schema = schema().mapper("name", geoPointMapper("lat", "lon").maxLevels(8)).build();
    GeoBBoxCondition condition = new GeoBBoxCondition(0.5f, "name", -90D, 90D, -180D, 180D);
    Query query = condition.query(schema);
    assertNotNull("Query is wrong is not built", query);
    assertTrue("Query type is wrong", query instanceof ConstantScoreQuery);
    query = ((ConstantScoreQuery) query).getQuery();
    assertTrue("Query type is wrong", query instanceof BooleanQuery);
}

@Test
public void testQuery() {
    Schema schema = schema().mapper("name", geoPointMapper("lat", "lon").maxLevels(8)).build();
    GeoBBoxCondition condition = new GeoBBoxCondition(0.5f, "name", -90D, 90D, -180D, 180D);
    Query query = condition.doQuery(schema);
    assertNotNull("Query is wrong is not built", query);
    assertTrue("Query type is wrong", query instanceof ConstantScoreQuery);
    query = ((ConstantScoreQuery) query).getQuery();
    assertTrue("Query type is wrong", query instanceof BooleanQuery);
}


@Test
public void testQueryMinMax() {
    Schema schema = schema().mapper("name", geoPointMapper("lat", "lon").maxLevels(8)).build();
    GeoDistanceCondition condition = new GeoDistanceCondition(0.5f, "name", 90D, -180D, GeoDistance.parse("1km"), GeoDistance.parse("3km"));
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertTrue("Query type is wrong", query instanceof BooleanQuery);
    BooleanQuery booleanQuery = (BooleanQuery) query;
    assertEquals("Query num clauses is wrong", 2, booleanQuery.clauses().size());
    BooleanClause minClause = booleanQuery.clauses().get(1);
    assertEquals("Query is wrong", BooleanClause.Occur.MUST_NOT, minClause.getOccur());
    query = minClause.getQuery();
    assertEquals("Query is wrong", IntersectsPrefixTreeQuery.class, query.getClass());
    IntersectsPrefixTreeQuery minFilter = (IntersectsPrefixTreeQuery) query;
    assertEquals("Query is wrong", "IntersectsPrefixTreeQuery(fieldName=name.dist,queryShape=Circle(Pt(x=-180.0,y=90.0), " + "d=0.0° 1.00km),detailLevel=8,prefixGridScanLevel=4)", minFilter.toString());
    BooleanClause maxClause = booleanQuery.clauses().get(0);
    assertEquals("Query is wrong", BooleanClause.Occur.FILTER, maxClause.getOccur());
    query = maxClause.getQuery();
    assertEquals("Query type is wrong", IntersectsPrefixTreeQuery.class, query.getClass());
    IntersectsPrefixTreeQuery maxFilter = (IntersectsPrefixTreeQuery) query;
    assertEquals("Query is wrong", "IntersectsPrefixTreeQuery(fieldName=name.dist,queryShape=Circle(Pt(x=-180.0,y=90.0), " + "d=0.0° 3.00km),detailLevel=8,prefixGridScanLevel=4)", maxFilter.toString());
}

@Test
public void testQueryMinMax() {
    Schema schema = schema().mapper("name", geoPointMapper("lat", "lon").maxLevels(8)).build();
    GeoDistanceCondition condition = new GeoDistanceCondition(0.5f, "name", 90D, -180D, GeoDistance.parse("1km"), GeoDistance.parse("3km"));
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertTrue("Query type is wrong", query instanceof BooleanQuery);
    BooleanQuery booleanQuery = (BooleanQuery) query;
    assertEquals("Query num clauses is wrong", 2, booleanQuery.clauses().size());
    BooleanClause minClause = booleanQuery.clauses().get(1);
    assertEquals("Query is wrong", BooleanClause.Occur.MUST_NOT, minClause.getOccur());
    query = minClause.getQuery();
    assertEquals("Query is wrong", IntersectsPrefixTreeQuery.class, query.getClass());
    IntersectsPrefixTreeQuery minFilter = (IntersectsPrefixTreeQuery) query;
    assertEquals("Query is wrong", "IntersectsPrefixTreeQuery(fieldName=name.dist,queryShape=Circle(Pt(x=-180.0,y=90.0), " + "d=0.0° 1.00km),detailLevel=8,prefixGridScanLevel=4)", minFilter.toString());
    BooleanClause maxClause = booleanQuery.clauses().get(0);
    assertEquals("Query is wrong", BooleanClause.Occur.FILTER, maxClause.getOccur());
    query = maxClause.getQuery();
    assertEquals("Query type is wrong", IntersectsPrefixTreeQuery.class, query.getClass());
    IntersectsPrefixTreeQuery maxFilter = (IntersectsPrefixTreeQuery) query;
    assertEquals("Query is wrong", "IntersectsPrefixTreeQuery(fieldName=name.dist,queryShape=Circle(Pt(x=-180.0,y=90.0), " + "d=0.0° 3.00km),detailLevel=8,prefixGridScanLevel=4)", maxFilter.toString());
}

@Test
public void testQueryMax() {
    Schema schema = schema().mapper("name", geoPointMapper("lat", "lon").maxLevels(8)).build();
    GeoDistanceCondition condition = new GeoDistanceCondition(0.5f, "name", 90D, -180D, null, GeoDistance.parse("10hm"));
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    BooleanQuery booleanQuery = (BooleanQuery) query;
    assertEquals("Query num clauses is wrong", 1, booleanQuery.clauses().size());
    BooleanClause maxClause = booleanQuery.clauses().get(0);
    assertEquals("Query occur is wrong", BooleanClause.Occur.FILTER, maxClause.getOccur());
    query = maxClause.getQuery();
    assertEquals("Query type is wrong", IntersectsPrefixTreeQuery.class, query.getClass());
    IntersectsPrefixTreeQuery filter = (IntersectsPrefixTreeQuery) query;
    assertEquals("Query is wrong", "IntersectsPrefixTreeQuery(fieldName=name.dist,queryShape=Circle(Pt(x=-180.0,y=90.0), " + "d=0.0° 1.00km),detailLevel=8,prefixGridScanLevel=4)", filter.toString());
}

@Test
public void testQueryMax() {
    Schema schema = schema().mapper("name", geoPointMapper("lat", "lon").maxLevels(8)).build();
    GeoDistanceCondition condition = new GeoDistanceCondition(0.5f, "name", 90D, -180D, null, GeoDistance.parse("10hm"));
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    BooleanQuery booleanQuery = (BooleanQuery) query;
    assertEquals("Query num clauses is wrong", 1, booleanQuery.clauses().size());
    BooleanClause maxClause = booleanQuery.clauses().get(0);
    assertEquals("Query occur is wrong", BooleanClause.Occur.FILTER, maxClause.getOccur());
    query = maxClause.getQuery();
    assertEquals("Query type is wrong", IntersectsPrefixTreeQuery.class, query.getClass());
    IntersectsPrefixTreeQuery filter = (IntersectsPrefixTreeQuery) query;
    assertEquals("Query is wrong", "IntersectsPrefixTreeQuery(fieldName=name.dist,queryShape=Circle(Pt(x=-180.0,y=90.0), " + "d=0.0° 1.00km),detailLevel=8,prefixGridScanLevel=4)", filter.toString());
}


@Override
public Query query(Schema schema) {
    SpatialStrategy strategy;
    Mapper mapper = schema.getMapper(field);
    if (mapper == null) {
        throw new IndexException("No mapper found for field '%s'", field);
    } else if (mapper instanceof GeoShapeMapper) {
        strategy = ((GeoShapeMapper) mapper).strategy;
    } else if (mapper instanceof GeoPointMapper) {
        strategy = ((GeoPointMapper) mapper).distanceStrategy;
    } else {
        throw new IndexException("'geo_shape' search requires a mapper of type 'geo_point' or 'geo_shape' " + "but found %s:%s", field, mapper);
    }
    JtsGeometry transformedGeometry = geometry;
    if (transformations != null) {
        for (GeoTransformation transformation : transformations) {
            transformedGeometry = transformation.apply(transformedGeometry, CONTEXT);
        }
    }
    SpatialArgs args = new SpatialArgs(operation.getSpatialOperation(), transformedGeometry);
    args.setDistErr(0.0);
    Query query = strategy.makeQuery(args);
    query.setBoost(boost);
    return query;
}

@Test
public void testToString() {
    String wkt = "POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2, 3 2, 3 3, 2 3,2 2))";
    GeoShapeCondition condition = geoShape("name", wkt).operation("intersects").transformations(null).build();
    assertEquals("Method #toString is wrong", "GeoShapeCondition{boost=1.0, field=name, geometry=" + "POLYGON ((1 1, 5 1, 5 5, 1 5, 1 1), (2 2, 3 2, 3 3, 2 3, 2 2)), " + "operation=INTERSECTS, transformations=[]}", condition.toString());
}

@Test
public void testToString() {
    String wkt = "POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2, 3 2, 3 3, 2 3,2 2))";
    GeoShapeCondition condition = geoShape("name", wkt).operation("intersects").transformations(null).build();
    assertEquals("Method #toString is wrong", "GeoShapeCondition{boost=null, field=name, geometry=" + "POLYGON ((1 1, 5 1, 5 5, 1 5, 1 1), (2 2, 3 2, 3 3, 2 3, 2 2)), " + "operation=INTERSECTS, transformations=[]}", condition.toString());
}

@Override
public String toString() {
    return toStringHelper(this).add("geometry", geometry).add("operation", operation).add("transformations", transformations).toString();
}


@Test
public void testToString() {
    LuceneCondition condition = new LuceneCondition(0.7f, "field_1", "field_2:houses");
    assertEquals("Method #toString is wrong", "LuceneCondition{query=field_2:houses, defaultField=field_1}", condition.toString());
}

@Test
public void testToString() {
    LuceneCondition condition = new LuceneCondition(0.7f, "field_1", "field_2:houses");
    assertEquals("Method #toString is wrong", "LuceneCondition{boost=0.7, query=field_2:houses, defaultField=field_1}", condition.toString());
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this).add("query", query).add("defaultField", defaultField).toString();
}

@Test
public void testQuery() {
    Schema schema = schema().defaultAnalyzer("english").build();
    LuceneCondition condition = new LuceneCondition(0.7f, "field_1", "field_2:houses");
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermQuery.class, query.getClass());
    TermQuery termQuery = (TermQuery) query;
    assertEquals("Query term is wrong", "hous", termQuery.getTerm().bytes().utf8ToString());
    assertEquals("Query boost is wrong", 0.7f, query.getBoost(), 0);
}

@Test
public void testQuery() {
    Schema schema = schema().defaultAnalyzer("english").build();
    LuceneCondition condition = new LuceneCondition(0.7f, "field_1", "field_2:houses");
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermQuery.class, query.getClass());
    TermQuery termQuery = (TermQuery) query;
    assertEquals("Query term is wrong", "hous", termQuery.getTerm().bytes().utf8ToString());
}

@Override
public Query query(Schema schema) {
    try {
        Analyzer analyzer = schema.getAnalyzer();
        QueryParser queryParser = new QueryParser(defaultField, analyzer);
        queryParser.setAllowLeadingWildcard(true);
        queryParser.setLowercaseExpandedTerms(false);
        Query luceneQuery = queryParser.parse(query);
        luceneQuery.setBoost(boost);
        return luceneQuery;
    } catch (ParseException e) {
        throw new IndexException("Error while parsing lucene syntax query: %s", e.getMessage());
    }
}


@Test
public void testInteger() {
    Schema schema = schema().mapper("name", integerMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", 42);
    Query query = matchCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query value is wrong", 42, numericRangeQuery.getMin());
    assertEquals("Query value is wrong", 42, numericRangeQuery.getMax());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testInteger() {
    Schema schema = schema().mapper("name", integerMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", 42);
    Query query = matchCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query value is wrong", 42, numericRangeQuery.getMin());
    assertEquals("Query value is wrong", 42, numericRangeQuery.getMax());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMax());
}

@Test
public void testBlob() {
    Schema schema = schema().mapper("name", blobMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", "0Fa1");
    Query query = matchCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermQuery.class, query.getClass());
    TermQuery termQuery = (TermQuery) query;
    assertEquals("Query value is wrong", "0fa1", termQuery.getTerm().bytes().utf8ToString());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testBlob() {
    Schema schema = schema().mapper("name", blobMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", "0Fa1");
    Query query = matchCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermQuery.class, query.getClass());
    TermQuery termQuery = (TermQuery) query;
    assertEquals("Query value is wrong", "0fa1", termQuery.getTerm().bytes().utf8ToString());
}

@Override
@SuppressWarnings("unchecked")
public final Query doQuery(Schema schema) {
    Mapper mapper = schema.getMapper(field);
    if (mapper == null) {
        throw new IndexException("No mapper found for field '%s'", field);
    } else if (!SingleColumnMapper.class.isAssignableFrom(mapper.getClass())) {
        throw new IndexException("Field '%s' requires a mapper of type '%s' but found '%s'", field, SingleColumnMapper.class.getSimpleName(), mapper);
    }
    return doQuery((SingleColumnMapper<?>) mapper, schema.getAnalyzer());
}

@Test
public void testLong() {
    Schema schema = schema().mapper("name", longMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", 42L);
    Query query = matchCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query value is wrong", 42L, numericRangeQuery.getMin());
    assertEquals("Query value is wrong", 42L, numericRangeQuery.getMax());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testLong() {
    Schema schema = schema().mapper("name", longMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", 42L);
    Query query = matchCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query value is wrong", 42L, numericRangeQuery.getMin());
    assertEquals("Query value is wrong", 42L, numericRangeQuery.getMax());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMax());
}

@Test
public void testDouble() {
    Schema schema = schema().mapper("name", doubleMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", 42.42D);
    Query query = matchCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query value is wrong", 42.42D, numericRangeQuery.getMin());
    assertEquals("Query value is wrong", 42.42D, numericRangeQuery.getMax());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testDouble() {
    Schema schema = schema().mapper("name", doubleMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", 42.42D);
    Query query = matchCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query value is wrong", 42.42D, numericRangeQuery.getMin());
    assertEquals("Query value is wrong", 42.42D, numericRangeQuery.getMax());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMax());
}

@Test
public void testFloat() {
    Schema schema = schema().mapper("name", floatMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", 42.42F);
    Query query = matchCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query value is wrong", 42.42F, numericRangeQuery.getMin());
    assertEquals("Query value is wrong", 42.42F, numericRangeQuery.getMax());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMax());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testFloat() {
    Schema schema = schema().mapper("name", floatMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", 42.42F);
    Query query = matchCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", NumericRangeQuery.class, query.getClass());
    NumericRangeQuery<?> numericRangeQuery = (NumericRangeQuery<?>) query;
    assertEquals("Query value is wrong", 42.42F, numericRangeQuery.getMin());
    assertEquals("Query value is wrong", 42.42F, numericRangeQuery.getMax());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMin());
    assertEquals("Query value is wrong", true, numericRangeQuery.includesMax());
}

@Test
public void testStringStopwords() {
    Schema schema = schema().mapper("name", textMapper().analyzer("english")).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", "the");
    Query query = matchCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", BooleanQuery.class, query.getClass());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testStringStopwords() {
    Schema schema = schema().mapper("name", textMapper().analyzer("english")).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", "the");
    Query query = matchCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", BooleanQuery.class, query.getClass());
}

@Test
public void testString() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", "value");
    Query query = matchCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermQuery.class, query.getClass());
    TermQuery termQuery = (TermQuery) query;
    assertEquals("Query value is wrong", "value", termQuery.getTerm().bytes().utf8ToString());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testString() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", "value");
    Query query = matchCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermQuery.class, query.getClass());
    TermQuery termQuery = (TermQuery) query;
    assertEquals("Query value is wrong", "value", termQuery.getTerm().bytes().utf8ToString());
}

@Test
public void testInetV4() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", "192.168.0.01");
    Query query = matchCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermQuery.class, query.getClass());
    TermQuery termQuery = (TermQuery) query;
    assertEquals("Query value is wrong", "192.168.0.1", termQuery.getTerm().bytes().utf8ToString());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testInetV4() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", "192.168.0.01");
    Query query = matchCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermQuery.class, query.getClass());
    TermQuery termQuery = (TermQuery) query;
    assertEquals("Query value is wrong", "192.168.0.1", termQuery.getTerm().bytes().utf8ToString());
}

@Test
public void testInetV6() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", "2001:DB8:2de::0e13");
    Query query = matchCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermQuery.class, query.getClass());
    TermQuery termQuery = (TermQuery) query;
    assertEquals("Query value is wrong", "2001:db8:2de:0:0:0:0:e13", termQuery.getTerm().bytes().utf8ToString());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testInetV6() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    MatchCondition matchCondition = new MatchCondition(0.5f, "name", "2001:DB8:2de::0e13");
    Query query = matchCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", TermQuery.class, query.getClass());
    TermQuery termQuery = (TermQuery) query;
    assertEquals("Query value is wrong", "2001:db8:2de:0:0:0:0:e13", termQuery.getTerm().bytes().utf8ToString());
}


@Test
public void testQuery() {
    Schema schema = schema().build();
    NoneCondition condition = new NoneCondition(0.7f);
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", BooleanQuery.class, query.getClass());
    assertEquals("Query boost is wrong", 0.7f, query.getBoost(), 0);
}

@Test
public void testQuery() {
    Schema schema = schema().build();
    NoneCondition condition = new NoneCondition(0.7f);
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", BooleanQuery.class, query.getClass());
}

@Override
public Query query(Schema schema) {
    Query query = new BooleanQuery.Builder().build();
    query.setBoost(boost);
    return query;
}


@Test
public void testPhraseQuery() {
    Schema schema = schema().mapper("name", textMapper().analyzer("spanish")).build();
    String value = "hola adios  the    a";
    PhraseCondition condition = new PhraseCondition(0.5f, "name", value, 2);
    Query query = condition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", PhraseQuery.class, query.getClass());
    PhraseQuery luceneQuery = (PhraseQuery) query;
    assertEquals("Query terms are wrong", 3, luceneQuery.getTerms().length);
    assertEquals("Query slop is wrong", 2, luceneQuery.getSlop());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testPhraseQuery() {
    Schema schema = schema().mapper("name", textMapper().analyzer("spanish")).build();
    String value = "hola adios  the    a";
    PhraseCondition condition = new PhraseCondition(0.5f, "name", value, 2);
    Query query = condition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", PhraseQuery.class, query.getClass());
    PhraseQuery luceneQuery = (PhraseQuery) query;
    assertEquals("Query terms are wrong", 3, luceneQuery.getTerms().length);
    assertEquals("Query slop is wrong", 2, luceneQuery.getSlop());
}


@Test
public void testInetV4Value() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    PrefixCondition wildcardCondition = new PrefixCondition(0.5f, "name", "192.168.");
    Query query = wildcardCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", PrefixQuery.class, query.getClass());
    PrefixQuery luceneQuery = (PrefixQuery) query;
    assertEquals("Query field is wrong", "name", luceneQuery.getField());
    assertEquals("Query prefix is wrong", "192.168.", luceneQuery.getPrefix().text());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testInetV4Value() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    PrefixCondition wildcardCondition = new PrefixCondition(0.5f, "name", "192.168.");
    Query query = wildcardCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", PrefixQuery.class, query.getClass());
    PrefixQuery luceneQuery = (PrefixQuery) query;
    assertEquals("Query field is wrong", "name", luceneQuery.getField());
    assertEquals("Query prefix is wrong", "192.168.", luceneQuery.getPrefix().text());
}

@Test
public void testToString() {
    PrefixCondition condition = new PrefixCondition(0.5f, "name", "tr");
    assertEquals("Method #toString is wrong", "PrefixCondition{field=name, value=tr}", condition.toString());
}

@Test
public void testToString() {
    PrefixCondition condition = new PrefixCondition(0.5f, "name", "tr");
    assertEquals("Method #toString is wrong", "PrefixCondition{boost=0.5, field=name, value=tr}", condition.toString());
}

@Override
public String toString() {
    return MoreObjects.toStringHelper(this).add("field", field).add("value", value).toString();
}

@Test
public void testStringValue() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    PrefixCondition prefixCondition = new PrefixCondition(0.5f, "name", "tr");
    Query query = prefixCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", PrefixQuery.class, query.getClass());
    PrefixQuery luceneQuery = (PrefixQuery) query;
    assertEquals("Query field is wrong", "name", luceneQuery.getField());
    assertEquals("Query prefix is wrong", "tr", luceneQuery.getPrefix().text());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testStringValue() {
    Schema schema = schema().mapper("name", stringMapper()).build();
    PrefixCondition prefixCondition = new PrefixCondition(0.5f, "name", "tr");
    Query query = prefixCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", PrefixQuery.class, query.getClass());
    PrefixQuery luceneQuery = (PrefixQuery) query;
    assertEquals("Query field is wrong", "name", luceneQuery.getField());
    assertEquals("Query prefix is wrong", "tr", luceneQuery.getPrefix().text());
}

@Test
public void testInetV6Value() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    PrefixCondition wildcardCondition = new PrefixCondition(0.5f, "name", "2001:db8:2de:0:0:0:0:e");
    Query query = wildcardCondition.query(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", PrefixQuery.class, query.getClass());
    PrefixQuery luceneQuery = (PrefixQuery) query;
    assertEquals("Query field is wrong", "name", luceneQuery.getField());
    assertEquals("Query prefix is wrong", "2001:db8:2de:0:0:0:0:e", luceneQuery.getPrefix().text());
    assertEquals("Query boost is wrong", 0.5f, query.getBoost(), 0);
}

@Test
public void testInetV6Value() {
    Schema schema = schema().mapper("name", inetMapper()).build();
    PrefixCondition wildcardCondition = new PrefixCondition(0.5f, "name", "2001:db8:2de:0:0:0:0:e");
    Query query = wildcardCondition.doQuery(schema);
    assertNotNull("Query is not built", query);
    assertEquals("Query type is wrong", PrefixQuery.class, query.getClass());
    PrefixQuery luceneQuery = (PrefixQuery) query;
    assertEquals("Query field is wrong", "name", luceneQuery.getField());
    assertEquals("Query prefix is wrong", "2001:db8:2de:0:0:0:0:e", luceneQuery.getPrefix().text());
}