package me.moodcat.backend.rooms;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import me.moodcat.api.ProfanityChecker;
import me.moodcat.api.models.ChatMessageModel;
import me.moodcat.backend.BackendTest;
import me.moodcat.backend.UnitOfWorkSchedulingService;
import me.moodcat.backend.Vote;
import me.moodcat.database.controllers.RoomDAO;
import me.moodcat.database.controllers.SongDAO;
import me.moodcat.database.controllers.UserDAO;
import me.moodcat.database.entities.Room;
import me.moodcat.database.entities.Song;
import me.moodcat.database.entities.User;
import me.moodcat.util.JukitoRunnerSupportingMockAnnotations;
import me.moodcat.util.MockedUnitOfWorkSchedulingService;
import org.hamcrest.Matchers;
import org.jukito.UseModules;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
@RunWith(JukitoRunnerSupportingMockAnnotations.class)
@UseModules(RoomInstanceTest.RoomInstanceTestModule.class)
public class RoomInstanceTest extends BackendTest {
private static UserDAO userDAO = Mockito.mock(UserDAO.class);
private static RoomDAO roomDAO = Mockito.mock(RoomDAO.class);
private static SongDAO songDAO = Mockito.mock(SongDAO.class);
public static class RoomInstanceTestModule extends AbstractModule {
@Override
protected void configure() {
install(new RoomBackendModule());
bind(SongDAO.class).toInstance(songDAO);
bind(RoomDAO.class).toInstance(roomDAO);
bind(UserDAO.class).toInstance(userDAO);
bind(ProfanityChecker.class).toInstance(Mockito.mock(ProfanityChecker.class));
bind(UnitOfWorkSchedulingService.class).to(MockedUnitOfWorkSchedulingService.class);
}
}
private RoomInstance instance;
@Inject
private RoomInstanceFactory roomInstanceFactory;
@Inject
private MockedUnitOfWorkSchedulingService unitOfWorkSchedulingService;
private Song song;
private Room room;
private User user;
@Before
public void setUp() {
song = createSong(1);
room = createRoom(song);
user = createUser();
when(roomDAO.findById(room.getId())).thenReturn(room);
when(userDAO.findById(user.getId())).thenReturn(user);
instance = roomInstanceFactory.create(room);
}
@After
public void tearDown() {
unitOfWorkSchedulingService.shutdownNow();
}
@Test
public void whenTooManyMessagesRemoveOneFromList() {
for (int i = 0; i < RoomInstance.MAXIMAL_NUMBER_OF_CHAT_MESSAGES + 1; i++) {
ChatMessageModel model = new ChatMessageModel();
model.setMessage(String.valueOf(i));
instance.sendMessage(model, createUser());
}
assertThat(instance.getMessages(),
Matchers.iterableWithSize(RoomInstance.MAXIMAL_NUMBER_OF_CHAT_MESSAGES));
}
@Test(expected = IllegalArgumentException.class)
public void whenOneUserSendsMessagesTooFastThrowsException() {
ChatMessageModel model = new ChatMessageModel();
model.setMessage("Spam");
User user = mock(User.class);
when(user.getId()).thenReturn(1337);
for (int i = 0; i < 6; i++) {
instance.sendMessage(model, user);
}
}
@Test
public void testReplayHistoryOnNoResults() {
when(songDAO.findForDistance(room.getVaVector(), RoomInstance.NUMBER_OF_SELECTED_SONGS))
.thenReturn(Collections.emptyList());
assertThat(room.getPlayHistory(), Matchers.empty());
assertThat(room.getPlayQueue(), Matchers.empty());
assertEquals(song, room.getCurrentSong());
instance.playNext();
assertThat(room.getPlayHistory(), Matchers.empty());
assertThat(room.getPlayQueue(), Matchers.empty());
assertEquals(song, room.getCurrentSong());
}
@Test
public void testPlayNextSongFromResults() throws ExecutionException, InterruptedException {
Song newSong = createSong();
stubFindForDistance(room, newSong);
assertThat(room.getPlayHistory(), Matchers.empty());
assertThat(room.getPlayQueue(), Matchers.empty());
assertEquals(song, room.getCurrentSong());
instance.playNext().get();
instance.merge().get();
assertThat(room.getPlayHistory(), Matchers.contains(song));
assertThat(room.getPlayQueue(), Matchers.empty());
assertEquals(newSong, room.getCurrentSong());
}
@Test
public void testScheduleResults() throws ExecutionException, InterruptedException {
Song newSong = createSong(2);
Song newOtherSong = createSong(3);
stubFindForDistance(room, newSong, newOtherSong);
assertThat(room.getPlayHistory(), Matchers.empty());
assertThat(room.getPlayQueue(), Matchers.empty());
assertEquals(song, room.getCurrentSong());
instance.playNext().get();
instance.merge().get();
assertThat(room.getPlayHistory(), Matchers.contains(song));
assertThat(room.getPlayQueue(), Matchers.contains(newOtherSong));
assertEquals(newSong, room.getCurrentSong());
}
@Test
public void testLimitHistory() throws ExecutionException, InterruptedException {
List<Song> history = ImmutableList.copyOf(Stream.generate(BackendTest::createSong)
.limit(RoomInstance.NUMBER_OF_SELECTED_SONGS)
.iterator());
room.setPlayHistory(history);
Song newSong = createSong();
stubFindForDistance(room, newSong);
assertThat(room.getPlayQueue(), Matchers.empty());
assertEquals(song, room.getCurrentSong());
instance.playNext().get();
instance.merge().get();
List<Song> expectedHistory = Stream
.concat(history.stream().skip(1), Stream.of(song))
.collect(Collectors.toList());
assertEquals(expectedHistory, room.getPlayHistory());
assertThat(room.getPlayQueue(), Matchers.empty());
assertEquals(newSong, room.getCurrentSong());
}
private void stubFindForDistance(Room room, Song... songs) {
when(songDAO.findNewSongsFor(room)).thenReturn(Lists.newArrayList(songs));
}
@Test
public void testVote() {
User user = Mockito.mock(User.class);
instance.addVote(user, Vote.LIKE);
}
@Test(expected = IllegalArgumentException.class)
public void testDuplicateVote() {
User user = Mockito.mock(User.class);
instance.addVote(user, Vote.LIKE);
instance.addVote(user, Vote.DISLIKE);
}
}