/*************************************************************************
* *
* 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.mechanisminspection;
import act.server.NoSQLAPI;
import act.shared.Reaction;
import com.act.biointerpretation.desalting.ReactionDesalter;
import com.act.biointerpretation.test.util.MockedNoSQLAPI;
import com.act.biointerpretation.test.util.TestUtils;
import junit.framework.Assert;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class MechanisticValidatorTest {
TestUtils utilsObject;
@Before
public void setUp() throws Exception {
// In case we ever use Mockito annotations, don't forget to initialize them.
MockitoAnnotations.initMocks(ReactionDesalter.class);
utilsObject = new TestUtils();
}
@Test
public void testAllRosHaveSubstrateAndProductCounts() throws Exception {
ErosCorpus erosCorpus = new ErosCorpus();
erosCorpus.loadValidationCorpus();
for (Ero ro : erosCorpus.getRos()) {
Assert.assertNotNull(ro.getProduct_count());
Assert.assertNotNull(ro.getSubstrate_count());
}
}
@Test
public void testMechanisticValidatorIsMatchingTheCorrectROsToReaction() throws Exception {
List<Reaction> testReactions = new ArrayList<>();
Map<Long, String> idToInchi = new HashMap<>();
// The first inchi is a cofactor while the second is not.
idToInchi.put(1L, "InChI=1S/p+1");
idToInchi.put(2L, "InChI=1S/C5H10O/c1-3-4-5(2)6/h3-4H2,1-2H3");
idToInchi.put(3L, "InChI=1S/C5H12O/c1-3-4-5(2)6/h5-6H,3-4H2,1-2H3/t5-/m1/s1");
JSONObject expectedResult = new JSONObject();
// The first RO is an alcohol oxidation to aldehyde. The score is 4 since the curated ERO list sets the category
// to perfect.
expectedResult.put("3", "4");
// This RO is for primary alcohol to aldehyde. Similar but more specific RO than the above.
expectedResult.put("337", "4");
// This RO is for secondary alcohol to aldehyde. Similar again to the above.
expectedResult.put("340", "4");
Long[] products = {1L, 2L};
Long[] substrates = {3L};
Integer[] substrateCoefficients = {1};
Integer[] productCoefficients = {1, 1};
Reaction testReaction =
utilsObject.makeTestReaction(substrates, products, substrateCoefficients, productCoefficients, true);
testReactions.add(testReaction);
MockedNoSQLAPI mockAPI = new MockedNoSQLAPI();
mockAPI.installMocks(testReactions, utilsObject.SEQUENCES, utilsObject.ORGANISM_NAMES, idToInchi);
NoSQLAPI mockNoSQLAPI = mockAPI.getMockNoSQLAPI();
MechanisticValidator mechanisticValidator = new MechanisticValidator(mockNoSQLAPI);
mechanisticValidator.init();
mechanisticValidator.run();
assertEquals("One reaction should be written to the DB", 1, mockAPI.getWrittenReactions().size());
assertEquals("The mechanistic validator result should be equal to the expected result",
expectedResult.toString(), mockAPI.getWrittenReactions().get(0).getMechanisticValidatorResult().toString());
}
@Test
public void testMechanisticValidatorDoesNotAddAnyROScoresWhenNoMatchesHappen() throws Exception {
List<Reaction> testReactions = new ArrayList<>();
Map<Long, String> idToInchi = new HashMap<>();
// The first inchi is a cofactor while the second is not.
idToInchi.put(1L, "InChI=1S/p+1");
idToInchi.put(2L, "InChI=1S/H2O/h1H2");
Long[] products = {2L};
Long[] substrates = {1L};
Integer[] substrateCoefficients = {1};
Integer[] productCoefficients = {1};
Reaction testReaction =
utilsObject.makeTestReaction(substrates, products, substrateCoefficients, productCoefficients, true);
testReactions.add(testReaction);
MockedNoSQLAPI mockAPI = new MockedNoSQLAPI();
mockAPI.installMocks(testReactions, utilsObject.SEQUENCES, utilsObject.ORGANISM_NAMES, idToInchi);
NoSQLAPI mockNoSQLAPI = mockAPI.getMockNoSQLAPI();
MechanisticValidator mechanisticValidator = new MechanisticValidator(mockNoSQLAPI);
mechanisticValidator.init();
mechanisticValidator.run();
assertEquals("One reaction should be written to the DB", 1, mockAPI.getWrittenReactions().size());
assertEquals("The mechanistic validator result should be null since no ROs should react with the reaction",
null, mockAPI.getWrittenReactions().get(0).getMechanisticValidatorResult());
}
@Test
public void testMechanisticValidatorIsMatchingTheCorrectROsToReactionThatAreNotPerfect() throws Exception {
List<Reaction> testReactions = new ArrayList<>();
Map<Long, String> idToInchi = new HashMap<>();
// The first inchi is a cofactor while the second is not.
idToInchi.put(1L, "InChI=1S/p+1");
idToInchi.put(2L, "InChI=1S/C25H40N7O19P3S/c1-25(2,20(38)23(39)28-6-5-14(33)27-7-8-55-16(36)4-3-15(34)35)10-48-54(45,46)51-53(43,44)47-9-13-19(50-52(40,41)42)18(37)24(49-13)32-12-31-17-21(26)29-11-30-22(17)32/h11-13,18-20,24,37-38H,3-10H2,1-2H3,(H,27,33)(H,28,39)(H,34,35)(H,43,44)(H,45,46)(H2,26,29,30)(H2,40,41,42)/t13-,18-,19-,20+,24-/m1/s1");
idToInchi.put(3L, "InChI=1S/C4H6O3/c5-3-1-2-4(6)7/h3H,1-2H2,(H,6,7)");
Long[] products = {1L, 2L};
Long[] substrates = {3L};
JSONObject expectedResult = new JSONObject();
// This RO has no name, but it currently in the "validated" category. Hence, the score should be 3.
expectedResult.put("284", "3");
Integer[] substrateCoefficients = {1};
Integer[] productCoefficients = {1, 1};
Reaction testReaction =
utilsObject.makeTestReaction(substrates, products, substrateCoefficients, productCoefficients, true);
testReactions.add(testReaction);
MockedNoSQLAPI mockAPI = new MockedNoSQLAPI();
mockAPI.installMocks(testReactions, utilsObject.SEQUENCES, utilsObject.ORGANISM_NAMES, idToInchi);
NoSQLAPI mockNoSQLAPI = mockAPI.getMockNoSQLAPI();
MechanisticValidator mechanisticValidator = new MechanisticValidator(mockNoSQLAPI);
mechanisticValidator.init();
mechanisticValidator.run();
assertEquals("One reaction should be written to the DB", 1, mockAPI.getWrittenReactions().size());
assertEquals("The mechanistic validator result should be equal to the expected result",
expectedResult.toString(), mockAPI.getWrittenReactions().get(0).getMechanisticValidatorResult().toString());
}
@Test
public void testMechanisticValidatorIsCorrectlyUsingCoefficients() throws Exception {
List<Reaction> testReactions = new ArrayList<>();
Map<Long, String> idToInchi = new HashMap<>();
idToInchi.put(1L, "InChI=1S/CH4O/c1-2/h2H,1H3");
idToInchi.put(2L, "InChI=1S/CH5O4P/c1-5-6(2,3)4/h1H3,(H2,2,3,4)");
Long[] products = {1L, 2L};
Long[] substrates = {1L, 2L};
// These coefficients meet the expectations of the RO we'll look for later.
Integer[] substrateCoefficients = {2, 1};
Integer[] productCoefficients = {1, 2};
JSONObject expectedResult = new JSONObject();
// This RO acts on two identical substrates.
expectedResult.put("165", "3");
Reaction testReaction =
utilsObject.makeTestReaction(substrates, products, substrateCoefficients, productCoefficients, true);
testReactions.add(testReaction);
MockedNoSQLAPI mockAPI = new MockedNoSQLAPI();
mockAPI.installMocks(testReactions, utilsObject.SEQUENCES, utilsObject.ORGANISM_NAMES, idToInchi);
NoSQLAPI mockNoSQLAPI = mockAPI.getMockNoSQLAPI();
MechanisticValidator mechanisticValidator = new MechanisticValidator(mockNoSQLAPI);
mechanisticValidator.init();
mechanisticValidator.run();
assertEquals("One reaction should be written to the DB", 1, mockAPI.getWrittenReactions().size());
assertEquals("The mechanistic validator result should be equal to the expected result",
expectedResult.toString(), mockAPI.getWrittenReactions().get(0).getMechanisticValidatorResult().toString());
}
@Test
public void testMechanisticValidatorMatchesNonPrimaryReactorPrediction() throws Exception {
// Arrange
List<Reaction> testReactions = new ArrayList<>();
Map<Long, String> idToInchi = new HashMap<>();
Long substrateId = 1L;
Long productId = 2L;
String substrateInchi =
"InChI=1S/C15H10O7/c16-7-4-10(19)12-11(5-7)22-15(14(21)13(12)20)6-1-2-8(17)9(18)3-6/h1-5,16-19,21H";
String productInchi =
"InChI=1S/C16H12O7/c1-22-11-3-2-7(4-9(11)18)16-15(21)14(20)13-10(19)5-8(17)6-12(13)23-16/h2-6,17-19,21H,1H3";
idToInchi.put(substrateId, substrateInchi);
idToInchi.put(productId, productInchi);
Long[] substrates = {substrateId};
Long[] products = {productId};
Integer[] substrateCoefficients = {1};
Integer[] productCoefficients = {1};
String expectedRo = "358";
String expectedScore = "4";
Reaction testReaction =
utilsObject.makeTestReaction(substrates, products, substrateCoefficients, productCoefficients, true);
testReactions.add(testReaction);
MockedNoSQLAPI mockAPI = new MockedNoSQLAPI();
mockAPI.installMocks(testReactions, utilsObject.SEQUENCES, utilsObject.ORGANISM_NAMES, idToInchi);
NoSQLAPI mockNoSQLAPI = mockAPI.getMockNoSQLAPI();
// Act
MechanisticValidator mechanisticValidator = new MechanisticValidator(mockNoSQLAPI);
mechanisticValidator.init();
mechanisticValidator.run();
// Assert
assertEquals("One reaction should be written to the DB", 1, mockAPI.getWrittenReactions().size());
assertTrue("The mechanistic validator result should contain the expected RO as a key.",
mockAPI.getWrittenReactions().get(0).getMechanisticValidatorResult().has(expectedRo));
assertEquals("The mechanistic validator result should contain the correct score for that RO.",
expectedScore, mockAPI.getWrittenReactions().get(0).getMechanisticValidatorResult().get(expectedRo));
}
}