package org.atlasapi.persistence.content.mongo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import org.atlasapi.media.entity.Brand;
import org.atlasapi.media.entity.ChildRef;
import org.atlasapi.media.entity.Container;
import org.atlasapi.media.entity.Content;
import org.atlasapi.media.entity.Episode;
import org.atlasapi.media.entity.Item;
import org.atlasapi.media.entity.ParentRef;
import org.atlasapi.media.entity.Publisher;
import org.atlasapi.media.entity.RelatedLink;
import org.atlasapi.media.entity.Series;
import org.atlasapi.media.entity.SeriesRef;
import org.atlasapi.persistence.audit.PerHourAndDayMongoPersistenceAuditLog;
import org.atlasapi.persistence.audit.PersistenceAuditLog;
import org.atlasapi.persistence.lookup.NewLookupWriter;
import org.atlasapi.persistence.media.entity.ContainerTranslator;
import org.atlasapi.persistence.media.entity.DescribedTranslator;
import org.atlasapi.persistence.media.entity.IdentifiedTranslator;
import org.atlasapi.persistence.media.entity.ItemTranslator;
import org.atlasapi.persistence.player.PlayerResolver;
import org.atlasapi.persistence.service.ServiceResolver;
import org.joda.time.DateTime;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.metabroadcast.common.ids.SubstitutionTableNumberCodec;
import com.metabroadcast.common.persistence.MongoTestHelper;
import com.metabroadcast.common.persistence.mongo.DatabasedMongo;
import com.metabroadcast.common.time.DateTimeZones;
import com.metabroadcast.common.time.SystemClock;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
@RunWith( MockitoJUnitRunner.class )
public class MongoContentWriterTest {
private static DatabasedMongo mongo;
private final NewLookupWriter lookupWriter = new NewLookupWriter() {
@Override
public void ensureLookup(Content described) {
}
};
private final ServiceResolver serviceResolver = mock(ServiceResolver.class);
private final PlayerResolver playerResolver = mock(PlayerResolver.class);
private final PersistenceAuditLog persistenceAuditLog = new PerHourAndDayMongoPersistenceAuditLog(mongo);
private final MongoContentWriter contentWriter = new MongoContentWriter(mongo, lookupWriter,
persistenceAuditLog, playerResolver, serviceResolver, new SystemClock());
private final DBCollection children = mongo.collection("children");
private final DBCollection topLevelItems = mongo.collection("topLevelItems");
private final DBCollection containers = mongo.collection("containers");
private final DBCollection programmeGroups = mongo.collection("programmeGroups");
private final ContainerTranslator containerTranslator = new ContainerTranslator(new SubstitutionTableNumberCodec());
private final ItemTranslator itemTranslator = new ItemTranslator(new SubstitutionTableNumberCodec());
@BeforeClass
public static void setUp() {
mongo = MongoTestHelper.anEmptyTestDatabase();
}
@After
public void clearDb() {
children.remove(new BasicDBObject());
topLevelItems.remove(new BasicDBObject());
containers.remove(new BasicDBObject());
programmeGroups.remove(new BasicDBObject());
}
@Test
public void testCreatingItemWithoutWrittenContainerFails() {
Item item = new Item("itemUri", "itemCurie", Publisher.BBC);
Container container = new Container("containerUri", "containerCurie", Publisher.BBC);
item.setContainer(container);
try {
contentWriter.createOrUpdate(item);
fail("Expected an exception");
} catch (IllegalStateException e) {
assertNull(children.findOne(item.getCanonicalUri()));
}
}
@Test
public void testCreatingEpisodeWithoutWrittenBrandFails() {
Episode item = new Episode("itemUri", "itemCurie", Publisher.BBC);
Series series = new Series("seriesUri","seriesCurie", Publisher.BBC);
Brand brand = new Brand("brandUri", "brandCurie", Publisher.BBC);
item.setSeries(series);
item.setContainer(brand);
try {
contentWriter.createOrUpdate(item);
fail("Expected an exception");
} catch (IllegalStateException e) {
assertNull(children.findOne(item.getCanonicalUri()));
}
}
@Test
public void testCreatingEpisodeWithoutWrittenSeriesFailsAndDoesntChangeBrand() {
Episode item = new Episode("itemUri", "itemCurie", Publisher.BBC);
item.setThisOrChildLastUpdated(new DateTime(DateTimeZones.UTC));
Series series = new Series("seriesUri","seriesCurie", Publisher.BBC);
Brand brand = new Brand("brandUri", "brandCurie", Publisher.BBC);
item.setSeries(series);
item.setContainer(brand);
contentWriter.createOrUpdate(brand);
try {
contentWriter.createOrUpdate(item);
fail("Expected an exception");
} catch (IllegalStateException e) {
Brand retrievedBrand = retrieveBrand(brand);
assertTrue(retrievedBrand.getChildRefs().isEmpty());
assertNull(children.findOne(item.getCanonicalUri()));
}
}
@Test
public void testWritingEpisodeInSeriesInBrand() {
Episode item = new Episode("itemUri", "itemCurie", Publisher.BBC);
item.setThisOrChildLastUpdated(new DateTime(DateTimeZones.UTC));
Series series = new Series("seriesUri","seriesCurie", Publisher.BBC);
series.setThisOrChildLastUpdated(new DateTime(DateTimeZones.UTC));
Brand brand = new Brand("brandUri", "brandCurie", Publisher.BBC);
series.setParent(brand);
item.setSeries(series);
item.setContainer(brand);
contentWriter.createOrUpdate(brand);
contentWriter.createOrUpdate(series);
contentWriter.createOrUpdate(item);
assertNotNull(children.findOne(item.getCanonicalUri()));
Series retrievedSeries = retrieveSeries(series);
assertEquals(item.getCanonicalUri(), Iterables.getOnlyElement(retrievedSeries.getChildRefs()).getUri());
assertNull(containers.findOne(series.getCanonicalUri()));
Brand retrievedBrand = retrieveBrand(brand);
assertEquals(item.getCanonicalUri(), Iterables.getOnlyElement(retrievedBrand.getChildRefs()).getUri());
assertEquals(series.getCanonicalUri(), Iterables.getOnlyElement(retrievedBrand.getSeriesRefs()).getUri());
}
@Test
public void testWritingEpisodeInTopLevelSeries() {
Episode item = new Episode("itemUri", "itemCurie", Publisher.BBC);
item.setThisOrChildLastUpdated(new DateTime(DateTimeZones.UTC));
Series series = new Series("seriesUri","seriesCurie", Publisher.BBC);
series.setThisOrChildLastUpdated(new DateTime(DateTimeZones.UTC));
item.setContainer(series);
item.setSeries(series);
contentWriter.createOrUpdate(series);
contentWriter.createOrUpdate(item);
assertNotNull(children.findOne(item.getCanonicalUri()));
Series retrievedSeries = retrieveSeries(series);
assertEquals(item.getCanonicalUri(), Iterables.getOnlyElement(retrievedSeries.getChildRefs()).getUri());
assertNotNull(containers.findOne(series.getCanonicalUri()));
}
@Test
public void testWritingContainer() {
Brand container = new Brand("containerUri", "containerCurie", Publisher.BBC);
contentWriter.createOrUpdate(container);
assertNotNull(containers.findOne(container.getCanonicalUri()));
}
@Test
public void testWritingContainerAndRemovingRelatedLinks() {
Brand container = new Brand("containerUri", "containerCurie", Publisher.BBC);
container.setRelatedLinks(ImmutableSet.of(RelatedLink.unknownTypeLink("http://example.org").build()));
contentWriter.createOrUpdate(container);
assertTrue(containers.findOne(container.getCanonicalUri()).containsField(DescribedTranslator.LINKS_KEY));
container.setRelatedLinks(ImmutableSet.<RelatedLink>of());
contentWriter.createOrUpdate(container);
assertFalse(containers.findOne(container.getCanonicalUri()).containsField(DescribedTranslator.LINKS_KEY));
}
@Test
public void testConvertingBetweenEpisodeAndTopLevelItem() {
Item item = new Item("itemUri", "itemCurie", Publisher.BBC);
contentWriter.createOrUpdate(item);
assertNotNull(topLevelItems.findOne(item.getCanonicalUri()));
Episode episode = new Episode("itemUri", "itemCurie", Publisher.BBC);
Brand brand = new Brand("brandUri", "brandUri", Publisher.BBC);
episode.setContainer(brand);
contentWriter.createOrUpdate(brand);
contentWriter.createOrUpdate(episode);
assertNull(topLevelItems.findOne(item.getCanonicalUri()));
assertNotNull(children.findOne(item.getCanonicalUri()));
}
@Test
public void testConvertingTopLevelSeriesToBrandedSeries() {
Series series = new Series("seriesUri","seriesCurie", Publisher.BBC);
series.setThisOrChildLastUpdated(new DateTime(DateTimeZones.UTC));
contentWriter.createOrUpdate(series);
Series retrievedTopLevelSeries = retrieveTopLevelSeries(series);
Series retrievedSeries = retrieveSeries(series);
assertNotNull(retrievedTopLevelSeries);
assertNotNull(retrievedSeries);
Brand brand = new Brand("brandUri", "brandCurie", Publisher.BBC);
series.setParent(brand);
contentWriter.createOrUpdate(brand);
contentWriter.createOrUpdate(series);
Brand retrievedBrand = retrieveBrand(brand);
assertNull("top-level series not null", containers.findOne(series.getCanonicalUri()));
assertNotNull(programmeGroups.findOne(series.getCanonicalUri()));
assertNotNull(retrievedBrand);
assertTrue(retrievedBrand.getSeriesRefs().size() == 1);
}
@Test
public void testThisOrChildLastUpdatedFieldIsKeptInSync() {
DateTime brandLastUpdated = new DateTime(100, DateTimeZones.UTC);
DateTime seriesLastUpdated = new DateTime(200, DateTimeZones.UTC);
DateTime episodeLastUpdated = new DateTime(300, DateTimeZones.UTC);
Brand brand = new Brand("brandUri", "brandUri", Publisher.BBC);
brand.setLastUpdated(brandLastUpdated);
contentWriter.createOrUpdate(brand);
assertThat(retrieveBrand(brand).getThisOrChildLastUpdated(), is(equalTo(brandLastUpdated)));
Series series = new Series("seriesUri","seriesCurie", Publisher.BBC);
series.setLastUpdated(seriesLastUpdated );
series.setParent(brand);
contentWriter.createOrUpdate(series);
assertThat(retrieveSeries(series).getThisOrChildLastUpdated(), is(equalTo(seriesLastUpdated)));
assertThat(retrieveBrand(brand).getThisOrChildLastUpdated(), is(equalTo(seriesLastUpdated)));
Episode episode = new Episode("itemUri", "itemCurie", Publisher.BBC);
episode.setLastUpdated(episodeLastUpdated);
episode.setSeries(series);
episode.setContainer(brand);
contentWriter.createOrUpdate(episode);
assertThat(retrieveEpisode(episode).getThisOrChildLastUpdated(), is(equalTo(episodeLastUpdated)));
assertThat(retrieveSeries(series).getThisOrChildLastUpdated(), is(equalTo(episodeLastUpdated)));
assertThat(retrieveBrand(brand).getThisOrChildLastUpdated(), is(equalTo(episodeLastUpdated)));
Episode episode2 = new Episode("itemUri2", "itemCurie2", Publisher.BBC);
DateTime episodeLastUpdated2 = new DateTime(250, DateTimeZones.UTC);
episode2.setLastUpdated(episodeLastUpdated2);
episode2.setSeries(series);
episode2.setContainer(brand);
contentWriter.createOrUpdate(episode2);
assertThat(retrieveEpisode(episode).getThisOrChildLastUpdated(), is(equalTo(episodeLastUpdated)));
assertThat(retrieveEpisode(episode2).getThisOrChildLastUpdated(), is(equalTo(episodeLastUpdated2)));
assertThat(retrieveSeries(series).getThisOrChildLastUpdated(), is(equalTo(episodeLastUpdated)));
assertThat(retrieveBrand(brand).getThisOrChildLastUpdated(), is(equalTo(episodeLastUpdated)));
}
@Test
public void testNumericIdsAreWrittenIntoParentAndChildRefs() {
Brand brand = new Brand("brandUri", "brandUri", Publisher.BBC);
brand.setId(1L);
contentWriter.createOrUpdate(brand);
Series series = new Series("seriesUri","seriesCurie", Publisher.BBC);
series.setId(2L);
series.withSeriesNumber(2);
series.setParent(brand);
contentWriter.createOrUpdate(series);
Episode episode = new Episode("itemUri", "itemCurie", Publisher.BBC);
episode.setId(3L);
episode.setSeries(series);
episode.setContainer(brand);
contentWriter.createOrUpdate(episode);
Brand retrievedBrand = retrieveBrand(brand);
SeriesRef seriesRef = Iterables.getOnlyElement(retrievedBrand.getSeriesRefs());
assertThat(seriesRef.getId(), is(2L));
assertThat(seriesRef.getSeriesNumber(), is(2));
ChildRef episodeRef = Iterables.getOnlyElement(retrievedBrand.getChildRefs());
assertThat(episodeRef.getId(), is(3L));
Series retrievedSeries = retrieveSeries(series);
ChildRef seriesEpisodeRef = Iterables.getOnlyElement(retrievedSeries.getChildRefs());
assertThat(seriesEpisodeRef.getId(), is(3L));
ParentRef seriesBrandRef = retrievedSeries.getParent();
assertThat(seriesBrandRef.getId(), is(1L));
Episode retrievedEpisode = retrieveEpisode(episode);
assertThat(retrievedEpisode.getContainer().getId(), is(1L));
assertThat(retrievedEpisode.getSeriesRef().getId(), is(2L));
}
@Test
public void testUpdateBrandRefForEpisodes(){
Brand brand = new Brand("brandUri", "brandUri", Publisher.BBC);
Series series = new Series("seriesUri","seriesCurie", Publisher.BBC);
series.setParent(brand);
Episode episode = new Episode("itemUri", "itemCurie", Publisher.BBC);
episode.setContainer(brand);
episode.setSeries(series);
contentWriter.createOrUpdate(brand);
contentWriter.createOrUpdate(series);
contentWriter.createOrUpdate(episode);
Episode retrievedEpisode = retrieveEpisode(episode);
assertEquals(retrievedEpisode.getContainer().getUri(), "brandUri");
Brand newBrand = new Brand("newBrandUri", "newBrandUri", Publisher.BBC);
Series newSeries = new Series("seriesUri","seriesCurie", Publisher.BBC);
newSeries.setParent(newBrand);
Episode newEpisode = new Episode("itemUri", "itemCurie", Publisher.BBC);
newEpisode.setContainer(newBrand);
newEpisode.setSeries(newSeries);
contentWriter.createOrUpdate(newBrand);
contentWriter.createOrUpdate(newSeries);
contentWriter.createOrUpdate(newEpisode);
Episode retrievedNewEpisode = retrieveEpisode(newEpisode);
assertEquals(retrievedNewEpisode.getContainer().getUri(), "newBrandUri");
}
public Brand retrieveBrand(Brand brand) {
return (Brand) containerTranslator.fromDB(containers.findOne(brand.getCanonicalUri()));
}
public Series retrieveSeries(Series series) {
return (Series) containerTranslator.fromDB(programmeGroups.findOne(series.getCanonicalUri()));
}
public Series retrieveTopLevelSeries(Series series) {
return (Series) containerTranslator.fromDB(containers.findOne(series.getCanonicalUri()));
}
private Episode retrieveEpisode(Episode episode) {
return (Episode) itemTranslator.fromDB(children.findOne(episode.getCanonicalUri()));
}
}