/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.itest.content; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import org.jdom.Document; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.orm.hibernate3.HibernateTemplate; import com.enonic.cms.framework.cache.CacheManager; import com.enonic.cms.framework.xml.XMLDocumentFactory; import com.enonic.cms.core.content.ContentEntity; import com.enonic.cms.core.content.ContentKey; import com.enonic.cms.core.content.ContentService; import com.enonic.cms.core.content.ContentStatus; import com.enonic.cms.core.content.RelatedContentFetcher; import com.enonic.cms.core.content.command.CreateContentCommand; import com.enonic.cms.core.content.command.UpdateContentCommand; import com.enonic.cms.core.content.contentdata.custom.CustomContentData; import com.enonic.cms.core.content.contentdata.custom.contentkeybased.RelatedContentDataEntry; import com.enonic.cms.core.content.contentdata.custom.relationdataentrylistbased.RelatedContentsDataEntry; import com.enonic.cms.core.content.contentdata.custom.stringbased.TextDataEntry; import com.enonic.cms.core.content.contenttype.ContentHandlerName; import com.enonic.cms.core.content.contenttype.ContentTypeConfigBuilder; import com.enonic.cms.core.content.resultset.ContentResultSet; import com.enonic.cms.core.content.resultset.ContentResultSetNonLazy; import com.enonic.cms.core.content.resultset.RelatedChildContent; import com.enonic.cms.core.content.resultset.RelatedContentResultSet; import com.enonic.cms.core.content.resultset.RelatedParentContent; import com.enonic.cms.core.security.PortalSecurityHolder; import com.enonic.cms.core.security.user.User; import com.enonic.cms.core.security.user.UserType; import com.enonic.cms.itest.AbstractSpringTest; import com.enonic.cms.itest.util.DomainFactory; import com.enonic.cms.itest.util.DomainFixture; import com.enonic.cms.store.dao.ContentEntityDao; import com.enonic.cms.store.dao.RelatedChildContentQuery; import static org.junit.Assert.*; public class RelatedContentFetcherTest extends AbstractSpringTest { @Autowired private HibernateTemplate hibernateTemplate; @Qualifier("cacheFacadeManager") @Autowired private CacheManager cacheManager; private OverridingContentEntityDao contentDao; @Autowired protected ContentService contentService; @Autowired private DomainFixture fixture; public RelatedContentFetcherTest() { super(); } @Before public void setUp() { DomainFactory factory = fixture.getFactory(); // setup needed common data for each test fixture.initSystemData(); fixture.createAndStoreUserAndUserGroup( "testuser", "testuser fullname", UserType.NORMAL, "testuserstore" ); //SecurityHolder.setUser( findUserByName( User.ANONYMOUS_UID ).getKey() ); PortalSecurityHolder.setAnonUser( fixture.findUserByName( User.ANONYMOUS_UID ).getKey() ); fixture.save( factory.createContentHandler( "Custom content", ContentHandlerName.CUSTOM.getHandlerClassShortName() ) ); fixture.flushAndClearHibernateSession(); // setup content type ContentTypeConfigBuilder ctyconf = new ContentTypeConfigBuilder( "MyRelatingContent", "title" ); ctyconf.startBlock( "General" ); ctyconf.addInput( "title", "text", "contentdata/title", "Title", true ); ctyconf.addRelatedContentInput( "myRelatedContent", "contentdata/myRelatedContent", "My related content", false, true ); ctyconf.endBlock(); Document configAsXmlBytes = XMLDocumentFactory.create( ctyconf.toString() ).getAsJDOMDocument(); fixture.save( factory.createContentType( "MyRelatingContent", ContentHandlerName.CUSTOM.getHandlerClassShortName(), configAsXmlBytes ) ); fixture.flushAndClearHibernateSession(); fixture.save( factory.createUnit( "MyUnit", "en" ) ); fixture.save( factory.createCategory( "MyCategory", null, "MyRelatingContent", "MyUnit", "testuser", "testuser" ) ); fixture.save( factory.createCategoryAccessForUser( "MyCategory", "testuser", "read, create, approve" ) ); fixture.flushAndClearHibernateSession(); contentDao = new OverridingContentEntityDao(); contentDao.setHibernateTemplate( hibernateTemplate ); contentDao.setCacheManager( cacheManager ); } @Test public void xxxx() { /** * Ole * -> Kjell * -> Ola * -> Odin * */ // setup: CreateContentCommand createCommand; createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Odin", null ); ContentKey odinContentKey = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Ola", odinContentKey ); ContentKey olaContentKey = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Brita", null ); ContentKey britaContentKey = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Kjell", olaContentKey, britaContentKey ); ContentKey kjellContentKey = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Ole", kjellContentKey ); ContentKey oleContentKey = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Agnes", kjellContentKey ); ContentKey agnesContentKey = contentService.createContent( createCommand ); fixture.flushAndClearHibernateSession(); // exercise RelatedContentFetcher relatedContentFetcher = new RelatedContentFetcher( contentDao ); relatedContentFetcher.setAvailableCheckDate( new Date() ); relatedContentFetcher.setMaxParentChildrenLevel( 0 ); relatedContentFetcher.setMaxParentLevel( Integer.MAX_VALUE ); relatedContentFetcher.setMaxChildrenLevel( Integer.MAX_VALUE ); relatedContentFetcher.setIncludeOfflineContent( true ); RelatedContentResultSet resultSet = relatedContentFetcher.fetch( createContentResultSet( kjellContentKey ) ); // verify: //assertEquals( 3, resultSet.getDinstinctSetOfContent().size() ); Collection relatedContent = resultSet.getDistinctCollectionOfRelatedContent(); System.out.println( "asdfasdf" ); } @Test public void fetch_children_when_one_content_relates_another() { // setup CreateContentCommand createCommand; createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Son", null ); ContentKey sonContentKey = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Father", sonContentKey ); ContentKey fatherContentKey = contentService.createContent( createCommand ); fixture.flushAndClearHibernateSession(); // exercise RelatedContentFetcher relatedContentFetcher = new RelatedContentFetcher( contentDao ); relatedContentFetcher.setAvailableCheckDate( new Date() ); relatedContentFetcher.setMaxParentChildrenLevel( 0 ); relatedContentFetcher.setMaxParentLevel( 0 ); relatedContentFetcher.setMaxChildrenLevel( Integer.MAX_VALUE ); relatedContentFetcher.setIncludeOfflineContent( true ); RelatedContentResultSet resultSet = relatedContentFetcher.fetch( createContentResultSet( fatherContentKey ) ); // verify: assertEquals( 1, resultSet.getDinstinctSetOfContent().size() ); } @Test public void fetch_parents_when_one_content_relates_another() { // setup CreateContentCommand createCommand; createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Son", null ); ContentKey sonContentKey = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Father", sonContentKey ); ContentKey fatherContentKey = contentService.createContent( createCommand ); fixture.flushAndClearHibernateSession(); // exercise RelatedContentFetcher relatedContentFetcher = new RelatedContentFetcher( contentDao ); relatedContentFetcher.setAvailableCheckDate( new Date() ); relatedContentFetcher.setMaxParentChildrenLevel( 0 ); relatedContentFetcher.setMaxParentLevel( Integer.MAX_VALUE ); relatedContentFetcher.setMaxChildrenLevel( 0 ); relatedContentFetcher.setIncludeOfflineContent( true ); RelatedContentResultSet resultSet = relatedContentFetcher.fetch( createContentResultSet( sonContentKey ) ); // verify: assertEquals( 1, resultSet.getDinstinctSetOfContent().size() ); } @Test public void three_content_relates_each_other_in_a_circle() { // setup CreateContentCommand createCommand; createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content C", null ); ContentKey contentKeyC = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content A", contentKeyC ); ContentKey contentKeyA = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content B", contentKeyA ); ContentKey contentKeyB = contentService.createContent( createCommand ); UpdateContentCommand updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( contentKeyC, "Content C", contentKeyB ); contentService.updateContent( updateCommand ); fixture.flushAndClearHibernateSession(); // exercise RelatedContentFetcher relatedContentFetcher = new RelatedContentFetcher( contentDao ); relatedContentFetcher.setAvailableCheckDate( new Date() ); relatedContentFetcher.setMaxParentChildrenLevel( 0 ); relatedContentFetcher.setMaxParentLevel( Integer.MAX_VALUE ); relatedContentFetcher.setMaxChildrenLevel( Integer.MAX_VALUE ); relatedContentFetcher.setIncludeOfflineContent( true ); RelatedContentResultSet resultSet = relatedContentFetcher.fetch( createContentResultSet( contentKeyA, contentKeyB, contentKeyC ) ); // verify: all are in the main set assertEquals( 3, resultSet.getDinstinctSetOfContent().size() ); assertEquals( 3, resultSet.getContentKeys().size() ); assertEquals( convertToSet( fixture.findContentByKey( contentKeyA ), fixture.findContentByKey( contentKeyB ), fixture.findContentByKey( contentKeyC ) ), resultSet.getDinstinctSetOfContent() ); assertRelatedContent( ContentKey.convertToList( contentKeyA, contentKeyB, contentKeyC ), resultSet.getContentKeys() ); // verify: each content has a root related children and parents ContentEntity contentA = fixture.findContentByKey( contentKeyA ); List<RelatedChildContent> contentArelatedChildren = convertRelatedChildContentToList( resultSet.getRootRelatedChildren( contentA.getMainVersion() ) ); assertEquals( 1, contentArelatedChildren.size() ); assertEquals( contentKeyC, contentArelatedChildren.get( 0 ).getContent().getKey() ); List<RelatedParentContent> contentArelatedParents = convertRelatedParentContentToList( resultSet.getRootRelatedParents( contentA ) ); assertEquals( 1, contentArelatedParents.size() ); assertEquals( contentKeyB, contentArelatedParents.get( 0 ).getContent().getKey() ); ContentEntity contentB = fixture.findContentByKey( contentKeyB ); List<RelatedChildContent> contentBrelatedChildren = convertRelatedChildContentToList( resultSet.getRootRelatedChildren( contentB.getMainVersion() ) ); assertEquals( 1, contentBrelatedChildren.size() ); assertEquals( contentKeyA, contentBrelatedChildren.get( 0 ).getContent().getKey() ); List<RelatedParentContent> contentBrelatedParents = convertRelatedParentContentToList( resultSet.getRootRelatedParents( contentB ) ); assertEquals( 1, contentBrelatedParents.size() ); assertEquals( contentKeyC, contentBrelatedParents.get( 0 ).getContent().getKey() ); ContentEntity contentC = fixture.findContentByKey( contentKeyC ); List<RelatedChildContent> contentCrelatedChildren = convertRelatedChildContentToList( resultSet.getRootRelatedChildren( contentC.getMainVersion() ) ); assertEquals( 1, contentCrelatedChildren.size() ); assertEquals( contentKeyB, contentCrelatedChildren.get( 0 ).getContent().getKey() ); List<RelatedParentContent> contentCrelatedParents = convertRelatedParentContentToList( resultSet.getRootRelatedParents( contentC ) ); assertEquals( 1, contentCrelatedParents.size() ); assertEquals( contentKeyA, contentCrelatedParents.get( 0 ).getContent().getKey() ); } @Test public void common_related_child_between_two_content_is_contained_in_root_related_children_for_both_content() { // setup CreateContentCommand createCommand; createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Common child 1", null ); ContentKey commonChild = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content A", commonChild ); ContentKey contentKeyA = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content B", commonChild ); ContentKey contentKeyB = contentService.createContent( createCommand ); fixture.flushAndClearHibernateSession(); // exercise RelatedContentFetcher relatedContentFetcher = new RelatedContentFetcher( contentDao ); relatedContentFetcher.setAvailableCheckDate( new Date() ); relatedContentFetcher.setMaxParentChildrenLevel( 0 ); relatedContentFetcher.setMaxParentLevel( 0 ); relatedContentFetcher.setMaxChildrenLevel( Integer.MAX_VALUE ); relatedContentFetcher.setIncludeOfflineContent( true ); RelatedContentResultSet resultSet = relatedContentFetcher.fetch( createContentResultSet( contentKeyA, contentKeyB ) ); // verify assertEquals( 1, resultSet.getDinstinctSetOfContent().size() ); assertEquals( 1, resultSet.getContentKeys().size() ); assertEquals( convertToSet( fixture.findContentByKey( commonChild ) ), resultSet.getDinstinctSetOfContent() ); assertRelatedContent( ContentKey.convertToList( commonChild ), resultSet.getContentKeys() ); ContentEntity contentA = fixture.findContentByKey( contentKeyA ); List<RelatedChildContent> contentArelatedChildren = convertRelatedChildContentToList( resultSet.getRootRelatedChildren( contentA.getMainVersion() ) ); assertEquals( 1, contentArelatedChildren.size() ); assertEquals( commonChild, contentArelatedChildren.get( 0 ).getContent().getKey() ); ContentEntity contentB = fixture.findContentByKey( contentKeyB ); List<RelatedChildContent> contentBrelatedChildren = convertRelatedChildContentToList( resultSet.getRootRelatedChildren( contentB.getMainVersion() ) ); assertEquals( 1, contentBrelatedChildren.size() ); assertEquals( commonChild, contentBrelatedChildren.get( 0 ).getContent().getKey() ); } @Test public void eternal_loop_is_prevented_for_related_children_with_circular_reference_and_all_other_are_included() { // setup content to update CreateContentCommand createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 1", null ); ContentKey content_1 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 2", null ); ContentKey content_2 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 3", null ); ContentKey content_3 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 4", null ); ContentKey content_4 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 5", null ); ContentKey content_5 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 6", null ); ContentKey content_6 = contentService.createContent( createCommand ); assertEquals( 6, fixture.countAllContent() ); UpdateContentCommand updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_1, "Relating content 1 to 2 and 6", content_2, content_6 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_2, "Relating content 2 to 3 and 5", content_3, content_5 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_3, "Relating content 3 to 1 and 4", content_1, content_4 ); contentService.updateContent( updateCommand ); assertEquals( 6, fixture.countAllContent() ); contentDao.setMaxExpectedFindRelatedChildrenByKeysAttempts( 6 ); RelatedContentFetcher relatedContentFetcher = new RelatedContentFetcher( contentDao ); relatedContentFetcher.setAvailableCheckDate( new Date() ); relatedContentFetcher.setMaxParentChildrenLevel( 0 ); relatedContentFetcher.setMaxParentLevel( 0 ); relatedContentFetcher.setMaxChildrenLevel( Integer.MAX_VALUE ); relatedContentFetcher.setIncludeOfflineContent( true ); RelatedContentResultSet resultSet = relatedContentFetcher.fetch( fixture.findContentByKey( content_1 ) ); List<ContentKey> expectedRelatedContentKeys = new ArrayList<ContentKey>(); expectedRelatedContentKeys.add( content_1 ); expectedRelatedContentKeys.add( content_2 ); expectedRelatedContentKeys.add( content_3 ); expectedRelatedContentKeys.add( content_4 ); expectedRelatedContentKeys.add( content_5 ); expectedRelatedContentKeys.add( content_6 ); assertRelatedContent( expectedRelatedContentKeys, resultSet.getContentKeys() ); } @Test public void eternal_loop_is_prevented_for_related_children_with_multiple_circular_references() { // setup content to update CreateContentCommand createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 1", null ); ContentKey content_1 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 2", null ); ContentKey content_2 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 3", null ); ContentKey content_3 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 4", null ); ContentKey content_4 = contentService.createContent( createCommand ); UpdateContentCommand updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_1, "Relating content 1 to 2, 3 and 4", content_2, content_3, content_4 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_2, "Relating content 2 to 1", content_1 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_3, "Relating content 3 to 1", content_1 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_4, "Relating content 4 to 1", content_1 ); contentService.updateContent( updateCommand ); assertEquals( 4, fixture.countAllContent() ); contentDao.setMaxExpectedFindRelatedChildrenByKeysAttempts( 4 ); RelatedContentFetcher relatedContentFetcher = new RelatedContentFetcher( contentDao ); relatedContentFetcher.setAvailableCheckDate( new Date() ); relatedContentFetcher.setMaxParentChildrenLevel( 0 ); relatedContentFetcher.setMaxParentLevel( 0 ); relatedContentFetcher.setMaxChildrenLevel( Integer.MAX_VALUE ); relatedContentFetcher.setIncludeOfflineContent( true ); RelatedContentResultSet resultSet = relatedContentFetcher.fetch( fixture.findContentByKey( content_1 ) ); List<ContentKey> expectedRelatedContentKeys = new ArrayList<ContentKey>(); expectedRelatedContentKeys.add( content_1 ); expectedRelatedContentKeys.add( content_2 ); expectedRelatedContentKeys.add( content_3 ); expectedRelatedContentKeys.add( content_4 ); assertRelatedContent( expectedRelatedContentKeys, resultSet.getContentKeys() ); } @Test public void eternal_loop_is_prevented_for_related_children_of_children() { // setup content to update CreateContentCommand createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 1", null ); ContentKey content_1 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 2", null ); ContentKey content_2 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 3", null ); ContentKey content_3 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 4", null ); ContentKey content_4 = contentService.createContent( createCommand ); UpdateContentCommand updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_1, "Relating content 1 to 2", content_2 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_2, "Relating content 2 to 3", content_3 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_3, "Relating content 3 to 4", content_4 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_4, "Relating content 4 to 3", content_3 ); contentService.updateContent( updateCommand ); assertEquals( 4, fixture.countAllContent() ); contentDao.setMaxExpectedFindRelatedChildrenByKeysAttempts( 4 ); RelatedContentFetcher relatedContentFetcher = new RelatedContentFetcher( contentDao ); relatedContentFetcher.setAvailableCheckDate( new Date() ); relatedContentFetcher.setMaxParentChildrenLevel( 0 ); relatedContentFetcher.setMaxParentLevel( 0 ); relatedContentFetcher.setMaxChildrenLevel( Integer.MAX_VALUE ); relatedContentFetcher.setIncludeOfflineContent( true ); RelatedContentResultSet resultSet = relatedContentFetcher.fetch( fixture.findContentByKey( content_1 ) ); List<ContentKey> expectedRelatedContentKeys = new ArrayList<ContentKey>(); expectedRelatedContentKeys.add( content_2 ); expectedRelatedContentKeys.add( content_3 ); expectedRelatedContentKeys.add( content_4 ); assertRelatedContent( expectedRelatedContentKeys, resultSet.getContentKeys() ); } @Test public void eternal_loop_is_prevented_for_related_parents_with_circular_reference() { // setup content to update CreateContentCommand createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 1", null ); ContentKey content_1 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 2", null ); ContentKey content_2 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 3", null ); ContentKey content_3 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 4", null ); ContentKey content_4 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 5", null ); ContentKey content_5 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 6", null ); ContentKey content_6 = contentService.createContent( createCommand ); assertEquals( 6, fixture.countAllContent() ); UpdateContentCommand updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_1, "Relating content 1 to 2 and 6", content_2 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_2, "Relating content 2 to 3 and 5", content_3 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_3, "Relating content 3 to 1 and 4", content_1 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_4, "Relating content 4 to 1", content_1 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_5, "Relating content 5 to 2", content_2 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_6, "Relating content 6 to 3", content_3 ); contentService.updateContent( updateCommand ); assertEquals( 6, fixture.countAllContent() ); contentDao.setMaxExpectedFindRelatedChildrenByKeysAttempts( 6 ); RelatedContentFetcher relatedContentFetcher = new RelatedContentFetcher( contentDao ); relatedContentFetcher.setAvailableCheckDate( new Date() ); relatedContentFetcher.setMaxParentChildrenLevel( 0 ); relatedContentFetcher.setMaxParentLevel( Integer.MAX_VALUE ); relatedContentFetcher.setMaxChildrenLevel( 0 ); relatedContentFetcher.setIncludeOfflineContent( true ); RelatedContentResultSet resultSet = relatedContentFetcher.fetch( fixture.findContentByKey( content_1 ) ); List<ContentKey> expectedRelatedContentKeys = new ArrayList<ContentKey>(); expectedRelatedContentKeys.add( content_1 ); expectedRelatedContentKeys.add( content_2 ); expectedRelatedContentKeys.add( content_3 ); expectedRelatedContentKeys.add( content_4 ); expectedRelatedContentKeys.add( content_5 ); expectedRelatedContentKeys.add( content_6 ); assertRelatedContent( expectedRelatedContentKeys, resultSet.getContentKeys() ); } @Test public void eternal_loop_is_prevented_for_related_parent_children_with_circular_reference() { // setup content to update CreateContentCommand createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 1", null ); ContentKey content_1 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 2", null ); ContentKey content_2 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 3", null ); ContentKey content_3 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 4", null ); ContentKey content_4 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 5", null ); ContentKey content_5 = contentService.createContent( createCommand ); createCommand = setupDefaultCreateContentCommandForMyRelatingContent( "Content not relating yet 6", null ); ContentKey content_6 = contentService.createContent( createCommand ); assertEquals( 6, fixture.countAllContent() ); UpdateContentCommand updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_1, "Relating content 1 to 2 and 6", content_2 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_2, "Relating content 2 to 3 and 5", content_3 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_3, "Relating content 3 to 1 and 4", content_1 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_4, "Relating content 4 to 1", content_1 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_5, "Relating content 5 to 2", content_2 ); contentService.updateContent( updateCommand ); updateCommand = setupDefaultUpdateContentCommandForMyRelatingContent( content_6, "Relating content 6 to 3", content_3 ); contentService.updateContent( updateCommand ); assertEquals( 6, fixture.countAllContent() ); contentDao.setMaxExpectedFindRelatedChildrenByKeysAttempts( 6 ); RelatedContentFetcher relatedContentFetcher = new RelatedContentFetcher( contentDao ); relatedContentFetcher.setAvailableCheckDate( new Date() ); relatedContentFetcher.setMaxParentChildrenLevel( Integer.MAX_VALUE ); relatedContentFetcher.setMaxParentLevel( Integer.MAX_VALUE ); relatedContentFetcher.setMaxChildrenLevel( 0 ); relatedContentFetcher.setIncludeOfflineContent( true ); RelatedContentResultSet resultSet = relatedContentFetcher.fetch( fixture.findContentByKey( content_1 ) ); List<ContentKey> expectedRelatedContentKeys = new ArrayList<ContentKey>(); expectedRelatedContentKeys.add( content_1 ); expectedRelatedContentKeys.add( content_2 ); expectedRelatedContentKeys.add( content_3 ); expectedRelatedContentKeys.add( content_4 ); expectedRelatedContentKeys.add( content_5 ); expectedRelatedContentKeys.add( content_6 ); assertRelatedContent( expectedRelatedContentKeys, resultSet.getContentKeys() ); } private CreateContentCommand setupDefaultCreateContentCommandForMyRelatingContent( String title, ContentKey... contentToRelateTo ) { CreateContentCommand createCommand = new CreateContentCommand(); createCommand.setAccessRightsStrategy( CreateContentCommand.AccessRightsStrategy.INHERIT_FROM_CATEGORY ); createCommand.setCategory( fixture.findCategoryByName( "MyCategory" ).getKey() ); createCommand.setCreator( fixture.findUserByName( "testuser" ).getKey() ); createCommand.setPriority( 0 ); createCommand.setLanguage( fixture.findLanguageByCode( "en" ) ); createCommand.setStatus( ContentStatus.APPROVED ); createCommand.setContentName( "testcontent" ); CustomContentData contentData = new CustomContentData( fixture.findContentTypeByName( "MyRelatingContent" ).getContentTypeConfig() ); contentData.add( new TextDataEntry( contentData.getInputConfig( "title" ), title ) ); if ( contentToRelateTo != null ) { RelatedContentsDataEntry relatedContentsDataEntry = new RelatedContentsDataEntry( contentData.getInputConfig( "myRelatedContent" ) ); for ( ContentKey singleContentToRelateTo : contentToRelateTo ) { relatedContentsDataEntry.add( new RelatedContentDataEntry( contentData.getInputConfig( "myRelatedContent" ), singleContentToRelateTo ) ); } contentData.add( relatedContentsDataEntry ); } createCommand.setContentData( contentData ); return createCommand; } private UpdateContentCommand setupDefaultUpdateContentCommandForMyRelatingContent( ContentKey contentKeyToUpdate, String title, ContentKey... contentToRelateTo ) { ContentEntity contentToUpdate = fixture.findContentByKey( contentKeyToUpdate ); UpdateContentCommand command = UpdateContentCommand.storeNewVersionEvenIfUnchanged( contentToUpdate.getMainVersion().getKey() ); command.setUpdateAsMainVersion( true ); command.setContentKey( contentToUpdate.getKey() ); command.setUpdateStrategy( UpdateContentCommand.UpdateStrategy.MODIFY ); command.setModifier( fixture.findUserByName( "testuser" ).getKey() ); command.setPriority( 0 ); command.setLanguage( fixture.findLanguageByCode( "en" ) ); command.setStatus( ContentStatus.APPROVED ); command.setSyncAccessRights( false ); CustomContentData contentData = new CustomContentData( fixture.findContentTypeByName( "MyRelatingContent" ).getContentTypeConfig() ); contentData.add( new TextDataEntry( contentData.getInputConfig( "title" ), title ) ); if ( contentToRelateTo != null ) { RelatedContentsDataEntry relatedContentsDataEntry = new RelatedContentsDataEntry( contentData.getInputConfig( "myRelatedContent" ) ); for ( ContentKey singleContentToRelateTo : contentToRelateTo ) { relatedContentsDataEntry.add( new RelatedContentDataEntry( contentData.getInputConfig( "myRelatedContent" ), singleContentToRelateTo ) ); } contentData.add( relatedContentsDataEntry ); } command.setContentData( contentData ); return command; } private void assertRelatedContent( Collection<ContentKey> expectedRelatedContentKeys, Collection<ContentKey> actual ) { for ( ContentKey expectedContentKey : expectedRelatedContentKeys ) { assertTrue( "expected related content with key: " + expectedContentKey, actual.contains( expectedContentKey ) ); } assertEquals( "unexpected number of related content", expectedRelatedContentKeys.size(), actual.size() ); } private ContentResultSet createContentResultSet( ContentKey... contentKeys ) { List<ContentEntity> list = new ArrayList<ContentEntity>(); for ( ContentKey contentKey : contentKeys ) { list.add( fixture.findContentByKey( contentKey ) ); } return new ContentResultSetNonLazy( list, 0, list.size() ); } private Set<ContentEntity> convertToSet( ContentEntity... contents ) { return new HashSet<ContentEntity>( Arrays.asList( contents ) ); } private List<RelatedChildContent> convertRelatedChildContentToList( Iterable<RelatedChildContent> iterable ) { List<RelatedChildContent> list = new ArrayList<RelatedChildContent>(); for ( RelatedChildContent rcc : iterable ) { list.add( rcc ); } return list; } private List<RelatedParentContent> convertRelatedParentContentToList( Iterable<RelatedParentContent> iterable ) { List<RelatedParentContent> list = new ArrayList<RelatedParentContent>(); for ( RelatedParentContent item : iterable ) { list.add( item ); } return list; } class OverridingContentEntityDao extends ContentEntityDao { private int numberOfFindRelatedChildrenByKeysAttempts = 0; private int maxExpectedFindRelatedChildrenByKeysAttempts = Integer.MAX_VALUE; public void setMaxExpectedFindRelatedChildrenByKeysAttempts( int maxExpectedFindRelatedChildrenByKeysAttempts ) { this.maxExpectedFindRelatedChildrenByKeysAttempts = maxExpectedFindRelatedChildrenByKeysAttempts; } @Override public Collection<RelatedChildContent> findRelatedChildrenByKeys( RelatedChildContentQuery relatedChildContentQuery ) { numberOfFindRelatedChildrenByKeysAttempts++; if ( numberOfFindRelatedChildrenByKeysAttempts > maxExpectedFindRelatedChildrenByKeysAttempts ) { fail( "max expected findRelatedChildrenByKeys attempts exceeded : " + numberOfFindRelatedChildrenByKeysAttempts ); } return super.findRelatedChildrenByKeys( relatedChildContentQuery ); } } }