package com.hida.service; import com.hida.repositories.DefaultSettingRepository; import com.hida.repositories.PidRepository; import com.hida.repositories.UsedSettingRepository; import com.hida.model.DefaultSetting; import com.hida.model.NotEnoughPermutationsException; import com.hida.model.Pid; import com.hida.model.Token; import com.hida.model.UsedSetting; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.Properties; import java.util.Set; import org.mockito.Mock; import org.mockito.InjectMocks; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.MockitoAnnotations; import org.testng.Assert; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** * This class tests the functionality of MinterService using Mockito. * * @author lruffin */ public class MinterServiceTest { /** * Default setting values stored in resources folder */ private final String TEST_FILE = "testDefaultSetting.properties"; @Mock private DefaultSettingRepository defaultSettingRepo_; @Mock private PidRepository pidRepo_; @Mock private UsedSettingRepository usedSettingRepo_; @InjectMocks private MinterService minterService_; /** * Sets up Mockito * * @throws Exception */ @BeforeClass public void setUpClass() throws Exception { MockitoAnnotations.initMocks(this); minterService_.setDefaultSettingPath(TEST_FILE); minterService_.initializeStoredSetting(); } /** * Test the various mint settings (auto/random and random/sequential) * * @return An array of values */ @DataProvider(name = "mintSettings") public Object[][] mintSettings() { return new Object[][]{ {true, true}, {true, false}, {false, true}, {false, false} }; } /** * Tests the MinterService by assuming that the settings aren't currently * stored in the database * * @param isRandom Determines if the PIDs are created randomly or * sequentially * @param isAuto Determines which generator, either Auto or Custom, will be * used */ @Test(dataProvider = "mintSettings") public void testMintWithNewUsedSetting(boolean isRandom, boolean isAuto) throws Exception { // retrieve a sample DefaultSetting entity DefaultSetting testSetting = this.sampleDefaultSetting(); testSetting.setAuto(isAuto); testSetting.setRandom(isRandom); // assume that any Pids created aren't already persisted and pretend to persist them when(pidRepo_.findOne(any(String.class))).thenReturn(null); when(pidRepo_.save(any(Pid.class))).thenReturn(null); // assume the UsedSetting isn't persisted and pretend to persist it when(usedSettingRepo_.findUsedSetting(any(String.class), any(Token.class), any(String.class), anyInt(), anyBoolean())).thenReturn(null); when(usedSettingRepo_.save(any(UsedSetting.class))).thenReturn(null); // retrieve a sample DefaultSetting entity int actualAmount = 5; Set<Pid> testSet = minterService_.mint(actualAmount, testSetting); // test behavior Assert.assertEquals(actualAmount, testSet.size()); verify(pidRepo_, atLeast(actualAmount)).findOne(any(String.class)); verify(pidRepo_, atLeast(actualAmount)).save(any(Pid.class)); verify(usedSettingRepo_, atLeastOnce()).save(any(UsedSetting.class)); verify(usedSettingRepo_, atLeastOnce()).findUsedSetting(any(String.class), any(Token.class), any(String.class), anyInt(), anyBoolean()); } /** * Tests the MinterService under the scenario where UsedSetting entity with * matching parameters already exist. * * @param isRandom Determines if the PIDs are created randomly or * sequentially * @param isAuto Determines which generator, either Auto or Custom, will be * used */ @Test(dataProvider = "mintSettings") public void testMintWithOldUsedSetting(boolean isAuto, boolean isRandom) throws Exception { // retrieve a sample DefaultSetting entity DefaultSetting testSetting = this.sampleDefaultSetting(); testSetting.setAuto(isAuto); testSetting.setRandom(isRandom); // get a sample UsedSetting entity UsedSetting usedSetting = getSampleUsedSetting(); // assume that any Pids created aren't already persisted and pretend to persist them when(pidRepo_.findOne(any(String.class))).thenReturn(null); when(pidRepo_.save(any(Pid.class))).thenReturn(null); // assume the UsedSetting isn't persisted and pretend to persist it when(usedSettingRepo_.findUsedSetting(any(String.class), any(Token.class), any(String.class), anyInt(), anyBoolean())).thenReturn(usedSetting); when(usedSettingRepo_.save(usedSetting)).thenReturn(null); int preTestAmount = (int) usedSetting.getAmount(); int actualAmount = 5; int postTestAmount = actualAmount + preTestAmount; Set<Pid> testSet = minterService_.mint(actualAmount, testSetting); // test behavior Assert.assertEquals(actualAmount, testSet.size()); Assert.assertEquals(postTestAmount, usedSetting.getAmount()); verify(pidRepo_, atLeast(actualAmount)).findOne(any(String.class)); verify(pidRepo_, atLeast(actualAmount)).save(any(Pid.class)); verify(usedSettingRepo_, never()).save(usedSetting); verify(usedSettingRepo_, atLeastOnce()).findUsedSetting(any(String.class), any(Token.class), any(String.class), anyInt(), anyBoolean()); } /** * Tests to ensure that the whenever the stored default setting is used then * the requested amount is saved and used as a starting value. */ @Test public void testMintWithStartingValue() throws Exception { // retrieve a sample DefaultSetting entity DefaultSetting testSetting = this.sampleDefaultSetting(); // get a sample UsedSetting entity UsedSetting usedSetting = getSampleUsedSetting(); // assume that any Pids created aren't already persisted and pretend to persist them when(pidRepo_.findOne(any(String.class))).thenReturn(null); when(pidRepo_.save(any(Pid.class))).thenReturn(null); // assume the UsedSetting isn't persisted and pretend to persist it when(usedSettingRepo_.findUsedSetting(any(String.class), any(Token.class), any(String.class), anyInt(), anyBoolean())).thenReturn(usedSetting); when(usedSettingRepo_.save(any(UsedSetting.class))).thenReturn(null); // check to see if all the Pids were created long amount = 5; long lastAmount = minterService_.getLastSequentialAmount(); Set<Pid> testSet = minterService_.mint(amount, testSetting); Assert.assertEquals(minterService_.getLastSequentialAmount(), (lastAmount + amount) % 10); } /** * Tests the MinterService to ensure that a NotEnoughPermutationsException * is thrown whenever the amount retrieved from FindUsedSetting is less than * the requested amount. * * @param isRandom Determines if the PIDs are created randomly or * sequentially * @param isAuto Determines which generator, either Auto or Custom, will be * used */ @Test(expectedExceptions = NotEnoughPermutationsException.class, dataProvider = "mintSettings") public void testMintNotEnoughPermutationsExceptionInFindUsedSetting( boolean isAuto, boolean isRandom) throws Exception { // retrieve a sample DefaultSetting entity DefaultSetting testSetting = this.sampleDefaultSetting(); testSetting.setAuto(isAuto); testSetting.setRandom(isRandom); // get a sample UsedSetting entity UsedSetting usedSetting = getSampleUsedSetting(); // assume that any Pids created aren't already persisted and pretend to persist them when(pidRepo_.findOne(any(String.class))).thenReturn(null); when(pidRepo_.save(any(Pid.class))).thenReturn(null); // pretend to find and retrieve variable usedSetting when(usedSettingRepo_.findUsedSetting(any(String.class), any(Token.class), any(String.class), anyInt(), anyBoolean())).thenReturn(usedSetting); when(usedSettingRepo_.findOne(anyInt())).thenReturn(usedSetting); // try to mint an amount greater than what is available Set<Pid> testSet = minterService_.mint(6, testSetting); } /** * Tests the MinterService to ensure that a NotEnoughPermutationsException * is thrown whenever the requested amount of Pids to mint exceeds the * possible number of permutations. * * @param isRandom Determines if the PIDs are created randomly or * sequentially * @param isAuto Determines which generator, either Auto or Custom, will be * used */ @Test(expectedExceptions = NotEnoughPermutationsException.class, dataProvider = "mintSettings") public void testMintNotEnoughPermutationsExceptionInCalculatePermutations( boolean isAuto, boolean isRandom) throws Exception { // retrieve a sample DefaultSetting entity DefaultSetting testSetting = this.sampleDefaultSetting(); testSetting.setAuto(isAuto); testSetting.setRandom(isRandom); // assume that any Pids created aren't already persisted and pretend to persist them when(pidRepo_.findOne(any(String.class))).thenReturn(null); when(pidRepo_.save(any(Pid.class))).thenReturn(null); // assume that UsedSetting entity with the relevant parameters does not exist when(usedSettingRepo_.findUsedSetting(any(String.class), any(Token.class), any(String.class), anyInt(), anyBoolean())).thenReturn(null); when(pidRepo_.save(any(Pid.class))).thenReturn(null); // try to mint an amount greater than what is possible Set<Pid> testSet = minterService_.mint(11, testSetting); } /** * Tests the MinterService to ensure that a NotEnoughPermutationsException * is thrown whenever it is no longer possible to 'roll' Pids. This is * important because there may be different settings that may have created * Pids that could match the fields of the currently used setting. * * @param isRandom Determines if the PIDs are created randomly or * sequentially * @param isAuto Determines which generator, either Auto or Custom, will be * used */ @Test(expectedExceptions = NotEnoughPermutationsException.class, dataProvider = "mintSettings") public void testMintNotEnoughPermutationExceptionInRollId(boolean isAuto, boolean isRandom) throws Exception { // retrieve a sample DefaultSetting entity DefaultSetting testSetting = this.sampleDefaultSetting(); testSetting.setAuto(isAuto); testSetting.setRandom(isRandom); // pretend any Pid with the name "0" is the only Pid that exists Pid sentinelPid = new Pid("1"); when(pidRepo_.findOne(any(String.class))).thenReturn(sentinelPid); when(pidRepo_.findOne("0")).thenReturn(null); // assume that UsedSetting entity with the relevant parameters does not exist when(usedSettingRepo_.findUsedSetting(any(String.class), any(Token.class), any(String.class), anyInt(), anyBoolean())).thenReturn(null); when(usedSettingRepo_.save(any(UsedSetting.class))).thenReturn(null); // try to mint an amount greater than what is possible Set<Pid> testSet = minterService_.mint(10, testSetting); } /** * Test in MinterService that ensures that the CurrentSetting is sought * after. */ @Test public void testInitializeStoredSetting() throws Exception { DefaultSetting testSetting = sampleDefaultSetting(); when(defaultSettingRepo_.findCurrentDefaultSetting()).thenReturn(testSetting); minterService_.initializeStoredSetting(); verify(defaultSettingRepo_, atLeastOnce()).findCurrentDefaultSetting(); } /** * Test in MinterService that ensures that the CurrentSetting is sought * after and if it does not exist, a new DefaultSetting is created and * saved. */ @Test public void testGetCurrentSettingWithoutExistingDefaultSetting() throws Exception { DefaultSetting testSetting = sampleDefaultSetting(); when(defaultSettingRepo_.findCurrentDefaultSetting()).thenReturn(null); DefaultSetting actualSetting = minterService_.getStoredSetting(); Assert.assertEquals(actualSetting.getCharMap(), testSetting.getCharMap()); Assert.assertEquals(actualSetting.getPrefix(), testSetting.getPrefix()); Assert.assertEquals(actualSetting.getPrepend(), testSetting.getPrepend()); Assert.assertEquals(actualSetting.getRootLength(), testSetting.getRootLength()); Assert.assertEquals(actualSetting.getTokenType(), testSetting.getTokenType()); Assert.assertEquals(actualSetting.isAuto(), testSetting.isAuto()); Assert.assertEquals(actualSetting.isRandom(), testSetting.isRandom()); Assert.assertEquals(actualSetting.isSansVowels(), testSetting.isSansVowels()); } /** * Test in MinterService that checks if CurrentSetting in MinterService is * being properly updated. */ @Test public void testUpdateCurrentSetting() throws Exception { DefaultSetting testSetting = sampleDefaultSetting(); when(defaultSettingRepo_.findCurrentDefaultSetting()).thenReturn(testSetting); minterService_.updateCurrentSetting(testSetting); verify(defaultSettingRepo_, atLeastOnce()).findCurrentDefaultSetting(); } /** * Create a test Default Setting object */ private DefaultSetting sampleDefaultSetting() { return new DefaultSetting("", // prepend "", // prefix 500, // cacheSize Token.DIGIT, // token type "d", // charmap 1, // rootlength true, // sans vowel true, // is auto false); // is random } /** * Return a sample UsedSetting * * @return */ private UsedSetting getSampleUsedSetting() { return new UsedSetting("", // prefix Token.DIGIT, // tokentype "d", // charmap 1, // rootlength true, //sans vowels 5); // amount } /** * Set all the keys' values in testDefaultSettings to default values to * ensure that the values are being changed during the updatedChangedSetting * tests. * * @throws Exception */ @AfterTest private void resetTestReadDefaultProperties() throws Exception { DefaultSetting setting = readPropertiesFile(TEST_FILE); Properties prop = new Properties(); ClassLoader loader = Thread.currentThread().getContextClassLoader(); URL url = loader.getResource(TEST_FILE); File file = new File(url.toURI()); OutputStream output = new FileOutputStream(file); // set the properties value prop.setProperty("prepend", setting.getPrepend()); prop.setProperty("prefix", setting.getPrefix()); prop.setProperty("cacheSize", setting.getCacheSize() + ""); prop.setProperty("charMap", setting.getCharMap()); prop.setProperty("rootLength", setting.getRootLength() + ""); prop.setProperty("tokenType", setting.getTokenType() + ""); prop.setProperty("sansVowel", setting.isSansVowels() + ""); prop.setProperty("auto", setting.isAuto() + ""); prop.setProperty("random", setting.isRandom() + ""); // save and close prop.store(output, ""); output.close(); } /** * Read a given properties file and return its values in the form of a * DefaultSetting object * * @return DefaultSetting object with read values * @throws IOException Thrown when the file cannot be found */ private DefaultSetting readPropertiesFile(String filename) throws IOException { Properties prop = new Properties(); ClassLoader loader = Thread.currentThread().getContextClassLoader(); InputStream input = loader.getResourceAsStream(filename); DefaultSetting setting = new DefaultSetting(); // load a properties file prop.load(input); // get the property value, store it, and return it setting.setPrepend(prop.getProperty("prepend")); setting.setPrefix(prop.getProperty("prefix")); setting.setCacheSize(Long.parseLong(prop.getProperty("cacheSize"))); setting.setCharMap(prop.getProperty("charMap")); setting.setTokenType(Token.valueOf(prop.getProperty("tokenType"))); setting.setRootLength(Integer.parseInt(prop.getProperty("rootLength"))); setting.setSansVowels(Boolean.parseBoolean(prop.getProperty("sansVowel"))); setting.setAuto(Boolean.parseBoolean(prop.getProperty("auto"))); setting.setRandom(Boolean.parseBoolean(prop.getProperty("random"))); // close and return input.close(); return setting; } }