org.cruk.genologics.api.cache.GenologicsAPICacheTest.java Source code

Java tutorial

Introduction

Here is the source code for org.cruk.genologics.api.cache.GenologicsAPICacheTest.java

Source

/*
 * CRUK-CI Genologics REST API Java Client.
 * Copyright (C) 2013 Cancer Research UK Cambridge Institute.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.cruk.genologics.api.cache;

import static org.junit.Assert.*;
import static org.cruk.genologics.api.cache.GenologicsAPICache.NO_STATE_VALUE;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import net.sf.ehcache.Element;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.cruk.genologics.api.GenologicsAPI;
import org.cruk.genologics.api.unittests.UnitTestApplicationContextFactory;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.genologics.ri.LimsEntity;
import com.genologics.ri.LimsLink;
import com.genologics.ri.Locatable;
import com.genologics.ri.artifact.Artifact;
import com.genologics.ri.artifact.OutputType;
import com.genologics.ri.container.Container;
import com.genologics.ri.containertype.ContainerType;
import com.genologics.ri.containertype.ContainerTypeLink;
import com.genologics.ri.file.GenologicsFile;
import com.genologics.ri.process.GenologicsProcess;
import com.genologics.ri.processexecution.ExecutableInputOutputMap;
import com.genologics.ri.processexecution.ExecutableProcess;
import com.genologics.ri.project.Project;
import com.genologics.ri.project.ResearcherLink;
import com.genologics.ri.researcher.Researcher;
import com.genologics.ri.sample.Sample;
import com.genologics.ri.userdefined.UDF;

public class GenologicsAPICacheTest {
    protected Log logger = LogFactory.getLog(GenologicsAPICacheTest.class);

    protected AbstractApplicationContext context;
    private boolean credentialsSet;
    protected GenologicsAPI api;
    protected GenologicsAPICache cacheAspect;
    protected RestCallTrackingAspect testAspect;

    private Researcher apiUser;
    private ContainerType plateType;
    private ContainerType tubeType;

    private Project project;
    private Container container;
    private Sample[] samples;
    private Container poolContainer;

    public GenologicsAPICacheTest() {
    }

    @Before
    public void setup() throws Exception {
        // Note - because of the Ehcache manager singleton in the context,
        // we need a new ApplicationContext for each test in this class.

        context = new ClassPathXmlApplicationContext("/org/cruk/genologics/api/genologics-client-context.xml",
                "/org/cruk/genologics/api/genologics-cache-context.xml", "unittest-cache-context.xml");

        api = context.getBean("genologicsAPI", GenologicsAPI.class);
        cacheAspect = context.getBean(GenologicsAPICache.class);
        testAspect = context.getBean(RestCallTrackingAspect.class);

        credentialsSet = UnitTestApplicationContextFactory.setCredentialsOnApi(api);
    }

    @After
    public void cleanup() {
        testAspect.setEnabled(false);

        if (project != null) {
            logger.fatal("Need to delete " + project.getUri() + " through operations.");
        }

        context.close();
    }

    @Test
    public void testCreateCacheElement() throws URISyntaxException {
        Locatable l = new Artifact();

        String base = "http://limsdev.cri.camres.org:8080/v2/api/";

        l.setUri(new URI(base + "artifacts/234"));

        Element e = cacheAspect.createCacheElement(l);

        assertEquals("Version wrong without state", NO_STATE_VALUE, e.getVersion());

        l.setUri(new URI(base + "artifacts/56-362?name=thing"));

        e = cacheAspect.createCacheElement(l);

        assertEquals("Version wrong with query without state", NO_STATE_VALUE, e.getVersion());

        l.setUri(new URI(base + "artifacts/56-362?state=5432"));

        e = cacheAspect.createCacheElement(l);

        assertEquals("Version wrong with state", 5432L, e.getVersion());
    }

    private void checkCredentialsSet() {
        Assume.assumeTrue("Could not set credentials for the API, which is needed for this test. "
                + "Put a \"testcredentials.properties\" file on the class path.", credentialsSet);
    }

    @Test
    public void testLoadOrRetrieve() throws Throwable {
        checkCredentialsSet();

        //CacheManager mockCacheManager = EasyMock.createMock(CacheManager.class);
        //EasyMock.expect(mockCacheManager.getCache(Artifact.class.getName())).andReturn(value);

        cacheAspect.getCache(Artifact.class).removeAll();

        String base = api.getServerApiAddress();

        Signature jpSig = EasyMock.createMock(Signature.class);
        EasyMock.expect(jpSig.getName()).andReturn("retrieve").times(0, 1);

        Artifact a1 = new Artifact();
        a1.setUri(new URI(base + "/artifacts/2-1771911?state=1294907"));
        a1.setLimsid("2-1771911");

        Object[] a1args = { a1.getUri(), a1.getClass() };

        ProceedingJoinPoint pjp1 = EasyMock.createStrictMock(ProceedingJoinPoint.class);
        EasyMock.expect(pjp1.getArgs()).andReturn(a1args).times(3);
        EasyMock.expect(pjp1.getSignature()).andReturn(jpSig).times(0, 1);
        EasyMock.expect(pjp1.proceed()).andReturn(a1).once();

        EasyMock.replay(pjp1, jpSig);

        Object returned = cacheAspect.retrieve(pjp1);

        EasyMock.verify(pjp1, jpSig);
        assertSame("Did not return a1", a1, returned);

        Artifact a2 = new Artifact();
        a2.setUri(new URI(base + "/artifacts/2-1771911?state=1500000"));
        a2.setLimsid("2-1771911");

        Object[] a2args = { a2.getUri(), a2.getClass() };

        ProceedingJoinPoint pjp2 = EasyMock.createStrictMock(ProceedingJoinPoint.class);
        EasyMock.expect(pjp2.getArgs()).andReturn(a2args).times(3);
        EasyMock.expect(pjp2.getSignature()).andReturn(jpSig).times(0, 1);
        EasyMock.expect(pjp2.proceed()).andReturn(a2).once();

        EasyMock.reset(jpSig);
        EasyMock.replay(pjp2, jpSig);

        returned = cacheAspect.retrieve(pjp2);
        assertSame("Did not return a2", a2, returned);

        EasyMock.verify(pjp2, jpSig);

        ProceedingJoinPoint pjp3 = EasyMock.createStrictMock(ProceedingJoinPoint.class);
        EasyMock.expect(pjp3.getArgs()).andReturn(a1args).times(3);

        EasyMock.reset(jpSig);
        EasyMock.replay(pjp3, jpSig);

        returned = cacheAspect.retrieve(pjp3);

        EasyMock.verify(pjp3, jpSig);
        assertSame("Did not return a2", a2, returned);

        Object[] a4args = { base + "/artifacts/2-1771911", Artifact.class };

        ProceedingJoinPoint pjp4 = EasyMock.createStrictMock(ProceedingJoinPoint.class);
        EasyMock.expect(pjp4.getArgs()).andReturn(a4args).times(3);
        EasyMock.expect(pjp4.getSignature()).andReturn(jpSig).times(0, 1);
        //EasyMock.expect(pjp4.proceed()).andReturn(a2).once();

        EasyMock.reset(jpSig);
        EasyMock.replay(pjp4, jpSig);

        returned = cacheAspect.retrieve(pjp4);

        EasyMock.verify(pjp4, jpSig);
        assertSame("Did not return a2", a2, returned);

        /*
        ProceedingJoinPoint pjp5 = EasyMock.createStrictMock(ProceedingJoinPoint.class);
        EasyMock.expect(pjp5.getArgs()).andReturn(a4args).times(3);
        EasyMock.expect(pjp5.getSignature()).andReturn(jpSig).times(0, 1);
            
        EasyMock.reset(jpSig);
        EasyMock.replay(pjp5, jpSig);
            
        returned = cacheAspect.retrieve(pjp5);
            
        EasyMock.verify(pjp5, jpSig);
        assertSame("Did not return a2", a2, returned);
        */
    }

    @Test
    public void readonlyTest() throws Exception {
        Assume.assumeTrue("Not in the CRUK-CI institute. This test will not work.",
                UnitTestApplicationContextFactory.inCrukCI());

        checkCredentialsSet();

        // Load reference links

        testAspect.setEnabled(true);

        String researcherURI = api.getServerApiAddress() + "researchers/3";
        String plateURI = api.getServerApiAddress() + "containertypes/1";
        String tubeURI = api.getServerApiAddress() + "containertypes/2";

        testAspect.setAllowedUris(researcherURI, plateURI, tubeURI);

        ResearcherLink researcherLink = new ResearcherLink(new URI(researcherURI));
        apiUser = api.load(researcherLink);

        assertEquals("API user wrong", "apiuser", apiUser.getCredentials().getUsername());

        plateType = api.load(new ContainerTypeLink(new URI(plateURI)));

        assertEquals("Plate type wrong", "96 well plate", plateType.getName());

        tubeType = api.load("2", ContainerType.class);

        assertEquals("Tube type wrong", "Tube", tubeType.getName());

        testAspect.clear();

        apiUser = api.load("3", Researcher.class);
        plateType = api.retrieve(plateURI, ContainerType.class);

    }

    /**
     * This test modifies the database, and so is controlled by the system property
     * "live.cache.test".
     *
     * @throws Exception
     */
    @Test
    public void fullTest() throws Exception {
        Assume.assumeTrue("Not in the CRUK-CI institute. This test will not work.",
                UnitTestApplicationContextFactory.inCrukCI());

        checkCredentialsSet();

        boolean runThisTest = Boolean.parseBoolean(System.getProperty("live.cache.test", Boolean.FALSE.toString()));

        Assume.assumeTrue("Not running the test \"GenologicsAPICachingAspectTest.fullTest\". "
                + "Set the property -Dlive.cache.test=true to make it run.", runThisTest);

        final String projectName = "Caching Aspect Test";

        if (apiUser == null || plateType == null) {
            readonlyTest();
        }

        // Find or create project

        Map<String, String> projectSearch = Collections.singletonMap("name", projectName);

        List<LimsLink<Project>> foundProjects = api.find(projectSearch, Project.class);

        if (foundProjects.isEmpty()) {
            project = new Project();
            project.setName(projectName);
            project.setResearcher(apiUser);
            project.setOpenDate(new Date());

            api.create(project);
        } else {
            LimsLink<Project> link = foundProjects.get(0);
            testAspect.setAllowedUris(link.getUri().toString());
            project = api.load(link);
            testAspect.clear();
        }

        testNotReloading(project);

        // Create a container for some samples.

        container = new Container(plateType, projectName + " Plate");

        api.create(container);

        testNotReloading(container);

        // Create some samples in the container.

        samples = new Sample[5];

        for (int i = 0; i < samples.length; i++) {
            Sample s = new Sample();
            s.setName(projectName + " sample #" + (i + 1));
            s.setDateReceived(new Date());
            s.setProject(project);
            s.setSubmitter(apiUser);

            UDF.setUDF(s, "Read Length", "50");
            UDF.setUDF(s, "Column", "2");
            UDF.setUDF(s, "Concentration", "-1");
            UDF.setUDF(s, "Library Type", "Other");
            UDF.setUDF(s, "Number of Lanes", "2");
            UDF.setUDF(s, "Pool Size", "-1");
            UDF.setUDF(s, "Priority Status", "Standard");
            UDF.setUDF(s, "Reference Genome", "Homo sapiens [GRCh37]");
            UDF.setUDF(s, "Row", "A");
            UDF.setUDF(s, "Sample Source", "Not Assigned");
            UDF.setUDF(s, "Sample Type", "DNA");
            UDF.setUDF(s, "Sequencer", "Not Assigned");
            UDF.setUDF(s, "SLX Identifier", "SLX-0000");
            UDF.setUDF(s, "Sequencing Type", "Paired End");
            UDF.setUDF(s, "Index Type", "Unspecified (Other)");
            UDF.setUDF(s, "Volume", "-1");
            UDF.setUDF(s, "Average Library Length", "-1");
            UDF.setUDF(s, "Version Number", "Not Assigned");
            UDF.setUDF(s, "Workflow", "MiSeq Express");

            s.setCreationLocation(container, (char) ('A' + i) + ":" + Integer.valueOf(i + 1));

            samples[i] = s;
        }

        api.createAll(Arrays.asList(samples));

        testNotReloading(samples[0]);

        // Try loading the samples' artifacts by individually and by batch.

        @SuppressWarnings("unchecked")
        LimsLink<Artifact>[] artifactLinks = new LimsLink[samples.length];

        for (int i = 0; i < samples.length; i++) {
            artifactLinks[i] = samples[i].getArtifact();
        }

        testAspect.setAllowedUris(artifactLinks[0], artifactLinks[4]);

        Artifact[] artifacts = new Artifact[samples.length];

        artifacts[0] = api.load(artifactLinks[0]);
        artifacts[4] = api.load(artifactLinks[4]);

        testNotReloading(artifacts[0]);

        // Mass fetch. Should only fetch the middle three.

        testAspect.setAllowedUris(artifactLinks[1], artifactLinks[2], artifactLinks[3]);

        api.loadAll(Arrays.asList(artifactLinks)).toArray(artifacts);

        testNotReloading(artifacts[2]);

        // Check fetched artifacts match their original links.

        for (int i = 0; i < samples.length; i++) {
            assertEquals("Sample/artifact " + i + " don't match", samples[i].getArtifact().getLimsid(),
                    artifacts[i].getLimsid());
        }

        // Try pooling the artifacts.

        // First, assign reagent labels.
        NumberFormat reagentFormat = NumberFormat.getIntegerInstance();
        reagentFormat.setMinimumIntegerDigits(3);

        for (int i = 0; i < artifacts.length; i++) {
            artifacts[i].addReagentLabel("A" + reagentFormat.format(i + 1));
        }
        api.updateAll(Arrays.asList(artifacts));

        // Now the actual pooling.

        poolContainer = new Container(tubeType, projectName + " pool container");

        api.create(poolContainer);

        ExecutableProcess execProcess = new ExecutableProcess("Pool Accepted SLX", apiUser);
        ExecutableInputOutputMap iomap = execProcess.newInputOutputMap();

        iomap.setShared(true);
        iomap.setInputs(Arrays.asList(artifacts));
        iomap.setOutput(OutputType.ANALYTE, poolContainer, "1:1");

        GenologicsProcess poolProcess = api.executeProcess(execProcess);
        assertNotNull("No pool process returned", poolProcess);

        testNotReloading(poolProcess);

        // Try attaching a file to a sample.

        File toUpload = new File("pom.xml").getAbsoluteFile();

        try {
            GenologicsFile uploadedFile = api.uploadFile(samples[0], toUpload.toURI().toURL(), false);
            assertNotNull("No file returned", uploadedFile);

            testNotReloading(uploadedFile);

            try {
                api.deleteAndRemoveFile(uploadedFile);

                Object fetched = cacheAspect.getCache(GenologicsFile.class)
                        .getQuiet(cacheAspect.keyFromLocatable(uploadedFile));
                assertNull("Deleted file is still in the cache", fetched);
            } catch (IOException e) {
                api.delete(uploadedFile);
            }
        } catch (IOException e) {
            logger.warn("Could not upload the file to the server: ", e);
        }
    }

    private <E extends LimsEntity<E>> void testNotReloading(E entity) {
        @SuppressWarnings("unchecked")
        Class<E> entityClass = (Class<E>) entity.getClass();

        String[] currentlyAllowed = testAspect.getAllowedUris();

        try {
            // Test cached.
            testAspect.clear();
            api.load(entity.getLimsid(), entityClass);
            api.retrieve(entity.getUri(), entityClass);
            api.retrieve(entity.getUri().toString(), entityClass);

            // Reload should be allowed through.
            //testAspect.setAllowedUris(entity.getUri().toString());
            //api.reload(entity);
        } finally {
            testAspect.setAllowedUris(currentlyAllowed);
        }
    }
}