diff --git a/impl/src/main/java/slack/SlackClient.java b/impl/src/main/java/slack/SlackClient.java new file mode 100644 index 0000000..cb85e5d --- /dev/null +++ b/impl/src/main/java/slack/SlackClient.java @@ -0,0 +1,43 @@ +package slack; + +import com.github.seratch.jslack.api.methods.MethodsClient; +import com.github.seratch.jslack.api.methods.request.channels.ChannelsRepliesRequest; +import com.github.seratch.jslack.api.methods.request.chat.ChatPostMessageRequest; +import com.github.seratch.jslack.api.methods.request.users.UsersInfoRequest; +import com.github.seratch.jslack.api.methods.request.users.UsersLookupByEmailRequest; +import com.github.seratch.jslack.api.methods.response.channels.ChannelsRepliesResponse; +import com.github.seratch.jslack.api.methods.response.channels.UsersLookupByEmailResponse; + +import com.github.seratch.jslack.api.methods.response.chat.ChatPostMessageResponse; +import com.github.seratch.jslack.api.methods.response.users.UsersInfoResponse; +import rx.Observable; + +/** + * Observable slack client that wraps the com.github.seratch.jslack.Slack client. + * + * @see "https://github.com/seratch/jslack" + * + */ +public interface SlackClient { + + /** + * @see MethodsClient#usersLookupByEmail(UsersLookupByEmailRequest) + */ + Observable usersLookupByEmail(UsersLookupByEmailRequest usersInfoRequest); + + + /** + * @see MethodsClient#usersInfo(UsersInfoRequest) + */ + Observable usersInfo(UsersInfoRequest slackRequest); + + /** + * @see MethodsClient#chatPostMessage(ChatPostMessageRequest) + */ + Observable chatPostMessage(ChatPostMessageRequest chatPostMessageRequest); + + /** + * @see MethodsClient#channelsReplies(ChannelsRepliesRequest) + */ + Observable channelsReplies(ChannelsRepliesRequest channelsRepliesRequest); +} diff --git a/impl/src/main/java/slack/SlackClientImpl.java b/impl/src/main/java/slack/SlackClientImpl.java new file mode 100644 index 0000000..5c65118 --- /dev/null +++ b/impl/src/main/java/slack/SlackClientImpl.java @@ -0,0 +1,72 @@ +package slack; + +import com.github.seratch.jslack.Slack; +import com.github.seratch.jslack.api.methods.request.channels.ChannelsRepliesRequest; +import com.github.seratch.jslack.api.methods.request.chat.ChatPostMessageRequest; +import com.github.seratch.jslack.api.methods.request.users.UsersInfoRequest; +import com.github.seratch.jslack.api.methods.request.users.UsersLookupByEmailRequest; +import com.github.seratch.jslack.api.methods.response.channels.ChannelsRepliesResponse; +import com.github.seratch.jslack.api.methods.response.channels.UsersLookupByEmailResponse; +import com.github.seratch.jslack.api.methods.response.chat.ChatPostMessageResponse; +import com.github.seratch.jslack.api.methods.response.users.UsersInfoResponse; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import rx.Observable; +import rx.functions.Action1; + +import java.io.IOException; +import java.util.concurrent.Callable; + +import static rx.Observable.fromCallable; + + +@Singleton +public class SlackClientImpl implements SlackClient { + + private static final Logger LOG = LoggerFactory.getLogger(SlackClientImpl.class); + + private Slack slack; + + @Inject + SlackClientImpl(Slack slack) { + this.slack = slack; + } + + @Override + public Observable usersLookupByEmail(UsersLookupByEmailRequest usersInfoRequest) { + return callSlack(() -> slack.methods().usersLookupByEmail(usersInfoRequest)); + } + + @Override + public Observable usersInfo(UsersInfoRequest slackRequest) { + return callSlack(() -> slack.methods().usersInfo(slackRequest)); + } + + + @Override + public Observable chatPostMessage(ChatPostMessageRequest chatPostMessageRequest) { + return callSlack(() -> slack.methods().chatPostMessage(chatPostMessageRequest)); + } + + + @Override + public Observable channelsReplies(ChannelsRepliesRequest channelsRepliesRequest) { + return callSlack(() -> slack.methods().channelsReplies(channelsRepliesRequest)); + } + + Observable callSlack(Callable callable) { + return fromCallable(callable).doOnError(logError()); + } + + private static Action1 logError() { + return e -> { + if(e instanceof IOException) { + LOG.error("Failed to communicate with slack", e); + } else { + LOG.warn("Got bad response while communicating with slack", e); + } + }; + } +} diff --git a/impl/src/main/java/slack/SlackResourceImpl.java b/impl/src/main/java/slack/SlackResourceImpl.java index fea8d00..f13bc5c 100644 --- a/impl/src/main/java/slack/SlackResourceImpl.java +++ b/impl/src/main/java/slack/SlackResourceImpl.java @@ -1,6 +1,5 @@ package slack; -import com.github.seratch.jslack.Slack; import com.github.seratch.jslack.api.methods.request.channels.ChannelsRepliesRequest; import com.github.seratch.jslack.api.methods.request.chat.ChatPostMessageRequest; import com.github.seratch.jslack.api.methods.request.users.UsersInfoRequest; @@ -18,40 +17,41 @@ import rx.Observable; import java.util.List; -import java.util.concurrent.Callable; import static rx.Observable.empty; import static rx.Observable.error; -import static rx.Observable.fromCallable; @Singleton public class SlackResourceImpl implements SlackResource { - private static final Logger LOG = LoggerFactory.getLogger(SlackResourceImpl.class); - private final Slack slack; - private final SlackConfig slackConfig; + private static final Logger LOG = LoggerFactory.getLogger(SlackResourceImpl.class); + public static final String MISSING_USER_EMAIL = "User email from slack is null and might be due to missing scope users:read.email"; + public static final String COULD_NOT_GET_USER_BY_EMAIL_FROM_SLACK = "Could not get user by email from slack: "; + public static final String COULD_NOT_FETCH_USER_EMAIL_FROM_SLACK = "Could not fetch user email from slack: "; + private final SlackConfig slackConfig; + private final SlackClient slackClient; @Inject - public SlackResourceImpl(SlackConfig slackConfig) { - slack = new Slack(); + public SlackResourceImpl(SlackConfig slackConfig, SlackClient slackClient) { this.slackConfig = slackConfig; + this.slackClient = slackClient; } static String handleUserInfoResponse(UsersInfoResponse usersInfoResponse) { if (usersInfoResponse.isOk()) { if (usersInfoResponse.getUser().getProfile().getEmail() == null) { LOG.warn("Could not fetch users email, maybe scope users:read.email is missing"); - throw new IllegalStateException("User email from slack is null and might be due to missing scope users:read.email"); + throw new IllegalStateException(MISSING_USER_EMAIL); } return usersInfoResponse.getUser().getProfile().getEmail(); } else { - throw new IllegalStateException("Could not fetch user email from slack: " + usersInfoResponse.getError()); + throw new IllegalStateException(COULD_NOT_FETCH_USER_EMAIL_FROM_SLACK + usersInfoResponse.getError()); } } static String handleUserByEmailResponse(UsersLookupByEmailResponse usersLookupByEmailResponse) { if (!usersLookupByEmailResponse.isOk()) { - throw new IllegalStateException("Could not get user by email from slack: " + usersLookupByEmailResponse.getError()); + throw new IllegalStateException(COULD_NOT_GET_USER_BY_EMAIL_FROM_SLACK + usersLookupByEmailResponse.getError()); } return usersLookupByEmailResponse.getUser().getId(); @@ -75,73 +75,59 @@ static Message handleGetMessageResponse(ChannelsRepliesResponse channelsRepliesR @Override public Observable getUserEmail(String userId) { - return callSlack(() -> { - - final UsersInfoRequest slackRequest = UsersInfoRequest - .builder() - .user(userId) - .token(slackConfig.getApiToken()) - .build(); - - return slack.methods().usersInfo(slackRequest); - }).map(SlackResourceImpl::handleUserInfoResponse); - } - - private Observable callSlack(Callable callable) { - return fromCallable(callable); + final UsersInfoRequest slackRequest = UsersInfoRequest + .builder() + .user(userId) + .token(slackConfig.getApiToken()) + .build(); + return slackClient.usersInfo(slackRequest).map(SlackResourceImpl::handleUserInfoResponse); } @Override public Observable getUserId(String email) { - return callSlack(() -> { - final UsersLookupByEmailRequest requestByEmail = UsersLookupByEmailRequest + final UsersLookupByEmailRequest requestByEmail = UsersLookupByEmailRequest .builder() .email(email) .token(slackConfig.getBotUserToken()) .build(); - return slack.methods().usersLookupByEmail(requestByEmail); - }).map(SlackResourceImpl::handleUserByEmailResponse); + return slackClient.usersLookupByEmail(requestByEmail).map(SlackResourceImpl::handleUserByEmailResponse); } @Override public Observable postMessageToSlack(String channel, String message, String threadId) { - return callSlack(() -> { - final ChatPostMessageRequest chatPostMessageRequest = ChatPostMessageRequest - .builder() - .channel(channel) - .threadTs(threadId) - .text(message) - .token(slackConfig.getApiToken()) - .build(); - - return slack.methods().chatPostMessage(chatPostMessageRequest); - }).flatMap(SlackResourceImpl::handleChatPostMessageResponse); + final ChatPostMessageRequest chatPostMessageRequest = ChatPostMessageRequest + .builder() + .channel(channel) + .threadTs(threadId) + .text(message) + .token(slackConfig.getApiToken()) + .build(); + return slackClient.chatPostMessage(chatPostMessageRequest) + .flatMap(SlackResourceImpl::handleChatPostMessageResponse); } @Override public Observable postMessageToSlackAsBotUser(String channel, List message) { - return callSlack(() -> { - final ChatPostMessageRequest chatPostMessageRequest = ChatPostMessageRequest + final ChatPostMessageRequest chatPostMessageRequest = ChatPostMessageRequest .builder() .channel(channel) .blocks(message) .asUser(true) .token(slackConfig.getBotUserToken()) .build(); - return slack.methods().chatPostMessage(chatPostMessageRequest); - }).flatMap(SlackResourceImpl::handleChatPostMessageResponse); + return slackClient.chatPostMessage(chatPostMessageRequest) + .flatMap(SlackResourceImpl::handleChatPostMessageResponse); } @Override public Observable getMessageFromSlack(String channel, String messageId) { - return callSlack(() -> { - final ChannelsRepliesRequest getMessageFromSlack = ChannelsRepliesRequest - .builder() - .channel(channel) - .threadTs(messageId) - .token(slackConfig.getApiToken()) - .build(); - return slack.methods().channelsReplies(getMessageFromSlack); - }).map(SlackResourceImpl::handleGetMessageResponse); + final ChannelsRepliesRequest getMessageFromSlack = ChannelsRepliesRequest + .builder() + .channel(channel) + .threadTs(messageId) + .token(slackConfig.getApiToken()) + .build(); + return slackClient.channelsReplies(getMessageFromSlack) + .map(SlackResourceImpl::handleGetMessageResponse); } } diff --git a/impl/src/test/java/slack/SlackClientImplTest.java b/impl/src/test/java/slack/SlackClientImplTest.java new file mode 100644 index 0000000..c27087a --- /dev/null +++ b/impl/src/test/java/slack/SlackClientImplTest.java @@ -0,0 +1,172 @@ +package slack; + +import auth.openid.OpenIdValidator; +import com.github.seratch.jslack.Slack; +import com.github.seratch.jslack.api.methods.MethodsClient; +import com.github.seratch.jslack.api.methods.request.channels.ChannelsRepliesRequest; +import com.github.seratch.jslack.api.methods.request.chat.ChatPostMessageRequest; +import com.github.seratch.jslack.api.methods.request.users.UsersInfoRequest; +import com.github.seratch.jslack.api.methods.request.users.UsersLookupByEmailRequest; +import com.github.seratch.jslack.api.methods.response.channels.ChannelsRepliesResponse; +import com.github.seratch.jslack.api.methods.response.channels.UsersLookupByEmailResponse; +import com.github.seratch.jslack.api.methods.response.chat.ChatPostMessageResponse; +import com.github.seratch.jslack.api.methods.response.users.UsersInfoResponse; +import org.apache.log4j.Appender; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import se.fortnox.reactivewizard.test.LoggingMockUtil; + +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static se.fortnox.reactivewizard.test.TestUtil.matches; + +public class SlackClientImplTest { + + private SlackConfig slackConfig; + private SlackClient slackClient; + private Slack slack = mock(Slack.class); + private MethodsClient methodsClient; + private Appender appender; + + @Before + public void beforeEach() throws NoSuchFieldException, IllegalAccessException { + slackConfig = new SlackConfig(); + slackConfig.setApiToken("1234"); + methodsClient = mock(MethodsClient.class); + when(slack.methods()).thenReturn(methodsClient); + slackClient = new SlackClientImpl(slack); + appender = LoggingMockUtil.createMockedLogAppender(SlackClientImpl.class); + } + + @After + public void afterEach() throws NoSuchFieldException, IllegalAccessException { + LoggingMockUtil.destroyMockedAppender(appender, OpenIdValidator.class); + } + + @Test + public void shouldCallUsersLookupByEmail() throws Exception { + // given a user lookup request + UsersLookupByEmailRequest usersLookupByEmailRequest = UsersLookupByEmailRequest.builder() + .email("email@to.lookup") + .token(slackConfig.getApiToken()) + .build(); + + // when we look up user + UsersLookupByEmailResponse usersLookupByEmailResponse = new UsersLookupByEmailResponse(); + when(methodsClient.usersLookupByEmail(usersLookupByEmailRequest)).thenReturn(usersLookupByEmailResponse); + + UsersLookupByEmailResponse response = slackClient.usersLookupByEmail(usersLookupByEmailRequest).toBlocking().single(); + // then the real method should be invoked + verify(methodsClient, times(1)).usersLookupByEmail(usersLookupByEmailRequest); + // and the expected response is returned + assertThat(response).isEqualTo(usersLookupByEmailResponse); + } + + @Test + public void shouldCallChatPostMessage() throws Exception { + // given a user lookup request + ChatPostMessageRequest chatPostMessageRequest = ChatPostMessageRequest.builder() + .token(slackConfig.getApiToken()) + .build(); + + // when we look up user + ChatPostMessageResponse usersLookupByEmailResponse = new ChatPostMessageResponse(); + when(methodsClient.chatPostMessage(chatPostMessageRequest)).thenReturn(usersLookupByEmailResponse); + + ChatPostMessageResponse response = slackClient.chatPostMessage(chatPostMessageRequest).toBlocking().single(); + // then the real method should be invoked + verify(methodsClient, times(1)).chatPostMessage(chatPostMessageRequest); + // and the expected response is returned + assertThat(response).isEqualTo(usersLookupByEmailResponse); + } + + + @Test + public void shouldCallChannelsReplies() throws Exception { + // given a user lookup request + ChannelsRepliesRequest chatPostMessageRequest = ChannelsRepliesRequest.builder() + .token(slackConfig.getApiToken()) + .build(); + + // when we look up user + ChannelsRepliesResponse usersLookupByEmailResponse = new ChannelsRepliesResponse(); + when(methodsClient.channelsReplies(chatPostMessageRequest)).thenReturn(usersLookupByEmailResponse); + + ChannelsRepliesResponse response = slackClient.channelsReplies(chatPostMessageRequest).toBlocking().single(); + // then the real method should be invoked + verify(methodsClient, times(1)).channelsReplies(chatPostMessageRequest); + // and the expected response is returned + assertThat(response).isEqualTo(usersLookupByEmailResponse); + } + + @Test + public void shouldCallUsersInfo() throws Exception { + // given a user lookup request + UsersInfoRequest chatPostMessageRequest = UsersInfoRequest.builder() + .token(slackConfig.getApiToken()) + .build(); + + // when we look up user + UsersInfoResponse usersLookupByEmailResponse = new UsersInfoResponse(); + when(methodsClient.usersInfo(chatPostMessageRequest)).thenReturn(usersLookupByEmailResponse); + + UsersInfoResponse response = slackClient.usersInfo(chatPostMessageRequest).toBlocking().single(); + // then the real method should be invoked + verify(methodsClient, times(1)).usersInfo(chatPostMessageRequest); + // and the expected response is returned + assertThat(response).isEqualTo(usersLookupByEmailResponse); + } + + @Test + public void shouldLogOnIOException() throws Exception { + + // given a user lookup request + UsersInfoRequest chatPostMessageRequest = UsersInfoRequest.builder() + .token(slackConfig.getApiToken()) + .build(); + + // when we look up user on bad network + when(methodsClient.usersInfo(chatPostMessageRequest)).thenThrow(new IOException("connection reset by peer")); + + // when we asks slack for the message, then a exception should be returned + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(() -> slackClient.usersInfo(chatPostMessageRequest).toBlocking().single()) + .satisfies(e -> assertThat(e.getMessage()).isEqualTo("java.io.IOException: connection reset by peer")); + + // and we should log that io exception happened + verify(appender).doAppend(matches(log -> { + assertThat(log.getLevel().toString()).isEqualTo("ERROR"); + assertThat(log.getMessage().toString()).contains("Failed to communicate with slack"); + })); + } + + @Test + public void shouldLogOnExceptionFromSlack() throws Exception{ + // given a user lookup request + UsersInfoRequest chatPostMessageRequest = UsersInfoRequest.builder() + .token(slackConfig.getApiToken()) + .build(); + + // when we look up user a random error happens + when(methodsClient.usersInfo(chatPostMessageRequest)).thenThrow(new NullPointerException("random error")); + + // when we asks slack for the message, then a exception should be returned + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(() -> slackClient.usersInfo(chatPostMessageRequest).toBlocking().single()) + .satisfies(e -> assertThat(e.getMessage()).isEqualTo("random error")); + + // and we should log that the exception happened + verify(appender).doAppend(matches(log -> { + assertThat(log.getLevel().toString()).isEqualTo("WARN"); + assertThat(log.getMessage().toString()).contains("Got bad response while communicating with slack"); + })); + + } +} diff --git a/impl/src/test/java/slack/SlackResourceImplTest.java b/impl/src/test/java/slack/SlackResourceImplTest.java index 71fff66..362fb6c 100644 --- a/impl/src/test/java/slack/SlackResourceImplTest.java +++ b/impl/src/test/java/slack/SlackResourceImplTest.java @@ -6,106 +6,237 @@ import com.github.seratch.jslack.api.methods.response.users.UsersInfoResponse; import com.github.seratch.jslack.api.model.Message; import com.github.seratch.jslack.api.model.User; +import com.github.seratch.jslack.api.model.block.DividerBlock; +import com.github.seratch.jslack.api.model.block.LayoutBlock; +import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static rx.Observable.just; +import static slack.SlackResourceImpl.COULD_NOT_FETCH_USER_EMAIL_FROM_SLACK; +import static slack.SlackResourceImpl.COULD_NOT_GET_USER_BY_EMAIL_FROM_SLACK; +import static slack.SlackResourceImpl.MISSING_USER_EMAIL; public class SlackResourceImplTest { - @Test - public void handleUserInfoResponse() { - UsersInfoResponse usersInfoResponse = new UsersInfoResponse(); - usersInfoResponse.setOk(true); - - final User.Profile profile = new User.Profile(); - profile.setEmail("tst@a.b"); - - User user = new User(); - user.setProfile(profile); - - usersInfoResponse.setUser(user); + private SlackClient slackClient; + private SlackResource slackResource; + private SlackConfig slackConfig; + + @Before + public void shouldDoStuff() { + slackConfig = new SlackConfig(); + slackConfig.setApiToken("api-token"); + slackConfig.setBotUserToken("bot-token"); + slackClient = mock(SlackClient.class); + slackResource = new SlackResourceImpl(slackConfig, slackClient); + } - //Successful handling - assertThat(SlackResourceImpl.handleUserInfoResponse(usersInfoResponse)).isEqualTo(profile.getEmail()); + @Test + public void shouldReturnUserEmailBySlackId() { + // given that slack has the user we are asking for + UsersInfoResponse usersInfoResponse = createSuccessfulUserResponse(); + final String userId = usersInfoResponse.getUser().getId(); + final User.Profile profile = usersInfoResponse.getUser().getProfile(); + + when(slackClient.usersInfo(any())).thenReturn(just(usersInfoResponse)); + // when we ask slack for the users mail with given id + String foundEmail = slackResource.getUserEmail(userId).toBlocking().single(); + + // then we should get the mail address + assertThat(foundEmail).isEqualTo(profile.getEmail()); + } - //Not successful handling + @Test + public void shouldThrowExceptionIfUserCannotBeFetchedFromSlack() { + // given slack yields an error that a user cannot be fetched with given id + UsersInfoResponse usersInfoResponse = createSuccessfulUserResponse(); usersInfoResponse.setOk(false); usersInfoResponse.setError("error"); + final String userId = usersInfoResponse.getUser().getId(); + when(slackClient.usersInfo(any())).thenReturn(just(usersInfoResponse)); + + // when we try to get user email by slack Id + // then we should get exception assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> SlackResourceImpl.handleUserInfoResponse(usersInfoResponse)) - .satisfies(e -> assertThat(e.getMessage()).isEqualTo("Could not fetch user email from slack: error")); + .isThrownBy(() -> slackResource.getUserEmail(userId).toBlocking().single()) + .withMessage(COULD_NOT_FETCH_USER_EMAIL_FROM_SLACK+"error"); + } - //Missing scope + @Test + public void shouldThrowExceptionIfMissingScopes() { + // given slack yields an error that a user cannot be fetched due to missing scopes + UsersInfoResponse usersInfoResponse = createSuccessfulUserResponse(); usersInfoResponse.setOk(true); usersInfoResponse.getUser().getProfile().setEmail(null); + final String userId = usersInfoResponse.getUser().getId(); + when(slackClient.usersInfo(any())).thenReturn(just(usersInfoResponse)); + + // when we try to get user email by slack Id + // then we should get exception assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> SlackResourceImpl.handleUserInfoResponse(usersInfoResponse)) - .satisfies(e -> assertThat(e.getMessage()).isEqualTo("User email from slack is null and might be due to missing scope users:read.email")); + .isThrownBy(() -> slackResource.getUserEmail(userId).toBlocking().single()) + .withMessage(MISSING_USER_EMAIL); } + @Test - public void handleUserByEmailResponse() { - UsersLookupByEmailResponse response = new UsersLookupByEmailResponse(); - response.setOk(true); - final User user = new User(); - user.setId(UUID.randomUUID().toString()); - response.setUser(user); + public void shouldReturnSlackUserIdByEmail() { + // given that slack has the user we are asking for + UsersInfoResponse usersInfoResponse = createSuccessfulUserResponse(); + final String slackId = usersInfoResponse.getUser().getId(); + final User.Profile profile = usersInfoResponse.getUser().getProfile(); + + when(slackClient.usersInfo(any())).thenReturn(just(usersInfoResponse)); + // when we ask slack for the users mail with given id + String foundEmail = slackResource.getUserEmail(slackId).toBlocking().single(); + + // then we should get the mail address + assertThat(foundEmail).isEqualTo(profile.getEmail()); + } - //Successful - assertThat(SlackResourceImpl.handleUserByEmailResponse(response)).isEqualTo(user.getId()); + @Test + public void shouldGetUserEmailByUserId() { + // given a successful response from slack + UsersLookupByEmailResponse usersLookupByEmailResponse = createSuccessfulUsersLookupByEmailResponse(); + final User user = usersLookupByEmailResponse.getUser(); + when(slackClient.usersLookupByEmail(any())).thenReturn(just(usersLookupByEmailResponse)); + + //when we ask slack for userId by email + String userId = slackResource.getUserId("any.email@will.do").toBlocking().single(); + + // then we should get the userId + assertThat(userId).isEqualTo(user.getId()); + } + + @Test + public void shouldThrowExceptionWhenFailureToFetchEmail() { + UsersLookupByEmailResponse usersLookupByEmailResponse = createSuccessfulUsersLookupByEmailResponse(); - //Failure - response.setOk(false); - response.setError("error"); + usersLookupByEmailResponse.setOk(false); + usersLookupByEmailResponse.setError("error"); assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> { - SlackResourceImpl.handleUserByEmailResponse(response); - }).satisfies(e -> { - assertThat(e.getMessage()).isEqualTo("Could not get user by email from slack: error"); - }); + SlackResourceImpl.handleUserByEmailResponse(usersLookupByEmailResponse); + }).withMessage(COULD_NOT_GET_USER_BY_EMAIL_FROM_SLACK + "error"); } @Test - public void handleChatPostMessageResponse() { + public void shouldReturnEmptyWhenPostingMessageToSlack() { + // given that slack will accept our chat message ChatPostMessageResponse chatPostMessageResponse = new ChatPostMessageResponse(); chatPostMessageResponse.setOk(true); + when(slackClient.chatPostMessage(any())).thenReturn(just(chatPostMessageResponse)); - //Successful call should return empty - SlackResourceImpl.handleChatPostMessageResponse(chatPostMessageResponse) + //then call should return empty + slackResource.postMessageToSlack("channel","a message", "a threadId") .test() .awaitTerminalEvent() .assertNoErrors() .assertNoValues(); + } - //Failure call should return IllegalStateException + @Test + public void shouldThrowExceptionWhenFailurePostingMessageToSlack() { + // given that we will get a bad response from slack + ChatPostMessageResponse chatPostMessageResponse = new ChatPostMessageResponse(); chatPostMessageResponse.setOk(false); - SlackResourceImpl.handleChatPostMessageResponse(chatPostMessageResponse) + chatPostMessageResponse.setError("error"); + when(slackClient.chatPostMessage(any())).thenReturn(just(chatPostMessageResponse)); + + // then call should return exception + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> { + slackResource.postMessageToSlack("channel", "message","threadId").toBlocking().singleOrDefault(null); + }).withMessage("error"); + } + + @Test + public void shouldGetMessageByMessageId() { + // given a successful response from slack + ChannelsRepliesResponse channelsRepliesResponse = createSuccessfulChannelRepliesResponse(); + when(slackClient.channelsReplies(any())).thenReturn(just(channelsRepliesResponse)); + + //when we ask slack for messages + Message message = slackResource.getMessageFromSlack("channel", "messageId").toBlocking().single(); + + // then we should get the message + assertThat(message.getText()).isEqualTo(channelsRepliesResponse.getMessages().get(0).getText()); + } + + @Test + public void shouldThrowExceptionWhenFailureToRetriveMessage() { + ChannelsRepliesResponse channelsRepliesResponse = new ChannelsRepliesResponse(); + // given that we will get a bad response from slack + channelsRepliesResponse.setOk(false); + channelsRepliesResponse.setError("error"); + channelsRepliesResponse.setWarning("warning"); + when(slackClient.channelsReplies(any())).thenReturn(just(channelsRepliesResponse)); + // when we ask slack for the message, then a exception should be returned + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> slackResource.getMessageFromSlack("channel", "messageId").toBlocking().single()) + .withMessage("error warning"); + } + + @Test + public void shouldReturnEmptyIfSuccessInPostingToSlackAsBot() { + + // given a successful response from slack + ChatPostMessageResponse chatPostMessageResponse = new ChatPostMessageResponse(); + chatPostMessageResponse.setOk(true); + when(slackClient.chatPostMessage(any())).thenReturn(just(chatPostMessageResponse)); + + //when we ask slack for messages + List layoutBlocks = new ArrayList<>(); + layoutBlocks.add( new DividerBlock("123")); + + // then we should get the message and nothing should be returned + slackResource.postMessageToSlackAsBotUser("channel", layoutBlocks) .test() .awaitTerminalEvent() - .assertError(IllegalStateException.class); + .assertNoErrors() + .assertNoValues(); } - @Test - public void handleGetMessageResponse() { + + private static ChannelsRepliesResponse createSuccessfulChannelRepliesResponse() { ChannelsRepliesResponse channelsRepliesResponse = new ChannelsRepliesResponse(); channelsRepliesResponse.setOk(true); Message message = new Message(); message.setText(UUID.randomUUID().toString()); channelsRepliesResponse.setMessages(singletonList(message)); + return channelsRepliesResponse; + } - assertThat(SlackResourceImpl.handleGetMessageResponse(channelsRepliesResponse).getText()).isEqualTo(message.getText()); - channelsRepliesResponse.setOk(false); - channelsRepliesResponse.setError("error"); - channelsRepliesResponse.setWarning("warning"); + private static UsersInfoResponse createSuccessfulUserResponse() { + UsersInfoResponse usersInfoResponse = new UsersInfoResponse(); + usersInfoResponse.setOk(true); - assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> SlackResourceImpl.handleGetMessageResponse(channelsRepliesResponse)) - .satisfies(e -> { - assertThat(e.getMessage()).isEqualTo("error warning"); - }); + final User.Profile profile = new User.Profile(); + profile.setEmail("tst@a.b"); + + User user = new User(); + user.setProfile(profile); + + usersInfoResponse.setUser(user); + return usersInfoResponse; + } + + private static UsersLookupByEmailResponse createSuccessfulUsersLookupByEmailResponse() { + UsersLookupByEmailResponse response = new UsersLookupByEmailResponse(); + response.setOk(true); + final User user = new User(); + user.setId(UUID.randomUUID().toString()); + response.setUser(user); + return response; } + }