content: /*************************************************************************
  *                                                                        *
  *  This file is part of the 20n/act project.                             *
  *  20n/act enables DNA prediction for synthetic biology/bioengineering.  *
  *  Copyright (C) 2017 20n Labs, Inc.                                     *
  *                                                                        *
  *  Please direct all queries to act@20n.com.                             *
  *                                                                        *
  *  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 com.act.biointerpretation.networkanalysis;
  
  import com.act.lcms.MS1;
  import com.act.lcms.v2.DetectedPeak;
  import com.act.lcms.v2.Ion;
  import com.act.lcms.v2.IonCalculator;
  import com.act.lcms.v2.LcmsIon;
  import com.act.lcms.v2.Metabolite;
  import com.act.lcms.v2.MolecularStructure;
  import com.act.lcms.v2.PeakSpectrum;
  import org.junit.Before;
  import org.junit.Test;
  import org.mockito.Mockito;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Optional;
  import java.util.Set;
  
  import static org.junit.Assert.assertEquals;
  
  /**
   * Tests for class PrecursorReport.
   */
  public class PrecursorReportTest {
  
    private static final String INCHI_1 = "A";
    private static final String INCHI_2 = "B";
  
    private static final Double MASS_1 = 10.0;
    private static final Double MASS_2 = 30.0;
  
    private static final Ion MET_1_ION_1 = new LcmsIon(15.0, Mockito.mock(MS1.MetlinIonMass.class));
    private static final Ion MET_1_ION_2 = new LcmsIon(20.0, Mockito.mock(MS1.MetlinIonMass.class));
    private static final Ion MET_2_ION_1 = new LcmsIon(25.0, Mockito.mock(MS1.MetlinIonMass.class));
  
    private static Metabolite METABOLITE_1;
    private static Metabolite METABOLITE_2;
  
    private static List<NetworkNode> nodes = new ArrayList<>();
    private static Map<String, Integer> inchiToID = new HashMap<>();
  
    private static final Double MASS_TOLERANCE = 0.01;
  
    private static final Set<String> ions = new HashSet<String>() {{
      add("M+H");
      add("M+Na");
    }};
  
    private static final List<DetectedPeak> NO_PEAKS = Collections.EMPTY_LIST;
    private static final List<DetectedPeak> SOME_PEAKS = new ArrayList<DetectedPeak>() {{
      add(Mockito.mock(DetectedPeak.class));
    }};
  
    private IonCalculator mockIonCalculator = Mockito.mock(IonCalculator.class);
  
    @Before
    public void createMockNodes() {
      METABOLITE_1 = getMockMetabolite(INCHI_1, MASS_1);
      METABOLITE_2 = getMockMetabolite(INCHI_2, MASS_2);
  
      Mockito.when(mockIonCalculator.getSelectedIons(METABOLITE_1, ions, MS1.IonMode.POS)).thenReturn(
          Arrays.asList(MET_1_ION_1, MET_1_ION_2));
      Mockito.when(mockIonCalculator.getSelectedIons(METABOLITE_2, ions, MS1.IonMode.POS)).thenReturn(
          Arrays.asList(MET_2_ION_1));
  
      nodes.add(new NetworkNode(METABOLITE_1));
      nodes.add(new NetworkNode(METABOLITE_2));
      nodes.forEach(n -> inchiToID.put(n.getMetabolite().getStructure().get().getInchi(), n.getUID()));
    }
  
    private Metabolite getMockMetabolite(String s, double mass) {
      MolecularStructure structure = Mockito.mock(MolecularStructure.class);
      Mockito.when(structure.getInchi()).thenReturn(s);
      Mockito.when(structure.getMonoIsotopicMass()).thenReturn(mass);
  
      Metabolite m = Mockito.mock(Metabolite.class);
      Mockito.when(m.getStructure()).thenReturn(Optional.of(structure));
      Mockito.when(m.getMonoIsotopicMass()).thenReturn(mass);
      return m;
    }
  
    /**
     * Ensures that the process of matching peaks from a PeakSpectrum to nodes in a PrecursorReport is done properly.
     * If any of a node's ions matches a peak, it should come out as a hit.
     * if none of a node's ions match any peak, it should come out as a miss.
     */
    @Test
    public void testAddLcmsData() {
      // Arrange
      MetabolismNetwork network = new MetabolismNetwork();
      nodes.forEach(network::addNode);
      network.addEdgeFromInchis(Arrays.asList(INCHI_1), Arrays.asList(INCHI_2));
      NetworkNode node1 = network.getNodeByInchi(INCHI_1);
      NetworkNode node2 = network.getNodeByInchi(INCHI_2);
      PrecursorReport report = network.getPrecursorReport(node2, 1);
  
      PeakSpectrum spectrum = Mockito.mock(PeakSpectrum.class);
      Mockito.when(spectrum.getPeaksByMZ(Mockito.any(), Mockito.any())).thenReturn(NO_PEAKS);
      Mockito.when(spectrum.getPeaksByMZ(MET_1_ION_1.getMzValue(), 1.0)).thenReturn(SOME_PEAKS);
  
      // Act
      report.addLcmsData(spectrum, mockIonCalculator, ions);
  
      // Assert
      assertEquals("Metabolite 1, matching at one ion, is positive.", new Double(1.0), report.getLcmsConfidence(node1));
      assertEquals("Metabolite 2, matching at no ions, is negative.", new Double(0.0), report.getLcmsConfidence(node2));
    }
  }
  