/* * Copyright 2015-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cloud.stream.module.cassandra.sink; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; import org.cassandraunit.spring.CassandraUnitDependencyInjectionIntegrationTestExecutionListener; import org.cassandraunit.spring.EmbeddedCassandra; import org.cassandraunit.utils.EmbeddedCassandraServerHelper; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.IntegrationTestPropertiesListener; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.WebIntegrationTest; import org.springframework.cloud.stream.annotation.Bindings; import org.springframework.cloud.stream.messaging.Sink; import org.springframework.cloud.stream.module.cassandra.test.domain.Book; import org.springframework.data.cassandra.core.CassandraOperations; import org.springframework.integration.support.json.Jackson2JsonObjectMapper; import org.springframework.messaging.support.GenericMessage; import org.springframework.util.StringUtils; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.utils.UUIDs; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; /** * @author Artem Bilan * @author Thomas Risberg * @author Ashu Gairola */ @RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners(mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS, listeners = {IntegrationTestPropertiesListener.class, CassandraUnitDependencyInjectionIntegrationTestExecutionListener.class}) @SpringApplicationConfiguration(CassandraSinkApplication.class) @IntegrationTest({"spring.cassandra.keyspace=" + CassandraSinkIntegrationTests.CASSANDRA_KEYSPACE, "spring.cassandra.createKeyspace=true", "server.port=-1"}) @EmbeddedCassandra(configuration = EmbeddedCassandraServerHelper.CASSANDRA_RNDPORT_YML_FILE, timeout = 120000) @DirtiesContext public abstract class CassandraSinkIntegrationTests { public static final String CASSANDRA_KEYSPACE = "test"; @Autowired @Bindings(CassandraSinkConfiguration.class) protected Sink sink; @Autowired protected CassandraOperations cassandraTemplate; @BeforeClass public static void setUp() { System.setProperty("spring.cassandra.port", "" + EmbeddedCassandraServerHelper.getNativeTransportPort()); } @AfterClass public static void cleanup() { System.clearProperty("spring.cassandra.port"); } @WebIntegrationTest({"spring.cassandra.schema-action=RECREATE", "spring.cassandra.entity-base-packages=org.springframework.cloud.stream.module.cassandra.test.domain"}) public static class CassandraEntityInsertTests extends CassandraSinkIntegrationTests { @Test public void testInsert() throws InterruptedException { Book book = new Book(); book.setIsbn(UUIDs.timeBased()); book.setTitle("Spring Integration Cassandra"); book.setAuthor("Cassandra Guru"); book.setPages(521); book.setSaleDate(new Date()); book.setInStock(true); this.sink.input().send(new GenericMessage<>(book)); final Select select = QueryBuilder.select().all().from("book"); assertEqualsEventually(1, new Supplier<Integer>() { @Override public Integer get() { return cassandraTemplate.select(select, Book.class).size(); } }); this.cassandraTemplate.delete(book); } } @WebIntegrationTest({"spring.cassandra.init-script=init-db.cql", "ingest-query=insert into book (isbn, title, author, pages, saleDate, inStock) values (?, ?, ?, ?, ?, ?)"}) public static class CassandraSinkIngestTests extends CassandraSinkIntegrationTests { @Test public void testIngestQuery() throws Exception { List<Book> books = getBookList(5); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); Jackson2JsonObjectMapper mapper = new Jackson2JsonObjectMapper(objectMapper); this.sink.input().send(new GenericMessage<>(mapper.toJson(books))); final Select select = QueryBuilder.select().all().from("book"); assertEqualsEventually(5, new Supplier<Integer>() { @Override public Integer get() { return cassandraTemplate.select(select, Book.class).size(); } }); this.cassandraTemplate.truncate("book"); } } @WebIntegrationTest({"spring.cassandra.init-script=init-db.cql", "ingest-query=insert into book (isbn, title, author, pages, saleDate, inStock) values (:myIsbn, :myTitle, :myAuthor, ?, ?, ?)"}) public static class CassandraSinkIngestNamedParamsTests extends CassandraSinkIntegrationTests { @Test public void testIngestQuery() throws Exception { List<Book> books = getBookList(5); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); Jackson2JsonObjectMapper mapper = new Jackson2JsonObjectMapper(objectMapper); String booksJsonWithNamedParams = mapper.toJson(books); booksJsonWithNamedParams = StringUtils.replace(booksJsonWithNamedParams, "isbn", "myIsbn"); booksJsonWithNamedParams = StringUtils.replace(booksJsonWithNamedParams, "title", "myTitle"); booksJsonWithNamedParams = StringUtils.replace(booksJsonWithNamedParams, "author", "myAuthor"); this.sink.input().send(new GenericMessage<>(booksJsonWithNamedParams)); final Select select = QueryBuilder.select().all().from("book"); assertEqualsEventually(5, new Supplier<Integer>() { @Override public Integer get() { return cassandraTemplate.select(select, Book.class).size(); } }); this.cassandraTemplate.truncate("book"); } } private static List<Book> getBookList(int numBooks) { List<Book> books = new ArrayList<>(); Book b; for (int i = 0; i < numBooks; i++) { b = new Book(); b.setIsbn(UUID.randomUUID()); b.setTitle("Spring XD Guide"); b.setAuthor("XD Guru"); b.setPages(i * 10 + 5); b.setInStock(true); b.setSaleDate(new Date()); books.add(b); } return books; } private static <T> void assertEqualsEventually(T expected, Supplier<T> actualSupplier) throws InterruptedException { int n = 0; while (!actualSupplier.get().equals(expected) && n++ < 100) { Thread.sleep(100); } assertTrue(n < 10); } private interface Supplier<T> { T get(); } }