org.phenotips.data.internal.MonarchPatientScorerTest.java Source code

Java tutorial

Introduction

Here is the source code for org.phenotips.data.internal.MonarchPatientScorerTest.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/
 */
package org.phenotips.data.internal;

import org.phenotips.data.Feature;
import org.phenotips.data.Patient;
import org.phenotips.data.PatientScorer;
import org.phenotips.data.PatientSpecificity;

import org.xwiki.cache.Cache;
import org.xwiki.cache.CacheException;
import org.xwiki.cache.CacheManager;
import org.xwiki.cache.config.CacheConfiguration;
import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.component.phase.Initializable;
import org.xwiki.component.phase.InitializationException;
import org.xwiki.component.util.ReflectionUtils;
import org.xwiki.configuration.ConfigurationSource;
import org.xwiki.test.mockito.MockitoComponentMockingRule;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Set;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.json.JSONObject;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.internal.matchers.CapturingMatcher;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class MonarchPatientScorerTest {
    private ConfigurationSource configuration;

    @Mock
    private Patient patient;

    @Mock
    private CloseableHttpClient client;

    @Mock
    private CloseableHttpResponse response;

    @Mock
    private HttpEntity responseEntity;

    @Mock
    private Cache<PatientSpecificity> cache;

    private Set<Feature> features = new LinkedHashSet<>();

    @Rule
    public final MockitoComponentMockingRule<PatientScorer> mocker = new MockitoComponentMockingRule<PatientScorer>(
            MonarchPatientScorer.class);

    @Before
    public void setup() throws CacheException, ComponentLookupException {
        MockitoAnnotations.initMocks(this);
        CacheManager cm = this.mocker.getInstance(CacheManager.class);
        when(cm.<PatientSpecificity>createNewCache(any(CacheConfiguration.class))).thenReturn(this.cache);

        this.configuration = this.mocker.getInstance(ConfigurationSource.class, "xwikiproperties");
        when(this.configuration.getProperty("phenotips.patientScoring.monarch.serviceURL",
                "https://monarchinitiative.org/score")).thenReturn("https://monarchinitiative.org/score");

        Feature feature = mock(Feature.class);
        when(feature.getId()).thenReturn("HP:1");
        when(feature.isPresent()).thenReturn(true);
        this.features.add(feature);
        feature = mock(Feature.class);
        when(feature.getId()).thenReturn("HP:2");
        when(feature.isPresent()).thenReturn(false);
        this.features.add(feature);
        feature = mock(Feature.class);
        when(feature.getName()).thenReturn("custom");
        this.features.add(feature);

        ReflectionUtils.setFieldValue(this.mocker.getComponentUnderTest(), "client", this.client);
    }

    @Test
    public void getScoreWithNoFeaturesReturns0() throws ComponentLookupException {
        Mockito.doReturn(Collections.emptySet()).when(this.patient).getFeatures();
        double score = this.mocker.getComponentUnderTest().getScore(this.patient);
        Assert.assertEquals(0.0, score, 0.0);
    }

    @Test
    public void getScoreSearchesRemotely()
            throws ComponentLookupException, URISyntaxException, ClientProtocolException, IOException {
        Mockito.doReturn(this.features).when(this.patient).getFeatures();
        URI expectedURI = new URI("https://monarchinitiative.org/score");
        CapturingMatcher<HttpPost> reqCapture = new CapturingMatcher<>();
        when(this.client.execute(Matchers.argThat(reqCapture))).thenReturn(this.response);
        when(this.response.getEntity()).thenReturn(this.responseEntity);
        when(this.responseEntity.getContent())
                .thenReturn(IOUtils.toInputStream("{\"scaled_score\":2}", StandardCharsets.UTF_8));
        double score = this.mocker.getComponentUnderTest().getScore(this.patient);
        Assert.assertEquals(expectedURI, reqCapture.getLastValue().getURI());
        String content = URLDecoder
                .decode(IOUtils.toString(reqCapture.getLastValue().getEntity().getContent(), "UTF-8"), "UTF-8");
        Assert.assertTrue(content.startsWith("annotation_profile="));
        JSONObject actualJson = new JSONObject(content.substring("annotation_profile=".length()));
        JSONObject expectedJson = new JSONObject(
                "{\"features\":[{\"id\":\"HP:1\"},{\"id\":\"HP:2\",\"isPresent\":false}]}");
        Assert.assertTrue(expectedJson.similar(actualJson));
        Assert.assertEquals("application/x-www-form-urlencoded; charset=UTF-8",
                reqCapture.getLastValue().getEntity().getContentType().getValue());
        Assert.assertEquals(2.0, score, 0.0);
    }

    @Test
    public void getScoreUsesCache()
            throws ComponentLookupException, URISyntaxException, ClientProtocolException, IOException {
        Mockito.doReturn(this.features).when(this.patient).getFeatures();
        PatientSpecificity spec = mock(PatientSpecificity.class);
        when(this.cache.get("HP:1-HP:2")).thenReturn(spec);
        when(spec.getScore()).thenReturn(2.0);
        double score = this.mocker.getComponentUnderTest().getScore(this.patient);
        Assert.assertEquals(2.0, score, 0.0);
        Mockito.verifyZeroInteractions(this.client);
    }

    @Test
    public void getScoreWithNoResponseReturnsNegative1()
            throws ComponentLookupException, URISyntaxException, ClientProtocolException, IOException {
        Mockito.doReturn(this.features).when(this.patient).getFeatures();
        URI expectedURI = new URI("https://monarchinitiative.org/score");
        CapturingMatcher<HttpPost> reqCapture = new CapturingMatcher<>();
        when(this.client.execute(Matchers.argThat(reqCapture))).thenReturn(this.response);
        when(this.response.getEntity()).thenReturn(this.responseEntity);
        when(this.responseEntity.getContent()).thenReturn(IOUtils.toInputStream("", StandardCharsets.UTF_8));
        double score = this.mocker.getComponentUnderTest().getScore(this.patient);
        Assert.assertEquals(expectedURI, reqCapture.getLastValue().getURI());
        Assert.assertEquals(-1.0, score, 0.0);
    }

    @Test
    public void getScoreWithExceptionReturnsNegative1() throws Exception {
        Mockito.doReturn(this.features).when(this.patient).getFeatures();
        when(this.client.execute(any(HttpUriRequest.class))).thenThrow(new IOException());
        double score = this.mocker.getComponentUnderTest().getScore(this.patient);
        Assert.assertEquals(-1.0, score, 0.0);
        Mockito.verify(this.cache, Mockito.never()).set(any(String.class), any(PatientSpecificity.class));
    }

    @Test
    public void getSpecificityWithNoFeaturesReturns0() throws ComponentLookupException {
        Mockito.doReturn(Collections.emptySet()).when(this.patient).getFeatures();
        CapturingMatcher<PatientSpecificity> specCapture = new CapturingMatcher<>();
        Mockito.doNothing().when(this.cache).set(Matchers.eq(""), Matchers.argThat(specCapture));
        Date d1 = new Date();
        this.mocker.getComponentUnderTest().getSpecificity(this.patient);
        Date d2 = new Date();
        PatientSpecificity spec = specCapture.getLastValue();
        Assert.assertEquals(0.0, spec.getScore(), 0.0);
        Assert.assertEquals("monarchinitiative.org", spec.getComputingMethod());
        Assert.assertFalse(d1.after(spec.getComputationDate()));
        Assert.assertFalse(d2.before(spec.getComputationDate()));
    }

    @Test
    public void getSpecificitySearchesRemotely() throws Exception {
        Mockito.doReturn(this.features).when(this.patient).getFeatures();
        URI expectedURI = new URI("https://monarchinitiative.org/score");
        CapturingMatcher<HttpPost> reqCapture = new CapturingMatcher<>();
        when(this.client.execute(Matchers.argThat(reqCapture))).thenReturn(this.response);
        when(this.response.getEntity()).thenReturn(this.responseEntity);
        when(this.responseEntity.getContent())
                .thenReturn(IOUtils.toInputStream("{\"scaled_score\":2}", StandardCharsets.UTF_8));
        CapturingMatcher<PatientSpecificity> specCapture = new CapturingMatcher<>();
        Mockito.doNothing().when(this.cache).set(Matchers.eq("HP:1-HP:2"), Matchers.argThat(specCapture));
        Date d1 = new Date();
        this.mocker.getComponentUnderTest().getSpecificity(this.patient);
        Date d2 = new Date();
        PatientSpecificity spec = specCapture.getLastValue();
        Assert.assertEquals(expectedURI, reqCapture.getLastValue().getURI());
        String content = URLDecoder
                .decode(IOUtils.toString(reqCapture.getLastValue().getEntity().getContent(), "UTF-8"), "UTF-8");
        Assert.assertTrue(content.startsWith("annotation_profile="));
        JSONObject actualJson = new JSONObject(content.substring("annotation_profile=".length()));
        JSONObject expectedJson = new JSONObject(
                "{\"features\":[{\"id\":\"HP:1\"},{\"id\":\"HP:2\",\"isPresent\":false}]}");
        Assert.assertTrue(expectedJson.similar(actualJson));
        Assert.assertEquals("application/x-www-form-urlencoded; charset=UTF-8",
                reqCapture.getLastValue().getEntity().getContentType().getValue());
        Assert.assertEquals(2.0, spec.getScore(), 0.0);
        Assert.assertEquals("monarchinitiative.org", spec.getComputingMethod());
        Assert.assertFalse(d1.after(spec.getComputationDate()));
        Assert.assertFalse(d2.before(spec.getComputationDate()));
    }

    @Test
    public void getSpecificityUsesCache() throws Exception {
        Mockito.doReturn(this.features).when(this.patient).getFeatures();
        PatientSpecificity spec = mock(PatientSpecificity.class);
        when(this.cache.get("HP:1-HP:2")).thenReturn(spec);
        Assert.assertSame(spec, this.mocker.getComponentUnderTest().getSpecificity(this.patient));
        Mockito.verifyZeroInteractions(this.client);
    }

    @Test
    public void getSpecificityWithNoResponseReturnsNull() throws Exception {
        Mockito.doReturn(this.features).when(this.patient).getFeatures();
        when(this.client.execute(any(HttpUriRequest.class))).thenReturn(this.response);
        when(this.response.getEntity()).thenReturn(this.responseEntity);
        when(this.responseEntity.getContent()).thenReturn(IOUtils.toInputStream("", StandardCharsets.UTF_8));
        Assert.assertNull(this.mocker.getComponentUnderTest().getSpecificity(this.patient));
    }

    @Test(expected = InitializationException.class)
    public void initializationFailsWhenCreatingCacheFails()
            throws ComponentLookupException, CacheException, InitializationException {
        CacheManager cm = this.mocker.getInstance(CacheManager.class);
        when(cm.<PatientSpecificity>createNewCache(any(CacheConfiguration.class)))
                .thenThrow(new CacheException("failed"));
        ((org.xwiki.component.phase.Initializable) this.mocker.getComponentUnderTest()).initialize();
    }

    @Test
    public void checkURLConfigurable() throws ComponentLookupException, URISyntaxException, ClientProtocolException,
            IOException, InitializationException {
        when(this.configuration.getProperty("phenotips.patientScoring.monarch.serviceURL",
                "https://monarchinitiative.org/score")).thenReturn("http://proxy/score");
        Mockito.doReturn(this.features).when(this.patient).getFeatures();
        URI expectedURI = new URI("http://proxy/score");
        CapturingMatcher<HttpPost> reqCapture = new CapturingMatcher<>();
        when(this.client.execute(Matchers.argThat(reqCapture))).thenReturn(this.response);
        when(this.response.getEntity()).thenReturn(this.responseEntity);
        when(this.responseEntity.getContent())
                .thenReturn(IOUtils.toInputStream("{\"scaled_score\":2}", StandardCharsets.UTF_8));
        // Since the component was already initialized in setUp() with the default URL, re-initialize it
        // with the new configuration mock
        ((Initializable) this.mocker.getComponentUnderTest()).initialize();
        ReflectionUtils.setFieldValue(this.mocker.getComponentUnderTest(), "client", this.client);
        double score = this.mocker.getComponentUnderTest().getScore(this.patient);
        Assert.assertEquals(expectedURI, reqCapture.getLastValue().getURI());
        String content = URLDecoder
                .decode(IOUtils.toString(reqCapture.getLastValue().getEntity().getContent(), "UTF-8"), "UTF-8");
        Assert.assertTrue(content.startsWith("annotation_profile="));
        JSONObject actualJson = new JSONObject(content.substring("annotation_profile=".length()));
        JSONObject expectedJson = new JSONObject(
                "{\"features\":[{\"id\":\"HP:1\"},{\"id\":\"HP:2\",\"isPresent\":false}]}");
        Assert.assertTrue(expectedJson.similar(actualJson));
        Assert.assertEquals("application/x-www-form-urlencoded; charset=UTF-8",
                reqCapture.getLastValue().getEntity().getContentType().getValue());
        Assert.assertEquals(2.0, score, 0.0);
    }
}