package org.molgenis.data.cache.l1;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.molgenis.data.Entity;
import org.molgenis.data.EntityKey;
import org.molgenis.data.EntityManager;
import org.molgenis.data.cache.utils.EntityHydration;
import org.molgenis.data.meta.model.AttributeFactory;
import org.molgenis.data.meta.model.EntityType;
import org.molgenis.data.meta.model.EntityTypeFactory;
import org.molgenis.data.support.DynamicEntity;
import org.molgenis.data.transaction.MolgenisTransactionManager;
import org.molgenis.test.data.AbstractMolgenisSpringTest;
import org.molgenis.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ContextConfiguration;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.Optional;
import java.util.stream.Stream;
import static java.util.Optional.empty;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.molgenis.data.EntityKey.create;
import static org.molgenis.data.EntityManager.CreationMode.NO_POPULATE;
import static org.molgenis.data.meta.model.EntityType.AttributeRole.ROLE_ID;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@ContextConfiguration(classes = L1CacheTest.Config.class)
public class L1CacheTest extends AbstractMolgenisSpringTest
{
private L1Cache l1Cache;
private EntityType entityType;
private Entity entity1;
private Entity entity2;
private final String transactionID = "__TRANSACTION__";
private final String repository = "TestRepository";
private final String entityID1 = "1";
private final String entityID2 = "2";
@Autowired
private EntityTypeFactory entityTypeFactory;
@Autowired
private AttributeFactory attributeFactory;
@Autowired
private EntityManager entityManager;
@Autowired
private EntityHydration entityHydration;
@Mock
private MolgenisTransactionManager molgenisTransactionManager;
@BeforeClass
public void beforeClass()
{
initMocks(this);
entityType = entityTypeFactory.create(repository);
entityType.addAttribute(attributeFactory.create().setName("ID"), ROLE_ID);
entityType.addAttribute(attributeFactory.create().setName("ATTRIBUTE_1"));
Mockito.when(entityManager.create(entityType, NO_POPULATE)).thenReturn(new DynamicEntity(entityType));
entity1 = new DynamicEntity(entityType);
entity1.set("ID", entityID1);
entity1.set("ATTRIBUTE_1", "test_value_1");
entity2 = new DynamicEntity(entityType);
entity2.set("ID", entityID2);
entity2.set("ATTRIBUTE_1", "test_value_2");
}
@BeforeMethod
public void beforeMethod()
{
l1Cache = new L1Cache(molgenisTransactionManager, entityHydration);
}
@Test
public void testWhenNotInTransaction()
{
// Without a transactionStarted() call the cache does not exist, return null
l1Cache.put(repository, entity1);
Optional<Entity> actualEntity = l1Cache.get(repository, entityID1, entityType);
assertEquals(actualEntity, null);
}
@Test
public void testPutAndGetWhenInTransaction()
{
// Start transaction
l1Cache.transactionStarted(transactionID);
// Entity has not been added to cache, return null
Optional<Entity> actualEntity = l1Cache.get(repository, entityID1, entityType);
assertEquals(actualEntity, null);
// Entity has been added to cache, return entity
l1Cache.put(repository, entity1);
Entity result = l1Cache.get(repository, entityID1, entityType).get();
assertTrue(EntityUtils.equals(result, entity1));
// Cleanup after transaction and expect the cache to be cleared, return null
l1Cache.doCleanupAfterCompletion(transactionID);
actualEntity = l1Cache.get(repository, entityID1, entityType);
assertEquals(actualEntity, null);
}
@Test
public void testEvictStream()
{
// Start transaction
l1Cache.transactionStarted(transactionID);
// Entity has been added to cache, return entity
l1Cache.put(repository, entity1);
l1Cache.put(repository, entity2);
Entity actualEntity = l1Cache.get(repository, entityID1, entityType).get();
assertTrue(EntityUtils.equals(actualEntity, entity1));
actualEntity = l1Cache.get(repository, entityID2, entityType).get();
assertTrue(EntityUtils.equals(actualEntity, entity2));
l1Cache.evict(Stream.of(EntityKey.create(entity1), EntityKey.create(entity2)));
Optional<Entity> result = l1Cache.get(repository, entityID1, entityType);
assertEquals(result, null);
result = l1Cache.get(repository, entityID2, entityType);
assertEquals(result, null);
}
@Test
public void testEvictStreamOfOneEntity()
{
// Start transaction
l1Cache.transactionStarted(transactionID);
// Entity has been added to cache, return entity
l1Cache.put(repository, entity1);
l1Cache.put(repository, entity2);
Entity actualEntity = l1Cache.get(repository, entityID1, entityType).get();
assertTrue(EntityUtils.equals(actualEntity, entity1));
actualEntity = l1Cache.get(repository, entityID2, entityType).get();
assertTrue(EntityUtils.equals(actualEntity, entity2));
l1Cache.evict(Stream.of(EntityKey.create(entity2)));
actualEntity = l1Cache.get(repository, entityID1, entityType).get();
assertTrue(EntityUtils.equals(actualEntity, entity1));
Optional<Entity> result = l1Cache.get(repository, entityID2, entityType);
assertEquals(result, null);
}
@Test
public void testEvictAll()
{
// Start transaction
l1Cache.transactionStarted(transactionID);
// Entity has been added to cache, return entity
l1Cache.put(repository, entity1);
l1Cache.put(repository, entity2);
Entity actualEntity = l1Cache.get(repository, entityID1, entityType).get();
assertTrue(EntityUtils.equals(actualEntity, entity1));
actualEntity = l1Cache.get(repository, entityID2, entityType).get();
assertTrue(EntityUtils.equals(actualEntity, entity2));
l1Cache.evictAll(entityType.getName());
Optional<Entity> result = l1Cache.get(repository, entityID1, entityType);
assertEquals(result, null);
result = l1Cache.get(repository, entityID2, entityType);
assertEquals(result, null);
}
@Test
public void testPutDeletionWhenInTransaction()
{
// Start transaction
l1Cache.transactionStarted(transactionID);
// Entity has been deleted once, return empty
l1Cache.putDeletion(create(entity1));
Optional<Entity> actualEntity = l1Cache.get(repository, entityID1, entityType);
assertEquals(actualEntity, empty());
// Cleanup transaction
l1Cache.doCleanupAfterCompletion(transactionID);
}
@Test
public void testEvictAllWhenInTransaction()
{
// Start transaction
l1Cache.transactionStarted(transactionID);
// Evict entity, return null
l1Cache.put(repository, entity1);
l1Cache.evictAll(repository);
Optional<Entity> actualEntity = l1Cache.get(repository, entityID1, entityType);
assertEquals(actualEntity, null);
// Cleanup transaction
l1Cache.doCleanupAfterCompletion(transactionID);
}
@Configuration
@Import({ EntityHydration.class })
public static class Config
{
@Mock
private EntityManager entityManager;
public Config()
{
initMocks(this);
}
@Bean
public EntityManager entityManager()
{
return entityManager;
}
}
}