/** * Copyright 2014 Otto (GmbH & Co KG) * * 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 com.ottogroup.bi.spqr.operator.twitter.source; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; import com.ottogroup.bi.spqr.exception.RequiredInputMissingException; import com.ottogroup.bi.spqr.pipeline.component.source.IncomingMessageCallback; import com.twitter.hbc.httpclient.BasicClient; /** * Test case for {@link TwitterStreamSource} * @author mnxfst * @since Dec 1, 2014 * */ public class TwitterStreamSourceTest { private static ExecutorService executor; @BeforeClass public static void initialize() throws Exception { executor = Executors.newCachedThreadPool(); } @AfterClass public static void shutdown() throws Exception { if(executor != null) executor.shutdownNow(); } /** * Test case for {@link TwitterStreamSource#initialize(java.util.Properties)} being provided * null as input */ @Test public void testInitialize_withNullInput() throws Exception { try { new TwitterStreamSource().initialize(null); Assert.fail("Missing required input"); } catch(RequiredInputMissingException e) { // expected } } /** * Test case for {@link TwitterStreamSource#initialize(java.util.Properties)} being provided * an empty properties instance */ @Test public void testInitialize_withEmptyProperties() throws Exception { try { new TwitterStreamSource().initialize(new Properties()); Assert.fail("Missing required input"); } catch(RequiredInputMissingException e) { // expected } } /** * Test case for {@link TwitterStreamSource#initialize(java.util.Properties)} being provided * properties missing the component id */ @Test public void testInitialize_withEmptyComponentId() throws Exception { Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, "ckey"); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, "csecret"); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, "tkey"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, "tsecret"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, ""); try { new TwitterStreamSource().initialize(props); Assert.fail("Missing required input"); } catch(RequiredInputMissingException e) { // expected } } /** * Test case for {@link TwitterStreamSource#initialize(java.util.Properties)} being provided * properties missing the consumer key */ @Test public void testInitialize_withEmptyConsumerKey() throws Exception { Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, ""); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, "csecret"); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, "tkey"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, "tsecret"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, ""); try { new TwitterStreamSource().initialize(props); Assert.fail("Missing required input"); } catch(RequiredInputMissingException e) { // expected } } /** * Test case for {@link TwitterStreamSource#initialize(java.util.Properties)} being provided * properties missing the consumer secret */ @Test public void testInitialize_withEmptyConsumerSecret() throws Exception { Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, "ckey"); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, ""); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, "tkey"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, "tsecret"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, ""); try { new TwitterStreamSource().initialize(props); Assert.fail("Missing required input"); } catch(RequiredInputMissingException e) { // expected } } /** * Test case for {@link TwitterStreamSource#initialize(java.util.Properties)} being provided * properties missing the token key */ @Test public void testInitialize_withEmptyTokenKey() throws Exception { Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, "ckey"); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, "csecret"); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, ""); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, "tsecret"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, ""); try { new TwitterStreamSource().initialize(props); Assert.fail("Missing required input"); } catch(RequiredInputMissingException e) { // expected } } /** * Test case for {@link TwitterStreamSource#initialize(java.util.Properties)} being provided * properties missing the token secret */ @Test public void testInitialize_withEmptyTokenSecret() throws Exception { Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, "ckey"); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, "csecret"); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, "tkey"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, ""); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, ""); try { new TwitterStreamSource().initialize(props); Assert.fail("Missing required input"); } catch(RequiredInputMissingException e) { // expected } } /** * Test case for {@link TwitterStreamSource#initialize(java.util.Properties)} being provided * properties showing all required input */ @Test public void testInitialize_withRequiredInput() throws Exception { Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, "ckey"); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, "csecret"); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, "tkey"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, "tsecret"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, ""); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, ""); TwitterStreamSource source = new TwitterStreamSource(); source.setId("test-id"); source.setTwitterClient(Mockito.mock(BasicClient.class)); source.initialize(props); } /** * Test case for {@link TwitterStreamSource#initialize(java.util.Properties)} being provided * properties showing all input */ @Test public void testInitialize_withAllInput() throws Exception { String componentId = "testInitialize_withAllInput"; String consumerKey = "testInitialize_withAllInput-consumer-key"; String consumerSecret = "testInitialize_withAllInput-consumer-secret"; String profiles = "123, 456, 789"; String tokenKey = "testInitialize_withAllInput-token-key"; String tokenSecret = "testInitialize_withAllInput-token-secret"; String languages = "de, fr, en"; String searchTerms = "soccer, fifa, uefa"; Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, consumerKey); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, consumerSecret); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, profiles); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, tokenKey); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, tokenSecret); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, languages); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, searchTerms); TwitterStreamSource consumer = new TwitterStreamSource(); consumer.setId(componentId); consumer.setTwitterClient(Mockito.mock(BasicClient.class)); consumer.initialize(props); Assert.assertEquals("Values must be equal", componentId, consumer.getId()); Assert.assertEquals("Values must be equal", consumerKey, consumer.getConsumerKey()); Assert.assertEquals("Values must be equal", consumerSecret, consumer.getConsumerSecret()); Assert.assertEquals("Values must be equal", tokenKey, consumer.getTokenKey()); Assert.assertEquals("Values must be equal", tokenSecret, consumer.getTokenSecret()); Assert.assertEquals("Values must be equal", 3, consumer.getProfiles().size()); Assert.assertTrue("Must be inclued", consumer.getProfiles().contains(123l)); Assert.assertTrue("Must be inclued", consumer.getProfiles().contains(456l)); Assert.assertTrue("Must be inclued", consumer.getProfiles().contains(789l)); Assert.assertEquals("Values must be equal", 3, consumer.getLanguages().size()); Assert.assertTrue("Must be inclued", consumer.getLanguages().contains("de")); Assert.assertTrue("Must be inclued", consumer.getLanguages().contains("en")); Assert.assertTrue("Must be inclued", consumer.getLanguages().contains("fr")); Assert.assertEquals("Values must be equal", 3, consumer.getSearchTerms().size()); Assert.assertTrue("Must be inclued", consumer.getSearchTerms().contains("uefa")); Assert.assertTrue("Must be inclued", consumer.getSearchTerms().contains("fifa")); Assert.assertTrue("Must be inclued", consumer.getSearchTerms().contains("soccer")); Assert.assertTrue("Must be true", consumer.isRunning()); } /** * Test case for {@link TwitterStreamSource#run()} with a * twitter client showing true when calling isDone */ @Test public void testRun_withTwitterClientWhichIsDone() throws Exception { BasicClient twitterClientMock = Mockito.mock(BasicClient.class); Mockito.when(twitterClientMock.isDone()).thenReturn(true); Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, "consumer-key"); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, "consumer-secret"); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, "1"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, "token-key"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, "token-secret"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, "en"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, "soccer"); TwitterStreamSource consumer = new TwitterStreamSource(); consumer.setId("test-id"); consumer.initialize(props); consumer.setTwitterClient(twitterClientMock); consumer.getStreamMessageQueue().offer("test-message"); Assert.assertFalse("Value must be true", consumer.getStreamMessageQueue().isEmpty()); executor.submit(consumer); Thread.sleep(100); consumer.run(); Assert.assertFalse("Value must be true", consumer.getStreamMessageQueue().isEmpty()); Assert.assertEquals("Values must be equal", 0, consumer.getMessageCount()); } /** * Test case for {@link TwitterStreamSource#run()} where isRunning shows false */ @Test public void testRun_withIsRunningSetToFalse() throws Exception { BasicClient twitterClientMock = Mockito.mock(BasicClient.class); IncomingMessageCallback callback = Mockito.mock(IncomingMessageCallback.class); Mockito.when(twitterClientMock.isDone()).thenReturn(false); Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, "consumer-key"); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, "consumer-secret"); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, "1"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, "token-key"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, "token-secret"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, "en"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, "soccer"); TwitterStreamSource consumer = new TwitterStreamSource(); consumer.setId("test-id"); consumer.setTwitterClient(twitterClientMock); consumer.initialize(props); consumer.setIncomingMessageCallback(callback); consumer.setRunning(false); String msgContent = "{\"content\":\"test-message\", \"timestamp_ms\":\""+System.currentTimeMillis()+"\"}"; consumer.getStreamMessageQueue().offer(msgContent); Assert.assertFalse("Value must be false", consumer.getStreamMessageQueue().isEmpty()); executor.submit(consumer); Thread.sleep(100); consumer.shutdown(); Assert.assertEquals("Must show one element as none were consumed", 1, consumer.getStreamMessageQueue().size()); Assert.assertEquals("Values must be equal", 0, consumer.getMessageCount()); } /** * Test case for {@link TwitterStreamSource#run()} with at least one iteration */ @Test public void testRun_withAtLeastOneIteration() throws Exception { IncomingMessageCallback callback = Mockito.mock(IncomingMessageCallback.class); BasicClient twitterClientMock = Mockito.mock(BasicClient.class); Mockito.when(twitterClientMock.isDone()).thenReturn(false); Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, "consumer-key"); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, "consumer-secret"); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, "1"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, "token-key"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, "token-secret"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, "en"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, "soccer"); TwitterStreamSource consumer = new TwitterStreamSource(); consumer.setId("test-id"); consumer.setTwitterClient(twitterClientMock); consumer.setIncomingMessageCallback(callback); consumer.initialize(props); consumer.getStreamMessageQueue().offer("{\"content\":\"test-message\", \"timestamp_ms\":\""+System.currentTimeMillis()+"\"}"); Assert.assertFalse("Value must be false", consumer.getStreamMessageQueue().isEmpty()); executor.submit(consumer); Thread.sleep(100); consumer.shutdown(); Assert.assertTrue("Value must be true", consumer.getStreamMessageQueue().isEmpty()); Assert.assertEquals("Values must be equal", 1, consumer.getMessageCount()); } /** * Test case for {@link TwitterStreamSource#run()} with one valid and one invalid message. Must show no errors * as the source does not care about the message format */ @Test public void testRun_withOneValidOneInvalidMessage() throws Exception { IncomingMessageCallback callback = Mockito.mock(IncomingMessageCallback.class); BasicClient twitterClientMock = Mockito.mock(BasicClient.class); Mockito.when(twitterClientMock.isDone()).thenReturn(false); Properties props = new Properties(); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_KEY, "consumer-key"); props.put(TwitterStreamSource.CFG_TWITTER_CONSUMER_SECRET, "consumer-secret"); props.put(TwitterStreamSource.CFG_TWITTER_PROFILES, "1"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_KEY, "token-key"); props.put(TwitterStreamSource.CFG_TWITTER_TOKEN_SECRET, "token-secret"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_LANGUAGES, "en"); props.put(TwitterStreamSource.CFG_TWITTER_TWEET_SEARCH_TERMS, "soccer"); TwitterStreamSource consumer = new TwitterStreamSource(); consumer.setTwitterClient(twitterClientMock); consumer.setId("testRun_withOneValidOneInvalidMessage"); consumer.initialize(props); consumer.setIncomingMessageCallback(callback); consumer.getStreamMessageQueue().offer("{\"content\":\"test-message\", \"timestamp_ms\":\""+System.currentTimeMillis()+"\"}"); consumer.getStreamMessageQueue().offer("noJsonContent"); Assert.assertEquals("Values must be equal", 2, consumer.getStreamMessageQueue().size()); executor.submit(consumer); Thread.sleep(200); consumer.shutdown(); Assert.assertTrue("Value must be true", consumer.getStreamMessageQueue().isEmpty()); Assert.assertEquals("Values must be equal", 2, consumer.getMessageCount()); } }