package de.axone.cache.ng; import static de.axone.cache.ng.CacheNGAssert.*; import static de.axone.cache.ng.CacheNGTestHelpers.*; import static de.axone.cache.ng.CacheNGTest_Implementations.*; import static org.assertj.core.api.Assertions.*; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.testng.annotations.Test; import de.axone.cache.ng.CacheNG.CacheBridge; import de.axone.cache.ng.CacheNGTestHelpers.Identifiable; import de.axone.cache.ng.CacheNGTestHelpers.RN; import de.axone.cache.ng.CacheNGTestHelpers.TArticle; import de.axone.cache.ng.CacheNGTestHelpers.Tid; import de.axone.cache.ng.CacheNGTest_ArticleListForTid.TestAccessor_ArticleForTid; import de.axone.cache.ng.CacheNGTest_ArticleListForTid.TestMapTidToArticle; @Test( groups="cacheng.events" ) public class CacheNGTest_ArticleListForTop { static final Top T001 = top( "001" ), T002 = top( "002" ) ; private static final TestMapTidToArticle data = new TestMapTidToArticle(); { data.addArticle( TArticle.build( A12345 ) ); data.addArticle( TArticle.build( A12346 ) ); data.addArticle( TArticle.build( A12347 ) ); } TidForTop tidForTop = new TidForTop(); public void cacheArticlesForTopAndInvalidateViaTid(){ TestAccessor_ArticleForTid accessorForTid = new TestAccessor_ArticleForTid( data ); CacheHashMap<Tid, List<TArticle>> cacheForTid = new CacheHashMap<>( RN.TID_LARTICLE ); CacheNG.AutomaticClient<Tid, List<TArticle>> autoForTid = new AutomaticClientImpl<>( cacheForTid ); TestAccessor_ArticleForTop accessorForTop = new TestAccessor_ArticleForTop( autoForTid, accessorForTid, tidForTop ); CacheHashMap<Top, List<TArticle>> cacheForTop = new CacheHashMap<>( RN.TOP_LARTICLE ); CacheNG.AutomaticClient<Top, List<TArticle>> autoForTop = new AutomaticClientImpl<>( cacheForTop ); cacheForTid.registerListener( new TidToTopBridge( cacheForTop, tidForTop ) ); assertThat( autoForTop ) .hasNotCached( T001 ) .hasNotCached( T002 ); List<TArticle> arts = autoForTop.fetch( T001, accessorForTop ); assertThat( autoForTop ) .hasCached( T001 ) .hasNotCached( T002 ); assertThat( arts ) .are( havingTid( T123 ) ) // T001 is top of T123 .areNot( havingTid( T999 ) ); // But not stored in article arts = autoForTop.fetch( T002, accessorForTop ); assertThat( arts ).hasSize( 2 ) .are( havingTid( T345 ) ) // T002 is top of T345 and T456 .areAtLeast( 1, havingTid( T234 ) ) .areAtLeast( 1, havingTid( T456 ) ) .areNot( havingTid( T123 ) ) .areNot( havingTid( T999 ) ) ; // Invalidating T002 leeves us with cached T001 ... autoForTop.invalidate( T002 ); assertThat( autoForTop ) .hasNotCached( T002 ) .hasCached( T001 ); // Which we try to remove using the master Cache // On import of articles: diff oldtids/newtids // Invalidation is done only on the article's tids autoForTid.invalidate( T123 ); // Invalidation must propagate to top assertThat( autoForTop ) .hasNotCached( T002 ) .hasNotCached( T001 ); } static class Top extends Identifiable { public Top( String identifier ) { super( identifier ); } } static Top top( String identifier ){ return new Top( identifier ); } private static class TidForTop { private static Map<Top,List<Tid>> tree = new HashMap<>(); static { tree.put( T001, Arrays.asList( T123 ) ); tree.put( T002, Arrays.asList( T345, T456 ) ); } List<Tid> tidForTop( Top top ){ return tree.get( top ); } Top topForTid( Tid tid ){ for( Map.Entry<Top, List<Tid>> entry : tree.entrySet() ){ if( entry.getValue().contains( tid ) ) return entry.getKey(); } return null; } } public static class TestAccessor_ArticleForTop implements CacheNG.SingleValueAccessor<Top, List<TArticle>> { private final CacheNG.AutomaticClient<Tid, List<TArticle>> forTid; private final CacheNG.SingleValueAccessor<Tid,List<TArticle>> accessor; private final TidForTop tft; public TestAccessor_ArticleForTop( CacheNG.AutomaticClient<Tid, List<TArticle>> forTid, CacheNG.SingleValueAccessor<Tid,List<TArticle>> accessor, TidForTop tft) { this.forTid = forTid; this.accessor = accessor; this.tft = tft; } @Override public List<TArticle> fetch( Top top ) { List<Tid> path = tft.tidForTop( top ); List<TArticle> result = Collections.emptyList(); for( Tid tid : path ){ result = union( result, forTid.fetch( tid, accessor ) ); } return result; } } private static final class TidToTopBridge extends CacheBridge<Tid,Top> { private final TidForTop tft; public TidToTopBridge( CacheNG.CacheEventListener<Top> cacheForTop, TidForTop tft ) { super( cacheForTop ); this.tft = tft; } @Override protected Top bridge( Tid tid ) { return tft.topForTid( tid ); } } }