package com.faforever.client.chat;
import com.faforever.client.i18n.I18n;
import com.faforever.client.net.ConnectionState;
import com.faforever.client.notification.NotificationService;
import com.faforever.client.notification.TransientNotification;
import com.faforever.client.player.PlayerService;
import com.faforever.client.preferences.ChatPrefs;
import com.faforever.client.preferences.Preferences;
import com.faforever.client.preferences.PreferencesService;
import com.faforever.client.remote.FafService;
import com.faforever.client.remote.domain.SocialMessage;
import com.faforever.client.task.CompletableTask;
import com.faforever.client.task.TaskService;
import com.faforever.client.test.AbstractPlainJavaFxTest;
import com.faforever.client.user.UserService;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.hash.Hashing;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.MapProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleMapProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.concurrent.Task;
import javafx.scene.paint.Color;
import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.pircbotx.Configuration;
import org.pircbotx.PircBotX;
import org.pircbotx.User;
import org.pircbotx.UserChannelDao;
import org.pircbotx.UserHostmask;
import org.pircbotx.UserLevel;
import org.pircbotx.hooks.Event;
import org.pircbotx.hooks.events.ActionEvent;
import org.pircbotx.hooks.events.ConnectEvent;
import org.pircbotx.hooks.events.DisconnectEvent;
import org.pircbotx.hooks.events.JoinEvent;
import org.pircbotx.hooks.events.MessageEvent;
import org.pircbotx.hooks.events.NoticeEvent;
import org.pircbotx.hooks.events.OpEvent;
import org.pircbotx.hooks.events.PartEvent;
import org.pircbotx.hooks.events.PrivateMessageEvent;
import org.pircbotx.hooks.events.QuitEvent;
import org.pircbotx.hooks.events.UserListEvent;
import org.pircbotx.output.OutputChannel;
import org.pircbotx.output.OutputIRC;
import org.pircbotx.snapshot.ChannelSnapshot;
import org.pircbotx.snapshot.UserChannelDaoSnapshot;
import org.pircbotx.snapshot.UserSnapshot;
import org.testfx.util.WaitForAsyncUtils;
import java.io.IOException;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import static com.faforever.client.chat.ChatColorMode.CUSTOM;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class PircBotXChatServiceTest extends AbstractPlainJavaFxTest {
public static final String CHAT_USER_NAME = "junit";
public static final String CHAT_PASSWORD = "123";
private static final InetAddress LOOPBACK_ADDRESS = InetAddress.getLoopbackAddress();
private static final long TIMEOUT = 300000;
private static final TimeUnit TIMEOUT_UNIT = TimeUnit.MILLISECONDS;
private static final String DEFAULT_CHANNEL_NAME = "#defaultChannel";
private static final String OTHER_CHANNEL_NAME = "#otherChannel";
private static final int IRC_SERVER_PORT = 123;
private PircBotXChatService instance;
private ChatUser chatUser1;
private ChatUser chatUser2;
@Mock
private User user1;
@Mock
private User user2;
@Mock
private org.pircbotx.Channel defaultChannel;
@Mock
private org.pircbotx.Channel otherChannel;
@Mock
private PircBotX pircBotX;
@Mock
private Configuration configuration;
@Mock
private UserChannelDaoSnapshot daoSnapshot;
@Mock
private OutputIRC outputIrc;
@Mock
private UserService userService;
@Mock
private TaskService taskService;
@Mock
private PreferencesService preferencesService;
@Mock
private NotificationService notificationService;
@Mock
private Preferences preferences;
@Mock
private ChatPrefs chatPrefs;
@Mock
private I18n i18n;
@Mock
private PircBotXFactory pircBotXFactory;
@Mock
private UserChannelDao<User, org.pircbotx.Channel> userChannelDao;
@Mock
private MapProperty<String, Color> userToColorProperty;
@Mock
private ObjectProperty<ChatColorMode> chatColorMode;
@Mock
private FafService fafService;
@Mock
private ThreadPoolExecutor threadPoolExecutor;
@Mock
private ChannelSnapshot defaultChannelSnapshot;
@Mock
private ChannelSnapshot otherChannelSnapshot;
@Mock
private PlayerService playerService;
@Mock
private PlayerInfoBean playerInfoBean;
@Captor
private ArgumentCaptor<Consumer<SocialMessage>> socialMessageListenerCaptor;
@Captor
private ArgumentCaptor<Configuration> configurationCaptor;
private CountDownLatch botShutdownLatch;
private CompletableFuture<Object> botStartedFuture;
@Before
public void setUp() throws Exception {
instance = new PircBotXChatService();
instance.fafService = fafService;
instance.userService = userService;
instance.taskService = taskService;
instance.playerService = playerService;
instance.notificationService = notificationService;
instance.i18n = i18n;
instance.pircBotXFactory = pircBotXFactory;
instance.preferencesService = preferencesService;
instance.threadPoolExecutor = threadPoolExecutor;
instance.defaultChannelName = DEFAULT_CHANNEL_NAME;
BooleanProperty loggedInProperty = new SimpleBooleanProperty();
botShutdownLatch = new CountDownLatch(1);
userToColorProperty = new SimpleMapProperty<>(FXCollections.observableHashMap());
chatColorMode = new SimpleObjectProperty<>(CUSTOM);
when(userService.getUsername()).thenReturn(CHAT_USER_NAME);
when(userService.getPassword()).thenReturn(CHAT_PASSWORD);
when(userService.loggedInProperty()).thenReturn(loggedInProperty);
when(defaultChannel.getName()).thenReturn(DEFAULT_CHANNEL_NAME);
when(defaultChannelSnapshot.getName()).thenReturn(DEFAULT_CHANNEL_NAME);
when(otherChannel.getName()).thenReturn(OTHER_CHANNEL_NAME);
when(otherChannelSnapshot.getName()).thenReturn(OTHER_CHANNEL_NAME);
when(pircBotX.getConfiguration()).thenReturn(configuration);
when(pircBotX.sendIRC()).thenReturn(outputIrc);
when(pircBotX.getUserChannelDao()).thenReturn(userChannelDao);
doAnswer(
invocation -> {
WaitForAsyncUtils.async(() -> invocation.getArgumentAt(0, Task.class).run());
return null;
}
).when(threadPoolExecutor).execute(any(Task.class));
doAnswer((InvocationOnMock invocation) -> {
@SuppressWarnings("unchecked")
CompletableTask<Void> task = invocation.getArgumentAt(0, CompletableTask.class);
task.run();
task.getFuture().complete(WaitForAsyncUtils.waitForAsyncFx(1000, task::getValue));
return task;
}).when(instance.taskService).submitTask(any());
botStartedFuture = new CompletableFuture<>();
doAnswer(invocation -> {
botStartedFuture.complete(true);
botShutdownLatch.await();
return null;
}).when(pircBotX).startBot();
when(pircBotXFactory.createPircBotX(any())).thenReturn(pircBotX);
instance.ircHost = LOOPBACK_ADDRESS.getHostAddress();
instance.ircPort = IRC_SERVER_PORT;
instance.reconnectDelay = 100;
when(preferencesService.getPreferences()).thenReturn(preferences);
when(preferences.getChat()).thenReturn(chatPrefs);
when(chatPrefs.getChatColorMode()).thenReturn(chatColorMode.get());
when(chatPrefs.getUserToColor()).thenReturn(userToColorProperty);
when(chatPrefs.userToColorProperty()).thenReturn(userToColorProperty);
when(chatPrefs.chatColorModeProperty()).thenReturn(chatColorMode);
when(user1.getNick()).thenReturn("user1");
when(user1.getChannels()).thenReturn(ImmutableSortedSet.of(defaultChannel));
when(user1.getUserLevels(defaultChannel)).thenReturn(ImmutableSortedSet.of(UserLevel.VOICE));
when(user2.getNick()).thenReturn("user2");
when(user2.getChannels()).thenReturn(ImmutableSortedSet.of(defaultChannel));
when(user2.getUserLevels(defaultChannel)).thenReturn(ImmutableSortedSet.of(UserLevel.VOICE));
chatUser1 = instance.getOrCreateChatUser(user1);
chatUser2 = instance.getOrCreateChatUser(user2);
instance.postConstruct();
verify(fafService).addOnMessageListener(eq(SocialMessage.class), socialMessageListenerCaptor.capture());
}
@After
public void tearDown() {
instance.close();
botShutdownLatch.countDown();
}
@Test
public void testOnChatUserList() throws Exception {
Channel channel = instance.getOrCreateChannel(DEFAULT_CHANNEL_NAME);
assertThat(channel.getUsers(), empty());
when(user1.compareTo(user2)).thenReturn(-1);
when(user2.compareTo(user1)).thenReturn(1);
connect();
CountDownLatch usersJoinedLatch = new CountDownLatch(2);
instance.addUsersListener(channel.getName(), change -> {
if (change.wasAdded()) {
usersJoinedLatch.countDown();
}
});
firePircBotXEvent(new UserListEvent(pircBotX, defaultChannel, ImmutableSortedSet.of(user1, user2), true));
assertTrue(usersJoinedLatch.await(TIMEOUT, TIMEOUT_UNIT));
assertThat(channel.getUsers(), hasSize(2));
assertThat(channel.getUser(chatUser1.getUsername()), sameInstance(chatUser1));
assertThat(channel.getUser(chatUser2.getUsername()), sameInstance(chatUser2));
}
private void connect() throws Exception {
CountDownLatch joinChannelLatch = new CountDownLatch(1);
doAnswer(invocation -> {
joinChannelLatch.countDown();
return null;
}).when(outputIrc).joinChannel(DEFAULT_CHANNEL_NAME);
instance.connect();
verify(pircBotXFactory).createPircBotX(configurationCaptor.capture());
CountDownLatch latch = listenForConnected();
firePircBotXEvent(new ConnectEvent(pircBotX));
assertTrue(latch.await(TIMEOUT, TIMEOUT_UNIT));
UserHostmask nickServHostMask = mock(UserHostmask.class);
when(nickServHostMask.getHostmask()).thenReturn("nickserv");
when(configuration.getNickservNick()).thenReturn("nickserv");
when(configuration.getNickservOnSuccess()).thenReturn("you are now");
firePircBotXEvent(new NoticeEvent(pircBotX, nickServHostMask, null, null, "", "you are now identified"));
SocialMessage socialMessage = new SocialMessage();
socialMessage.setChannels(Collections.emptyList());
socialMessageListenerCaptor.getValue().accept(socialMessage);
assertTrue(joinChannelLatch.await(TIMEOUT, TIMEOUT_UNIT));
}
private void firePircBotXEvent(Event event) {
configurationCaptor.getValue().getListenerManager().onEvent(event);
}
private CountDownLatch listenForConnected() {
CountDownLatch latch = new CountDownLatch(1);
instance.connectionStateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == ConnectionState.CONNECTED) {
latch.countDown();
}
});
return latch;
}
@Test
public void testOnUserJoinedChannel() throws Exception {
Channel channel = instance.getOrCreateChannel(DEFAULT_CHANNEL_NAME);
assertThat(channel.getUsers(), empty());
connect();
joinChannel(defaultChannel, user1);
joinChannel(defaultChannel, user2);
assertThat(channel.getUsers(), hasSize(2));
assertThat(channel.getUser(chatUser1.getUsername()), sameInstance(chatUser1));
assertThat(channel.getUser(chatUser2.getUsername()), sameInstance(chatUser2));
}
private void joinChannel(org.pircbotx.Channel channel, User user) throws Exception {
CompletableFuture<ChatUser> future = listenForUserJoined(channel);
firePircBotXEvent(createJoinEvent(channel, user));
future.get(TIMEOUT, TIMEOUT_UNIT);
}
private CompletableFuture<ChatUser> listenForUserJoined(org.pircbotx.Channel channel) {
CompletableFuture<ChatUser> future = new CompletableFuture<>();
instance.addUsersListener(channel.getName(), change -> {
if (change.wasAdded()) {
future.complete(change.getValueAdded());
}
});
return future;
}
@NotNull
private JoinEvent createJoinEvent(org.pircbotx.Channel channel, User user) {
return new JoinEvent(pircBotX, channel, user, user);
}
@Test
public void testOnChatUserLeftChannel() throws Exception {
Channel channel = instance.getOrCreateChannel(DEFAULT_CHANNEL_NAME);
assertThat(channel.getUsers(), empty());
connect();
joinChannel(defaultChannel, user1);
CompletableFuture<ChatUser> user2JoinedFuture = listenForUserJoined(defaultChannel);
firePircBotXEvent(createJoinEvent(defaultChannel, user2));
user2JoinedFuture.get(TIMEOUT, TIMEOUT_UNIT);
CompletableFuture<ChatUser> user1PartFuture = listenForUserParted(defaultChannel);
firePircBotXEvent(createPartEvent(defaultChannel, user1));
user1PartFuture.get(TIMEOUT, TIMEOUT_UNIT);
assertThat(channel.getUsers(), hasSize(1));
assertThat(channel.getUser(chatUser2.getUsername()), sameInstance(chatUser2));
}
private CompletableFuture<ChatUser> listenForUserParted(org.pircbotx.Channel channel) {
CompletableFuture<ChatUser> future = new CompletableFuture<>();
instance.addUsersListener(channel.getName(), change -> {
if (change.wasRemoved()) {
future.complete(change.getValueRemoved());
}
});
return future;
}
@NotNull
private PartEvent createPartEvent(org.pircbotx.Channel channel, User user) {
return new PartEvent(pircBotX, daoSnapshot, defaultChannelSnapshot, user, new UserSnapshot(user), "");
}
@Test
public void testOnChatUserQuit() throws Exception {
Channel channel = instance.getOrCreateChannel(DEFAULT_CHANNEL_NAME);
assertThat(channel.getUsers(), empty());
connect();
joinChannel(defaultChannel, user1);
joinChannel(defaultChannel, user2);
quit(user1);
assertThat(channel.getUsers(), hasSize(1));
assertThat(channel.getUser(chatUser2.getUsername()), sameInstance(chatUser2));
}
private void quit(User user) throws Exception {
CompletableFuture<ChatUser> future = listenForUserQuit();
firePircBotXEvent(createQuitEvent(user));
future.get(TIMEOUT, TIMEOUT_UNIT);
}
private CompletableFuture<ChatUser> listenForUserQuit() {
CompletableFuture<ChatUser> future = new CompletableFuture<>();
instance.addUsersListener(DEFAULT_CHANNEL_NAME, change -> {
if (change.wasRemoved()) {
future.complete(change.getValueRemoved());
}
});
return future;
}
private QuitEvent createQuitEvent(User user) {
return new QuitEvent(pircBotX, daoSnapshot, user, new UserSnapshot(user), "");
}
@Test
public void testAddOnMessageListenerWithMessage() throws Exception {
CompletableFuture<String> channelNameFuture = new CompletableFuture<>();
CompletableFuture<ChatMessage> chatMessageFuture = new CompletableFuture<>();
instance.addOnMessageListener(chatMessage -> {
channelNameFuture.complete(chatMessage.getSource());
chatMessageFuture.complete(chatMessage);
});
String message = "chat message";
Channel channel = mock(Channel.class);
when(channel.getName()).thenReturn(DEFAULT_CHANNEL_NAME);
connect();
CompletableFuture<ChatMessage> messageFuture = listenForMessage();
firePircBotXEvent(createMessageEvent(defaultChannel, user1, message));
messageFuture.get(TIMEOUT, TIMEOUT_UNIT);
assertThat(channelNameFuture.get(), is(DEFAULT_CHANNEL_NAME));
assertThat(chatMessageFuture.get().getMessage(), is(message));
assertThat(chatMessageFuture.get().getUsername(), is(chatUser1.getUsername()));
assertThat(chatMessageFuture.get().getTime(), is(greaterThan(Instant.ofEpochMilli(System.currentTimeMillis() - 1000))));
assertThat(chatMessageFuture.get().isAction(), is(false));
}
private CompletableFuture<ChatMessage> listenForMessage() {
CompletableFuture<ChatMessage> future = new CompletableFuture<>();
instance.addOnMessageListener(future::complete);
return future;
}
private MessageEvent createMessageEvent(org.pircbotx.Channel channel, User user, String message) {
return new MessageEvent(pircBotX, channel, channel.getName(), user, user, message, null);
}
@Test
public void testAddOnMessageListenerWithAction() throws Exception {
String action = "chat action";
connect();
CompletableFuture<ChatMessage> messageFuture = listenForMessage();
firePircBotXEvent(createActionEvent(defaultChannel, user1, action));
messageFuture.get(TIMEOUT, TIMEOUT_UNIT);
assertThat(messageFuture.get().getSource(), is(defaultChannel.getName()));
assertThat(messageFuture.get().getMessage(), is(action));
assertThat(messageFuture.get().getUsername(), is(chatUser1.getUsername()));
assertThat(messageFuture.get().getTime(), is(greaterThan(Instant.ofEpochMilli(System.currentTimeMillis() - 10_000))));
assertThat(messageFuture.get().isAction(), is(true));
}
private ActionEvent createActionEvent(org.pircbotx.Channel channel, User user, String action) {
return new ActionEvent(pircBotX, user, user, channel, channel.getName(), action);
}
@Test
public void testAddOnPrivateChatMessageListener() throws Exception {
CompletableFuture<String> usernameFuture = new CompletableFuture<>();
CompletableFuture<ChatMessage> chatMessageFuture = new CompletableFuture<>();
instance.addOnPrivateChatMessageListener(chatMessage -> {
usernameFuture.complete(chatMessage.getSource());
chatMessageFuture.complete(chatMessage);
});
String message = "private message";
User user = mock(User.class);
when(user.getNick()).thenReturn(chatUser1.getUsername());
Channel channel = mock(Channel.class);
when(channel.getName()).thenReturn(DEFAULT_CHANNEL_NAME);
connect();
firePircBotXEvent(createPrivateMessageEvent(user, message));
assertThat(chatMessageFuture.get().getMessage(), is(message));
assertThat(chatMessageFuture.get().getUsername(), is(chatUser1.getUsername()));
assertThat(chatMessageFuture.get().getTime(), is(greaterThan(Instant.ofEpochMilli(System.currentTimeMillis() - 1000))));
assertThat(chatMessageFuture.get().isAction(), is(false));
}
private PrivateMessageEvent createPrivateMessageEvent(User sender, String message) {
return new PrivateMessageEvent(pircBotX, sender, sender, message);
}
@Test
public void testAddOnChatConnectedListener() throws Exception {
CompletableFuture<Boolean> onChatConnectedFuture = new CompletableFuture<>();
instance.connectionStateProperty().addListener((observable, oldValue, newValue) -> {
switch (newValue) {
case CONNECTED:
onChatConnectedFuture.complete(null);
break;
}
});
String password = "123";
when(userService.getPassword()).thenReturn(password);
connect();
assertThat(onChatConnectedFuture.get(TIMEOUT, TIMEOUT_UNIT), is(nullValue()));
}
@Test
public void testAddOnChatDisconnectedListener() throws Exception {
CompletableFuture<Void> onChatDisconnectedFuture = new CompletableFuture<>();
instance.connectionStateProperty().addListener((observable, oldValue, newValue) -> {
switch (newValue) {
case DISCONNECTED:
onChatDisconnectedFuture.complete(null);
break;
}
});
connect();
CompletableFuture<Void> future = listenForDisconnected();
firePircBotXEvent(new DisconnectEvent(pircBotX, daoSnapshot, null));
future.get(TIMEOUT, TIMEOUT_UNIT);
onChatDisconnectedFuture.get(TIMEOUT, TIMEOUT_UNIT);
}
private CompletableFuture<Void> listenForDisconnected() {
CompletableFuture<Void> future = new CompletableFuture<>();
instance.connectionStateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == ConnectionState.DISCONNECTED) {
future.complete(null);
}
});
return future;
}
@Test
public void testAddOnModeratorSetListener() throws Exception {
connect();
joinChannel(defaultChannel, user1);
assertThat(chatUser1.getModeratorInChannels(), empty());
CompletableFuture<Set<String>> user1OpEvent = listenForUserOp(chatUser1);
firePircBotXEvent(createOpEvent(defaultChannel, user1));
user1OpEvent.get(TIMEOUT, TIMEOUT_UNIT);
ObservableSet<String> moderatorInChannels = chatUser1.getModeratorInChannels();
assertThat(moderatorInChannels, hasSize(1));
assertThat(moderatorInChannels.iterator().next(), is(DEFAULT_CHANNEL_NAME));
}
@SuppressWarnings("unchecked")
private CompletableFuture<Set<String>> listenForUserOp(ChatUser chatUser) {
CompletableFuture<Set<String>> future = new CompletableFuture<>();
chatUser.getModeratorInChannels().addListener((SetChangeListener<String>) change -> {
if (change.wasAdded()) {
future.complete((Set<String>) change.getSet());
}
});
return future;
}
private OpEvent createOpEvent(org.pircbotx.Channel channel, User user) {
return new OpEvent(pircBotX, channel, user, user, user, user, true);
}
@Test
@SuppressWarnings("unchecked")
public void testConnect() throws Exception {
ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
when(userService.getUid()).thenReturn(681);
connect();
botStartedFuture.get(TIMEOUT, TIMEOUT_UNIT);
verify(pircBotX).startBot();
verify(pircBotXFactory).createPircBotX(captor.capture());
Configuration configuration = captor.getValue();
assertThat(configuration.getName(), is(CHAT_USER_NAME));
assertThat(configuration.getLogin(), is("681"));
assertThat(configuration.getRealName(), is(CHAT_USER_NAME));
assertThat(configuration.getServers().get(0).getHostname(), is(LOOPBACK_ADDRESS.getHostAddress()));
assertThat(configuration.getServers().get(0).getPort(), is(IRC_SERVER_PORT));
}
@Test
public void testReconnect() throws Exception {
CompletableFuture<Boolean> firstStartFuture = new CompletableFuture<>();
CompletableFuture<Boolean> secondStartFuture = new CompletableFuture<>();
doAnswer(invocation -> {
if (!firstStartFuture.isDone()) {
firstStartFuture.complete(true);
throw new IOException("test exception");
}
secondStartFuture.complete(true);
botShutdownLatch.await();
return null;
}).when(pircBotX).startBot();
connect();
firstStartFuture.get(TIMEOUT, TIMEOUT_UNIT);
secondStartFuture.get(TIMEOUT, TIMEOUT_UNIT);
verify(pircBotX, times(2)).startBot();
}
@Test
@SuppressWarnings("unchecked")
public void testSendMessageInBackground() throws Exception {
connect();
String message = "test message";
CompletableFuture<String> future = instance.sendMessageInBackground(DEFAULT_CHANNEL_NAME, message).toCompletableFuture();
assertThat(future.get(TIMEOUT, TIMEOUT_UNIT), is(message));
verify(outputIrc).message(DEFAULT_CHANNEL_NAME, message);
}
@Test
public void testGetChatUsersForChannelEmpty() throws Exception {
Channel channel = instance.getOrCreateChannel(DEFAULT_CHANNEL_NAME);
assertThat(channel.getUsers(), empty());
}
@Test
public void testGetChatUsersForChannelTwoUsersInDifferentChannel() throws Exception {
connect();
joinChannel(defaultChannel, user1);
joinChannel(otherChannel, user2);
List<ChatUser> usersInDefaultChannel = instance.getOrCreateChannel(DEFAULT_CHANNEL_NAME).getUsers();
assertThat(usersInDefaultChannel, hasSize(1));
assertThat(usersInDefaultChannel.iterator().next(), sameInstance(chatUser1));
List<ChatUser> usersInOtherChannel = instance.getOrCreateChannel(OTHER_CHANNEL_NAME).getUsers();
assertThat(usersInOtherChannel, hasSize(1));
assertThat(usersInOtherChannel.iterator().next(), sameInstance(chatUser2));
}
@Test
public void testGetChatUsersForChannelTwoUsersInSameChannel() throws Exception {
connect();
joinChannel(defaultChannel, user1);
joinChannel(defaultChannel, user2);
Channel channel = instance.getOrCreateChannel(DEFAULT_CHANNEL_NAME);
List<ChatUser> users = channel.getUsers();
assertThat(users, hasSize(2));
assertThat(users, containsInAnyOrder(chatUser1, chatUser2));
}
@Test
public void testAddChannelUserListListener() throws Exception {
connect();
@SuppressWarnings("unchecked")
MapChangeListener<String, ChatUser> listener = mock(MapChangeListener.class);
instance.addUsersListener(DEFAULT_CHANNEL_NAME, listener);
joinChannel(defaultChannel, user1);
joinChannel(defaultChannel, user2);
verify(listener, times(2)).onChanged(any());
}
@Test
public void testLeaveChannel() throws Exception {
OutputChannel outputChannel = mock(OutputChannel.class);
when(userChannelDao.getChannel(DEFAULT_CHANNEL_NAME)).thenReturn(defaultChannel);
when(defaultChannel.send()).thenReturn(outputChannel);
instance.connect();
instance.leaveChannel(DEFAULT_CHANNEL_NAME);
verify(outputChannel).part();
}
@Test
public void testSendActionInBackground() throws Exception {
connect();
String action = "test action";
CompletableFuture<String> future = instance.sendActionInBackground(DEFAULT_CHANNEL_NAME, action).toCompletableFuture();
assertThat(future.get(TIMEOUT, TIMEOUT_UNIT), is(action));
verify(outputIrc).action(DEFAULT_CHANNEL_NAME, action);
}
@Test
public void testUsersListenerJoinPart() throws Exception {
connect();
CompletableFuture<ChatUser> userJoinedFuture = listenForUserJoined(defaultChannel);
CompletableFuture<ChatUser> userPartFuture = listenForUserParted(defaultChannel);
firePircBotXEvent(createJoinEvent(defaultChannel, user1));
assertThat(userJoinedFuture.get(TIMEOUT, TIMEOUT_UNIT), is(chatUser1));
firePircBotXEvent(createPartEvent(defaultChannel, user1));
assertThat(userPartFuture.get(TIMEOUT, TIMEOUT_UNIT), is(chatUser1));
}
@Test
public void testUsersListenerJoinQuit() throws Exception {
connect();
CompletableFuture<ChatUser> userJoinedFuture = listenForUserJoined(defaultChannel);
firePircBotXEvent(createJoinEvent(defaultChannel, user1));
assertThat(userJoinedFuture.get(TIMEOUT, TIMEOUT_UNIT), is(chatUser1));
CompletableFuture<ChatUser> userQuitFuture = listenForUserParted(defaultChannel);
firePircBotXEvent(createQuitEvent(user1));
assertThat(userQuitFuture.get(TIMEOUT, TIMEOUT_UNIT), is(chatUser1));
}
@Test
public void testJoinChannel() throws Exception {
reset(taskService);
doAnswer((InvocationOnMock invocation) -> {
@SuppressWarnings("unchecked")
CompletableTask<Void> task = invocation.getArgumentAt(0, CompletableTask.class);
task.getFuture().complete(null);
return task;
}).when(instance.taskService).submitTask(any());
connect();
botStartedFuture.get(TIMEOUT, TIMEOUT_UNIT);
instance.connectionStateProperty().set(ConnectionState.CONNECTED);
String channelToJoin = "#anotherChannel";
instance.joinChannel(channelToJoin);
verify(outputIrc).joinChannel(channelToJoin);
}
@Test
public void testIsDefaultChannel() throws Exception {
assertTrue(instance.isDefaultChannel(DEFAULT_CHANNEL_NAME));
}
@Test
public void testRegisterOnNotRegisteredNotice() throws Exception {
String password = "123";
when(userService.getPassword()).thenReturn(password);
connect();
botStartedFuture.get(TIMEOUT, TIMEOUT_UNIT);
UserHostmask nickServHostMask = mock(UserHostmask.class);
when(nickServHostMask.getHostmask()).thenReturn("nickserv");
firePircBotXEvent(new NoticeEvent(pircBotX, nickServHostMask, null, null, "", "User foo isn't registered"));
instance.connectionStateProperty().set(ConnectionState.CONNECTED);
String md5Password = Hashing.md5().hashString(password, StandardCharsets.UTF_8).toString();
verify(outputIrc, timeout(100)).message("NickServ", String.format("register %s junit@users.faforever.com", md5Password));
}
@Test
public void testOnDisconnected() throws Exception {
connect();
joinChannel(defaultChannel, user1);
assertThat(instance.getOrCreateChannel(DEFAULT_CHANNEL_NAME).getUsers(), hasSize(1));
instance.connectionStateProperty().set(ConnectionState.DISCONNECTED);
assertThat(instance.getOrCreateChannel(DEFAULT_CHANNEL_NAME).getUsers(), empty());
}
@Test
public void testClose() {
instance.close();
}
@Test
public void testCreateOrGetChatUserStringPopulatedMap() throws Exception {
ChatUser addedUser = instance.getOrCreateChatUser(chatUser1.getUsername());
ChatUser returnedUser = instance.getOrCreateChatUser(chatUser1.getUsername());
assertThat(returnedUser, is(addedUser));
assertEquals(returnedUser, addedUser);
}
@Test
public void testCreateOrGetChatUserUserObjectPopulatedMap() throws Exception {
ChatUser addedUser = instance.getOrCreateChatUser(user1);
ChatUser returnedUser = instance.getOrCreateChatUser(user1);
assertThat(returnedUser, is(addedUser));
assertEquals(returnedUser, addedUser);
}
@Test
public void getOrCreateChatUserFoeNoNotification() throws Exception {
when(playerService.getPlayerForUsername(anyString())).thenReturn(playerInfoBean);
when(playerInfoBean.getSocialStatus()).thenReturn(SocialStatus.FOE);
when(playerInfoBean.getId()).thenReturn(1);
instance.getOrCreateChatUser(CHAT_USER_NAME);
verify(notificationService, never()).addNotification(any(TransientNotification.class));
}
@Test
public void testRejoinChannel() throws Exception {
OutputChannel outputChannel = mock(OutputChannel.class);
doAnswer((InvocationOnMock invocation) -> {
@SuppressWarnings("unchecked")
CompletableTask<Void> task = invocation.getArgumentAt(0, CompletableTask.class);
task.getFuture().complete(null);
return task;
}).when(instance.taskService).submitTask(any());
String channelToJoin = OTHER_CHANNEL_NAME;
when(userService.getUsername()).thenReturn("user1");
when(userChannelDao.getChannel(channelToJoin)).thenReturn(otherChannel);
when(otherChannel.send()).thenReturn(outputChannel);
doAnswer(invocation -> {
firePircBotXEvent(createJoinEvent(otherChannel, user1));
return null;
}).when(outputIrc).joinChannel(channelToJoin);
doAnswer(invocation -> {
firePircBotXEvent(createPartEvent(otherChannel, user1));
return null;
}).when(outputChannel).part();
connect();
botStartedFuture.get(TIMEOUT, TIMEOUT_UNIT);
instance.connectionStateProperty().set(ConnectionState.CONNECTED);
CountDownLatch firstJoinLatch = new CountDownLatch(1);
CountDownLatch secondJoinLatch = new CountDownLatch(1);
CountDownLatch leaveLatch = new CountDownLatch(1);
instance.addChannelsListener(change -> {
if (change.wasAdded()) {
if (firstJoinLatch.getCount() > 0) {
firstJoinLatch.countDown();
} else {
secondJoinLatch.countDown();
}
} else if (change.wasRemoved()) {
leaveLatch.countDown();
}
});
instance.joinChannel(channelToJoin);
assertTrue(firstJoinLatch.await(TIMEOUT, TIMEOUT_UNIT));
instance.leaveChannel(channelToJoin);
assertTrue(leaveLatch.await(TIMEOUT, TIMEOUT_UNIT));
instance.joinChannel(channelToJoin);
assertTrue(secondJoinLatch.await(TIMEOUT, TIMEOUT_UNIT));
}
@Test
public void testOnModeratorJoined() throws Exception {
connect();
User moderator = mock(User.class);
when(moderator.getNick()).thenReturn("moderator");
when(moderator.getChannels()).thenReturn(ImmutableSortedSet.of(defaultChannel));
when(moderator.getUserLevels(defaultChannel)).thenReturn(ImmutableSortedSet.of(UserLevel.OWNER));
joinChannel(defaultChannel, moderator);
firePircBotXEvent(createJoinEvent(defaultChannel, moderator));
ChatUser chatUserModerator = instance.getOrCreateChatUser(moderator.getNick());
assertTrue(chatUserModerator.moderatorInChannelsProperty().getValue().contains(DEFAULT_CHANNEL_NAME));
}
}