package org.atlasapi.persistence.content.schedule.mongo; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import com.metabroadcast.applications.client.model.internal.Application; import com.metabroadcast.applications.client.model.internal.ApplicationConfiguration; import com.metabroadcast.common.stream.MoreCollectors; import com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider; import org.atlasapi.application.v3.DefaultApplication; import org.atlasapi.media.channel.Channel; import org.atlasapi.media.channel.ChannelResolver; import org.atlasapi.media.entity.Brand; import org.atlasapi.media.entity.Broadcast; import org.atlasapi.media.entity.Content; import org.atlasapi.media.entity.Encoding; import org.atlasapi.media.entity.Episode; import org.atlasapi.media.entity.Identified; import org.atlasapi.media.entity.Item; import org.atlasapi.media.entity.Location; import org.atlasapi.media.entity.MediaType; import org.atlasapi.media.entity.Publisher; import org.atlasapi.media.entity.Schedule; import org.atlasapi.media.entity.Schedule.ScheduleChannel; import org.atlasapi.media.entity.ScheduleEntry; import org.atlasapi.media.entity.ScheduleEntry.ItemRefAndBroadcast; import org.atlasapi.media.entity.Version; import org.atlasapi.messaging.v3.ScheduleUpdateMessage; import org.atlasapi.output.Annotation; import org.atlasapi.persistence.channels.DummyChannelResolver; import org.atlasapi.persistence.content.ContentResolver; import org.atlasapi.persistence.content.EquivalentContent; import org.atlasapi.persistence.content.EquivalentContentResolver; import org.atlasapi.persistence.testing.StubContentResolver; import com.metabroadcast.common.persistence.MongoTestHelper; import com.metabroadcast.common.persistence.mongo.DatabasedMongo; import com.metabroadcast.common.queue.MessageSender; import com.metabroadcast.common.queue.MessagingException; import com.metabroadcast.common.time.DateTimeZones; import com.google.common.base.Optional; import com.google.common.collect.Collections2; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import org.joda.time.DateTime; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isIn; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class MongoScheduleStoreTest { private static class StubEquivalentContentResolver implements EquivalentContentResolver { private final Multimap<String, Content> content = HashMultimap.create(); public StubEquivalentContentResolver respondsTo(Content...contents) { ImmutableSet<Content> set = ImmutableSet.copyOf(contents); for (Content content : contents) { this.content.putAll(content.getCanonicalUri(), set); } return this; } @Override public EquivalentContent resolveUris( Iterable<String> uris, Application application, Set<Annotation> activeAnnotations, boolean withAliases ) { EquivalentContent.Builder builder = EquivalentContent.builder(); for (String uri : uris) { builder.putEquivalents(uri, copy(content.get(uri))); } return builder.build(); } private Iterable<Content> copy(Collection<Content> collection) { return Iterables.transform(collection, input -> (Content) input.copy()); } @Override public EquivalentContent resolveIds( Iterable<Long> ids, Application application, Set<Annotation> activeAnnotations ) { throw new UnsupportedOperationException(); } @Override public EquivalentContent resolveAliases( Optional<String> namespace, Iterable<String> values, Application application, Set<Annotation> activeAnnotations ) { throw new UnsupportedOperationException(); } } private MongoScheduleStore store; private final Channel BBC_ONE = new Channel(Publisher.BBC, "BBC One", "bbcone", false, MediaType.VIDEO, "http://www.bbc.co.uk/bbcone"); private final Channel BBC_TWO = new Channel(Publisher.BBC, "BBC Two", "bbctwo", false, MediaType.VIDEO, "http://www.bbc.co.uk/bbctwo"); private final Channel Channel_4_HD = new Channel(Publisher.C4, "Channel 4", "channel4", false, MediaType.VIDEO, "http://www.channel4.com"); private final Channel AL_JAZEERA_ENGLISH = new Channel(Publisher.C4, "Al Jazeera English", "aljazeera", false, MediaType.VIDEO, "http://www.aljazeera.com/"); private final DateTime now = new DateTime(DateTimeZones.UTC); private final Broadcast broadcast1 = new Broadcast(BBC_ONE.getUri(), now.minusHours(4), now.minusHours(2)); private final Broadcast broadcast2 = new Broadcast(BBC_TWO.getUri(), now.minusHours(4), now.minusHours(1)); private final Broadcast broadcast3 = new Broadcast(BBC_ONE.getUri(), now.minusHours(2), now.minusHours(1)); private final Broadcast broadcast4 = new Broadcast(BBC_TWO.getUri(), now.minusHours(1), now); private final Broadcast veryOldBroadcast = new Broadcast(BBC_TWO.getUri(), now.minusYears(1).minusHours(1), now.minusYears(1)); private final Version version1 = new Version(); private final Version version2 = new Version(); private final Version version3 = new Version(); private final Item item1 = new Item("item1", "item1", Publisher.BBC); private final Item item2 = new Episode("item2", "item2", Publisher.BBC); private final Item item3 = new Episode("item3", "item3", Publisher.BBC); private final Brand brand1 = new Brand("brand1", "brand1", Publisher.BBC); private final Location availableLocation = new Location(); private final Location unavailableLocation = new Location(); private final Encoding encoding = new Encoding(); private long when = System.currentTimeMillis(); private DatabasedMongo database; private final StubEquivalentContentResolver equivContentResolver = new StubEquivalentContentResolver(); private StubContentResolver contentResolver = new StubContentResolver(); private MessageSender<ScheduleUpdateMessage> ms = new MessageSender<ScheduleUpdateMessage>(){ @Override public void close() throws Exception { } @Override public void sendMessage(ScheduleUpdateMessage message) throws MessagingException { } @Override public void sendMessage(ScheduleUpdateMessage scheduleUpdateMessage, byte[] bytes) throws MessagingException { } }; private Application application = mock(Application.class); @Before public void setUp() throws Exception { database = MongoTestHelper.anEmptyTestDatabase(); availableLocation.setAvailable(true); unavailableLocation.setAvailable(false); encoding.addAvailableAt(availableLocation); encoding.addAvailableAt(unavailableLocation); version1.addBroadcast(broadcast1); version1.addManifestedAs(encoding); version2.addBroadcast(broadcast2); version2.addManifestedAs(encoding); version3.addBroadcast(broadcast3); version3.addManifestedAs(encoding); version3.addBroadcast(broadcast4); item1.addVersion(version1); item1.setContainer(brand1); item2.addVersion(version2); item2.addVersion(version3); when = System.currentTimeMillis(); equivContentResolver .respondsTo(item1) .respondsTo(item2) .respondsTo(item3); contentResolver .respondTo(item1) .respondTo(item2) .respondTo(item3); ChannelResolver channelResolver = new DummyChannelResolver(ImmutableList.of(BBC_ONE, BBC_TWO, Channel_4_HD, AL_JAZEERA_ENGLISH)); store = new MongoScheduleStore(database, channelResolver, contentResolver, equivContentResolver, ms); } @After public void tearDown() { System.out.println("Completed in "+(System.currentTimeMillis()-when)+" millis"); } @Test public void shouldSaveItemsAndRetrieveSchedule() throws Exception { store.writeScheduleFrom(item1); store.writeScheduleFrom(item2); Set<Publisher> publishers = ImmutableSet.of(Publisher.BBC); when(application.getConfiguration()).thenReturn(configWithSources(publishers)); Schedule schedule = store.schedule(now.minusHours(4), now, ImmutableSet.of(BBC_ONE, BBC_TWO), publishers, Optional.of(application)); assertSchedule(schedule); } @Test public void testShouldNotIgnoreBroadcastsOverAYearOld() throws Exception { Item item = itemWithBroadcast("id", new Broadcast(BBC_ONE.getUri(), now.withHourOfDay(1).withMinuteOfHour(10), now.withHourOfDay(1).withMinuteOfHour(20))); store.writeScheduleFrom(item); assertEquals(3, Iterables.size(database.collection("schedule").find())); } private Item itemWithBroadcast(String id, Broadcast broadcast) { Version version = new Version(); version.addBroadcast(veryOldBroadcast); version.addBroadcast(broadcast); Item item = new Item(id, "", Publisher.BBC); item.addVersion(version); return item; } @Test public void shouldSaveItemsAndRetrieveScheduleWithLoadsOfPublishers() throws Exception { store.writeScheduleFrom(item1); store.writeScheduleFrom(item2); Set<Publisher> publishers = ImmutableSet.of(Publisher.BBC, Publisher.C4, Publisher.ITV); when(application.getConfiguration()).thenReturn(configWithSources(publishers)); Schedule schedule = store.schedule(now.minusHours(4), now, ImmutableSet.of(BBC_ONE, BBC_TWO), publishers, Optional.of(application)); assertSchedule(schedule); } @Test public void bothItemsShouldExistInTheSameScheduleBucket(){ BBC_ONE.setId(1234L); DateTime broadcast1Start = now.withMinuteOfHour(5); DateTime broadcast1End = broadcast1Start.plusMinutes(25); Broadcast b1 = new Broadcast(BBC_ONE.getUri(), broadcast1Start, broadcast1End); DateTime broadcast2End = broadcast1End.plusMinutes(25); Broadcast b2 = new Broadcast(BBC_ONE.getUri(), broadcast1End, broadcast2End); Version v1 = new Version(); Version v2 = new Version(); v1.addBroadcast(b1); v2.addBroadcast(b2); item1.addVersion(v1); item2.addVersion(v2); ItemRefAndBroadcast itemRefAndBroadcast1 = new ItemRefAndBroadcast(item1, b1); ItemRefAndBroadcast itemRefAndBroadcast2 = new ItemRefAndBroadcast(item2, b2); store.replaceScheduleBlock(Publisher.BBC, BBC_ONE, Arrays.asList(itemRefAndBroadcast1)); when(application.getConfiguration()).thenReturn(configWithSources(ImmutableSet.of(Publisher.BBC))); Schedule schedule1 = store.schedule( broadcast1Start, broadcast2End.plusMinutes(10), ImmutableSet.of(BBC_ONE), ImmutableSet.of(Publisher.BBC), Optional.of(application) ); assertEquals(1, schedule1.scheduleChannels().size()); ScheduleChannel channel1 = Iterables.getOnlyElement(schedule1.scheduleChannels()); assertEquals(1, channel1.items().size()); store.replaceScheduleBlock(Publisher.BBC, BBC_ONE, Arrays.asList(itemRefAndBroadcast2)); Schedule schedule2 = store.schedule( broadcast1Start, broadcast2End.plusMinutes(10), ImmutableSet.of(BBC_ONE), ImmutableSet.of(Publisher.BBC), Optional.of(application) ); assertEquals(1, schedule2.scheduleChannels().size()); ScheduleChannel channel2 = Iterables.getOnlyElement(schedule2.scheduleChannels()); assertEquals(2, channel2.items().size()); } @Test public void shouldReplaceScheduleBlock() throws Exception { Channel_4_HD.setId(1234L); DateTime broadcast1Start = now.withMinuteOfHour(20); DateTime broadcast1End = broadcast1Start.plusMinutes(30); Broadcast b1 = new Broadcast(Channel_4_HD.getUri(), broadcast1Start, broadcast1End); DateTime broadcast2End = broadcast1End.plusMinutes(45); Broadcast b2 = new Broadcast(Channel_4_HD.getUri(), broadcast1End, broadcast2End); DateTime broadcast3End = broadcast2End.plusMinutes(60); Broadcast b3 = new Broadcast(Channel_4_HD.getUri(), broadcast2End, broadcast3End); Version v1 = new Version(); Version v2 = new Version(); Version v3 = new Version(); v1.addBroadcast(b1); v2.addBroadcast(b2); v3.addBroadcast(b3); item1.addVersion(v1); item2.addVersion(v2); item3.addVersion(v3); List<ItemRefAndBroadcast> itemsAndBroadcasts = Lists.newArrayList(); itemsAndBroadcasts.add(new ItemRefAndBroadcast(item1, b1)); itemsAndBroadcasts.add(new ItemRefAndBroadcast(item2, b2)); itemsAndBroadcasts.add(new ItemRefAndBroadcast(item3, b3)); store.replaceScheduleBlock(Publisher.BBC, Channel_4_HD, itemsAndBroadcasts); Set<Publisher> publishers = ImmutableSet.of(Publisher.BBC); when(application.getConfiguration()).thenReturn(configWithSources(publishers)); Schedule schedule = store.schedule(broadcast1Start, broadcast3End.plusMinutes(10), ImmutableSet.of(Channel_4_HD), publishers, Optional.of(application)); assertEquals(1, schedule.scheduleChannels().size()); ScheduleChannel channel = Iterables.getOnlyElement(schedule.scheduleChannels()); assertEquals(3, channel.items().size()); assertEquals(item1.getCanonicalUri(), channel.items().get(0).getCanonicalUri()); assertEquals(item2.getCanonicalUri(), channel.items().get(1).getCanonicalUri()); assertEquals(item3.getCanonicalUri(), channel.items().get(2).getCanonicalUri()); // Now replace the item being broadcast in the b2 slot Item item4 = itemWithBroadcast("item4", b2); equivContentResolver.respondsTo(item4); List<ItemRefAndBroadcast> replacementItemAndBcast = Lists.newArrayList(); replacementItemAndBcast.add(new ItemRefAndBroadcast(item4, b2)); store.replaceScheduleBlock(Publisher.BBC, Channel_4_HD, replacementItemAndBcast); Schedule updatedSchedule = store.schedule(broadcast1Start, broadcast3End.plusMinutes(10), ImmutableSet.of(Channel_4_HD), ImmutableSet.of(Publisher.BBC), Optional.of(application)); assertEquals(1, updatedSchedule.scheduleChannels().size()); ScheduleChannel replacementChannel = Iterables.getOnlyElement(updatedSchedule.scheduleChannels()); assertEquals(3, replacementChannel.items().size()); assertEquals(item1.getCanonicalUri(), replacementChannel.items().get(0).getCanonicalUri()); assertEquals(item4.getCanonicalUri(), replacementChannel.items().get(1).getCanonicalUri()); assertEquals(item3.getCanonicalUri(), replacementChannel.items().get(2).getCanonicalUri()); } @Test(expected=IllegalArgumentException.class) public void overlappingScheduleShouldError() throws Exception { Broadcast wrongBroadcast = new Broadcast(BBC_ONE.getUri(), now.minusHours(3), now.minusHours(1)); version3.setBroadcasts(ImmutableSet.of(wrongBroadcast)); List<ItemRefAndBroadcast> itemsAndBroadcasts = Lists.newArrayList(); itemsAndBroadcasts.add(new ItemRefAndBroadcast(item1, broadcast1)); itemsAndBroadcasts.add(new ItemRefAndBroadcast(item3, wrongBroadcast)); store.replaceScheduleBlock(Publisher.BBC, BBC_ONE, itemsAndBroadcasts); } @Test(expected=IllegalArgumentException.class) public void multipleChannelsInScheduleReplaceShouldError() throws Exception { List<ItemRefAndBroadcast> itemsAndBroadcasts = Lists.newArrayList(); itemsAndBroadcasts.add(new ItemRefAndBroadcast(item1, broadcast1)); itemsAndBroadcasts.add(new ItemRefAndBroadcast(item2, broadcast2)); store.replaceScheduleBlock(Publisher.BBC, BBC_ONE, itemsAndBroadcasts); } @Test public void wrongChannelShouldBeFiltered() throws Exception { Broadcast broadcast = new Broadcast(AL_JAZEERA_ENGLISH.getUri(), now.minusHours(2), now.minusHours(3)); version1.addBroadcast(broadcast); store.writeScheduleFrom(item1); store.writeScheduleFrom(item2); Set<Publisher> publishers = ImmutableSet.of(Publisher.BBC); when(application.getConfiguration()).thenReturn(configWithSources(publishers)); Schedule schedule = store.schedule(now.minusHours(4), now, ImmutableSet.of(BBC_ONE, BBC_TWO), publishers, Optional.of(application)); assertSchedule(schedule); } @Test public void wrongIntervalShouldBeFiltered() throws Exception { Broadcast broadcast = new Broadcast(BBC_ONE.getUri(), now.minusHours(6), now.minusHours(5)); version1.addBroadcast(broadcast); store.writeScheduleFrom(item1); store.writeScheduleFrom(item2); Set<Publisher> publishers = ImmutableSet.of(Publisher.BBC); when(application.getConfiguration()).thenReturn(configWithSources(publishers)); Schedule schedule = store.schedule(now.minusHours(4), now, ImmutableSet.of(BBC_ONE, BBC_TWO), publishers, Optional.of(application)); assertSchedule(schedule); schedule = store.schedule(now.minusHours(6), now.minusHours(5), ImmutableSet.of(BBC_ONE), publishers, Optional.of(application)); ScheduleChannel channel = Iterables.getOnlyElement(schedule.scheduleChannels()); Item item1 = Iterables.getOnlyElement(channel.items()); Broadcast broadcast1 = ScheduleEntry.BROADCAST.apply(item1); assertEquals(now.minusHours(6), broadcast1.getTransmissionTime()); } @Test public void wrongPublisherShouldBeFiltered() throws Exception { Item copy = (Item) item1.copy(); copy.setPublisher(Publisher.BLIP); store.writeScheduleFrom(copy); store.writeScheduleFrom(item2); Set<Publisher> publishers = ImmutableSet.of(Publisher.BBC); when(application.getConfiguration()).thenReturn(configWithSources(publishers)); Schedule schedule = store.schedule(now.minusHours(4), now, ImmutableSet.of(BBC_ONE), publishers, Optional.of(application)); ScheduleChannel channel = Iterables.getOnlyElement(schedule.scheduleChannels()); Item item1 = Iterables.getOnlyElement(channel.items()); Broadcast broadcast1 = ScheduleEntry.BROADCAST.apply(item1); assertEquals(now.minusHours(2), broadcast1.getTransmissionTime()); } private void assertSchedule(Schedule schedule) { assertEquals(2, schedule.scheduleChannels().size()); for (ScheduleChannel channel: schedule.scheduleChannels()) { assertEquals(2, channel.items().size()); Item item1 = channel.items().get(0); assertFalse(Iterables.isEmpty(item1.flattenLocations())); for (Location location : item1.flattenLocations()) { assertTrue(location.getAvailable()); } Broadcast broadcast1 = ScheduleEntry.BROADCAST.apply(item1); if (item1.getCanonicalUri().equals(this.item1.getCanonicalUri())) { assertNotNull(item1.getContainer()); } Item item2 = channel.items().get(1); assertTrue(item2 instanceof Episode); Broadcast broadcast2 = ScheduleEntry.BROADCAST.apply(item2); if (channel.channel() == BBC_ONE) { assertEquals(now.minusHours(4), broadcast1.getTransmissionTime()); assertEquals(now.minusHours(2), broadcast2.getTransmissionTime()); } else { assertEquals(now.minusHours(4), broadcast1.getTransmissionTime()); assertEquals(now.minusHours(1), broadcast2.getTransmissionTime()); } } } @Test public void testResolvingScheduleByCount() { Item item1 = itemWithBroadcast(BBC_ONE, now, now.plusHours(30)); Item item2 = itemWithBroadcast(BBC_ONE, now.plusHours(30), now.plusHours(35)); Item item3 = itemWithBroadcast(BBC_TWO, now, now.plusHours(5)); Item item4 = itemWithBroadcast(BBC_TWO, now.plusHours(5), now.plusHours(10)); StubEquivalentContentResolver contentResolver = new StubEquivalentContentResolver() .respondsTo(item1) .respondsTo(item2) .respondsTo(item3) .respondsTo(item4); ImmutableSet<Channel> channels = ImmutableSet.of(BBC_ONE, BBC_TWO); ChannelResolver channelResolver = new DummyChannelResolver(channels); MongoScheduleStore store = new MongoScheduleStore(database, channelResolver, new StubContentResolver(), contentResolver, ms); store.writeScheduleFrom(item1); store.writeScheduleFrom(item2); store.writeScheduleFrom(item3); store.writeScheduleFrom(item4); checkCount(now, store, channels, 1, 1); checkCount(now, store, channels, 2, 2); } @Test public void testResolvesMaxItemsWhenCountHigherThanMax() { Item item1 = itemWithBroadcast(BBC_ONE, now, now.plusHours(30)); Item item2 = itemWithBroadcast(BBC_ONE, now.plusHours(30), now.plusHours(35)); StubEquivalentContentResolver contentResolver = new StubEquivalentContentResolver() .respondsTo(item1) .respondsTo(item2); ImmutableSet<Channel> channel = ImmutableSet.of(BBC_ONE); ChannelResolver channelResolver = new DummyChannelResolver(channel); MongoScheduleStore store = new MongoScheduleStore(database, channelResolver, new StubContentResolver(), contentResolver, ms); store.writeScheduleFrom(item1); store.writeScheduleFrom(item2); checkCount(now, store, channel, 3, 2); } @Test public void testIgnoresRefsInEntryBeforeFromParam() { Item item1 = itemWithBroadcast(BBC_ONE, now, now.plusMinutes(30)); Item item2 = itemWithBroadcast(BBC_ONE, now.plusMinutes(30), now.plusMinutes(60)); StubEquivalentContentResolver contentResolver = new StubEquivalentContentResolver() .respondsTo(item1) .respondsTo(item2); ImmutableSet<Channel> channel = ImmutableSet.of(BBC_ONE); ChannelResolver channelResolver = new DummyChannelResolver(channel); MongoScheduleStore store = new MongoScheduleStore(database, channelResolver, new StubContentResolver(), contentResolver, ms); store.writeScheduleFrom(item1); store.writeScheduleFrom(item2); checkCount(now.plusMinutes(45), store, channel, 1, 1); } @SuppressWarnings("unchecked") @Test public void testResolvesEquivalentItemsForApplicationConfigurationWithPrecendenceEnabled() { Item item1 = itemWithBroadcast(BBC_ONE, now, now.plusHours(30)); Item item2 = itemWithBroadcast(BBC_ONE, now.plusHours(30), now.plusHours(35)); Item item3 = itemWithBroadcast(BBC_TWO, now, now.plusHours(5)); Item item4 = itemWithBroadcast(BBC_TWO, now.plusHours(5), now.plusHours(10)); Collection<String> uris = Collections2.transform(ImmutableList.of(item1,item2,item3,item4), Identified.TO_URI); ChannelResolver channelResolver = new DummyChannelResolver(ImmutableList.of(BBC_ONE, BBC_TWO, Channel_4_HD, AL_JAZEERA_ENGLISH)); ContentResolver contentResolver = mock(ContentResolver.class); EquivalentContentResolver equivalentContentResolver = mock(EquivalentContentResolver.class); MongoScheduleStore store = new MongoScheduleStore(database, channelResolver, contentResolver, equivalentContentResolver, ms); store.writeScheduleFrom(item1); store.writeScheduleFrom(item2); store.writeScheduleFrom(item3); store.writeScheduleFrom(item4); Set<Publisher> publishers = ImmutableSet.of(Publisher.BBC); when(application.getConfiguration()).thenReturn(configWithSources(publishers)); Optional<Application> applicationOptional = Optional.of(application); when(equivalentContentResolver.resolveUris( (Iterable) argThat(hasItem(isIn(uris))), argThat(is(applicationOptional.get())), argThat(is(Annotation.defaultAnnotations())), eq(false))) .thenReturn(EquivalentContent.builder().build()); store.schedule(now, now.plusHours(48), ImmutableSet.of(BBC_ONE, BBC_TWO), publishers, applicationOptional); verify(contentResolver, never()).findByCanonicalUris(any(Iterable.class)); for (String uri : uris) { verify(equivalentContentResolver).resolveUris(argThat(hasItems(uri)), argThat(is(applicationOptional.get())), argThat(is(Annotation.defaultAnnotations())), eq(false)); } } @Test public void testResolvingUnmergedSchedule() { store.writeScheduleFrom(item1); store.writeScheduleFrom(item2); Schedule schedule = store.unmergedSchedule(now.minusHours(4), now, ImmutableSet.of(BBC_ONE, BBC_TWO), ImmutableSet.of(Publisher.BBC, Publisher.C4, Publisher.ITV)); assertSchedule(schedule); } private void checkCount( DateTime from, MongoScheduleStore store, Set<Channel> channels, int requestedCount, int expectedCount ) { Set<Publisher> publishers = ImmutableSet.of(Publisher.BBC); when(application.getConfiguration()).thenReturn(configWithSources(publishers)); Schedule schedule = store.schedule(from, requestedCount, ImmutableSet.copyOf(channels), publishers, Optional.of(application)); for (ScheduleChannel sc : schedule.scheduleChannels()) { assertThat(String.format("Schedule for %s should have %s items", sc.channel(), expectedCount), sc.items().size(), is(expectedCount)); } } private Item itemWithBroadcast(Channel channel, DateTime start, DateTime end) { Item item = new Item(); item.setCanonicalUri(String.valueOf(item.hashCode())); item.setPublisher(Publisher.BBC); Version version = new Version(); version.addBroadcast(new Broadcast(channel.getCanonicalUri(), start, end)); item.addVersion(version); return item; } private ApplicationConfiguration configWithSources(Set<Publisher> publishers) { return ApplicationConfiguration.builder() .withPrecedence(publishers.stream().collect(MoreCollectors.toImmutableList())) .withEnabledWriteSources(ImmutableList.of()) .build(); } }