/* * Copyright 2017, Yahoo Holdings Inc. * Copyrights licensed under the GPL License. * See the accompanying LICENSE file for terms. */ package com.yahoo.sherlock; import com.beust.jcommander.internal.Lists; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.yahoo.egads.data.Anomaly; import com.yahoo.sherlock.enums.Granularity; import com.yahoo.sherlock.enums.JobStatus; import com.yahoo.sherlock.exception.ClusterNotFoundException; import com.yahoo.sherlock.exception.EmailNotFoundException; import com.yahoo.sherlock.exception.JobNotFoundException; import com.yahoo.sherlock.exception.SchedulerException; import com.yahoo.sherlock.exception.SherlockException; import com.yahoo.sherlock.model.AnomalyReport; import com.yahoo.sherlock.model.DruidCluster; import com.yahoo.sherlock.model.EgadsResult; import com.yahoo.sherlock.model.EmailMetaData; import com.yahoo.sherlock.model.JobMetadata; import com.yahoo.sherlock.model.JobTimeline; import com.yahoo.sherlock.query.EgadsConfig; import com.yahoo.sherlock.query.Query; import com.yahoo.sherlock.service.JobExecutionService; import com.yahoo.sherlock.service.SchedulerService; import com.yahoo.sherlock.service.DetectorService; import com.yahoo.sherlock.service.DruidQueryService; import com.yahoo.sherlock.service.ServiceFactory; import com.yahoo.sherlock.settings.CLISettingsTest; import com.yahoo.sherlock.settings.Constants; import com.yahoo.sherlock.settings.QueryConstants; import com.yahoo.sherlock.store.AnomalyReportAccessor; import com.yahoo.sherlock.store.DBTestHelper; import com.yahoo.sherlock.store.DeletedJobMetadataAccessor; import com.yahoo.sherlock.store.DruidClusterAccessor; import com.yahoo.sherlock.store.EmailMetadataAccessor; import com.yahoo.sherlock.store.JobMetadataAccessor; import com.yahoo.sherlock.store.JsonDumper; import org.testng.Assert; import org.testng.annotations.Test; import spark.HaltException; import spark.ModelAndView; import spark.QueryParamsMap; import spark.Request; import spark.Response; import spark.template.thymeleaf.ThymeleafTemplateEngine; import java.io.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static org.mockito.Matchers.anyDouble; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyList; import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; public class RoutesTest { private Request req; private Response res; private DruidQueryService qs; private DetectorService ds; private DruidClusterAccessor dca; private JobMetadataAccessor jma; private AnomalyReportAccessor ara; private EmailMetadataAccessor ema; private JobExecutionService jes; private ThymeleafTemplateEngine tte; private ServiceFactory sf; private SchedulerService ss; private void mocks() { req = mock(Request.class); res = mock(Response.class); qs = mock(DruidQueryService.class); ds = mock(DetectorService.class); jes = mock(JobExecutionService.class); sf = mock(ServiceFactory.class); ss = mock(SchedulerService.class); when(sf.newJobExecutionService()).thenReturn(jes); when(sf.newDetectorServiceInstance()).thenReturn(ds); when(sf.newDruidQueryServiceInstance()).thenReturn(qs); inject("serviceFactory", sf); tte = mock(ThymeleafTemplateEngine.class); inject("thymeleaf", tte); dca = mock(DruidClusterAccessor.class); inject("clusterAccessor", dca); jma = mock(JobMetadataAccessor.class); inject("jobAccessor", jma); ara = mock(AnomalyReportAccessor.class); inject("reportAccessor", ara); ema = mock(EmailMetadataAccessor.class); inject("emailMetadataAccessor", ema); @SuppressWarnings("unchecked") Map<String, Object> dp = (Map<String, Object>) mock(Map.class); inject("defaultParams", dp); } private static void inject(String name, Object mock) { CLISettingsTest.setField(CLISettingsTest.getField(name, Routes.class), mock); } @SuppressWarnings("unchecked") private static Map<String, Object> params(Object o) { return (Map<String, Object>) o; } private static Map<String, Object> params(ModelAndView mav) { return params(mav.getModel()); } @Test @SuppressWarnings("unchecked") public void testInitParams() { Field defaultParamsField = CLISettingsTest.getField("defaultParams", Routes.class); Object prevValue = CLISettingsTest.fieldVal(defaultParamsField); try { CLISettingsTest.setField(defaultParamsField, null); Routes.initParams(); Object curValue = CLISettingsTest.fieldVal(defaultParamsField); assertNotNull(curValue); Map<String, Object> params = (Map<String, Object>) curValue; assertTrue(params.containsKey(Constants.PROJECT)); assertTrue(params.containsKey(Constants.TITLE)); assertTrue(params.containsKey(Constants.VERSION)); } finally { CLISettingsTest.setField(defaultParamsField, prevValue); } } @Test public void testInitAccessors() { CLISettingsTest.setField(CLISettingsTest.getField("reportAccessor", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("deletedJobAccessor", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("clusterAccessor", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("jobAccessor", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("emailMetadataAccessor", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("jsonDumper", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("serviceFactory", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("schedulerService", Routes.class), null); try { Routes.initServices(); assertNotNull(CLISettingsTest.fieldVal(CLISettingsTest.getField("reportAccessor", Routes.class))); assertNotNull(CLISettingsTest.fieldVal(CLISettingsTest.getField("deletedJobAccessor", Routes.class))); assertNotNull(CLISettingsTest.fieldVal(CLISettingsTest.getField("clusterAccessor", Routes.class))); assertNotNull(CLISettingsTest.fieldVal(CLISettingsTest.getField("emailMetadataAccessor", Routes.class))); assertNotNull(CLISettingsTest.fieldVal(CLISettingsTest.getField("jobAccessor", Routes.class))); assertNotNull(CLISettingsTest.fieldVal(CLISettingsTest.getField("jsonDumper", Routes.class))); assertNotNull(CLISettingsTest.fieldVal(CLISettingsTest.getField("serviceFactory", Routes.class))); assertNotNull(CLISettingsTest.fieldVal(CLISettingsTest.getField("schedulerService", Routes.class))); } finally { CLISettingsTest.setField(CLISettingsTest.getField("reportAccessor", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("deletedJobAccessor", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("clusterAccessor", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("jobAccessor", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("emailMetadataAccessor", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("jsonDumper", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("serviceFactory", Routes.class), null); CLISettingsTest.setField(CLISettingsTest.getField("schedulerService", Routes.class), null); } } @Test public void testViewHomePage() { Routes.initParams(); mocks(); ModelAndView mav = Routes.viewHomePage(req, res); assertEquals(mav.getViewName(), "homePage"); } @Test @SuppressWarnings("unchecked") public void testViewInstantAnomalyFormEmptyDruidList() throws IOException { Routes.initParams(); mocks(); when(dca.getDruidClusterList()).thenThrow(new IOException()); inject("clusterAccessor", dca); ModelAndView mav = Routes.viewInstantAnomalyJobForm(req, res); assertEquals(params(mav.getModel()).get(Constants.INSTANTVIEW), "true"); List<DruidCluster> clusters = (List<DruidCluster>) params(mav.getModel()).get(Constants.DRUID_CLUSTERS); assertEquals(clusters.size(), 0); } @Test @SuppressWarnings("unchecked") public void testViewInstantAnomalyForm() throws IOException { Routes.initParams(); mocks(); DruidCluster cl = new DruidCluster(); cl.setClusterId(5); List<DruidCluster> cls = Collections.singletonList(cl); when(dca.getDruidClusterList()).thenReturn(cls); inject("clusterAccessor", dca); ModelAndView mav = Routes.viewInstantAnomalyJobForm(req, res); List<DruidCluster> clusters = (List<DruidCluster>) params(mav.getModel()).get(Constants.DRUID_CLUSTERS); assertEquals(clusters.size(), 1); assertEquals(clusters.get(0).getClusterId(), (Integer) 5); } @Test @SuppressWarnings("unchecked") public void testViewNewAnomalyForm() throws IOException { Routes.initParams(); mocks(); DruidCluster cl = new DruidCluster(); cl.setClusterId(5); List<DruidCluster> cls = Collections.singletonList(cl); when(dca.getDruidClusterList()).thenReturn(cls); inject("clusterAccessor", dca); ModelAndView mav = Routes.viewNewAnomalyJobForm(req, res); List<DruidCluster> clusters = (List<DruidCluster>) params(mav.getModel()).get(Constants.DRUID_CLUSTERS); assertEquals(clusters.size(), 1); assertEquals(clusters.get(0).getClusterId(), (Integer) 5); } @Test @SuppressWarnings("unchecked") public void testViewNewAnomalyFormEmptyDruidList() throws IOException { Routes.initParams(); mocks(); when(dca.getDruidClusterList()).thenThrow(new IOException()); inject("clusterAccessor", dca); ModelAndView mav = Routes.viewNewAnomalyJobForm(req, res); assertTrue(params(mav.getModel()).containsKey(Constants.ERROR)); } @Test public void testSaveUserJob() throws Exception { mocks(); when(req.body()).thenReturn( "{" + "\"clusterId\":\"1\"," + "\"ownerEmail\":\"[email protected]\"" + "}" ); when(sf.newEmailServiceInstance()).thenCallRealMethod(); DruidQueryService dqs = mock(DruidQueryService.class); Query query = mock(Query.class); JsonObject jo = new JsonObject(); when(query.getQueryJsonObject()).thenReturn(jo); when(dqs.build(anyString(), any(Granularity.class), anyInt(), anyInt(), anyInt())).thenReturn(query); when(sf.newDruidQueryServiceInstance()).thenReturn(dqs); DruidCluster dc = mock(DruidCluster.class); when(dca.getDruidCluster(anyInt())).thenReturn(dc); when(dc.getHoursOfLag()).thenReturn(0); doAnswer(iom -> { Object[] args = iom.getArguments(); ((JobMetadata) args[0]).setJobId(10); assertEquals(((JobMetadata) args[0]).getClusterId(), (Integer) 1); return null; } ).when(jma).putJobMetadata(any(JobMetadata.class)); inject("clusterAccessor", dca); inject("jobAccessor", jma); inject("serviceFactory", sf); assertEquals(Routes.saveUserJob(req, res), "10"); verify(res, times(1)).status(200); } @Test public void testSaveUserJobException() throws Exception { mocks(); when(req.body()).thenReturn( "{" + "\"clusterId\":\"1\"," + "\"ownerEmail\":\"[email protected]\"" + "}" ); when(sf.newEmailServiceInstance()).thenCallRealMethod(); DruidQueryService dqs = mock(DruidQueryService.class); when(dqs.build(anyString(), any(Granularity.class), anyInt(), anyInt(), anyInt())).thenThrow(new SherlockException("exception")); when(sf.newDruidQueryServiceInstance()).thenReturn(dqs); inject("serviceFactory", sf); assertEquals(Routes.saveUserJob(req, res), "exception"); verify(res, times(1)).status(500); } @Test public void testDeleteJob() throws SchedulerException { mocks(); when(req.params(Constants.ID)).thenReturn("1"); inject("jobAccessor", jma); inject("schedulerService", ss); assertEquals(Routes.deleteJob(req, res), Constants.SUCCESS); verify(ss, times(1)).stopJob(1); } @Test public void testDeleteJobException() throws IOException, JobNotFoundException { mocks(); when(req.params(anyString())).thenReturn("1"); inject("schedulerService", ss); doThrow(new IOException("fake")).when(jma).deleteJobMetadata(anyInt()); inject("jobAccessor", jma); assertEquals(Routes.deleteJob(req, res), "fake"); verify(res, times(1)).status(500); when(req.params(anyString())).thenReturn("%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%31%29%3c%2f%73%63%72%69%70%74%3e"); assertEquals(Routes.deleteJob(req, res), "Invalid Job!"); } @Test @SuppressWarnings("unchecked") public void testViewJobsList() throws IOException { Routes.initParams(); mocks(); List<JobMetadata> jobsList = (List<JobMetadata>) mock(List.class); when(jobsList.size()).thenReturn(1); when(jma.getJobMetadataList()).thenReturn(jobsList); inject("jobAccessor", jma); JobTimeline jobTimeline = mock(JobTimeline.class); inject("jobTimeline", jobTimeline); when(jobTimeline.getCurrentTimeline(any())).thenReturn(null); ModelAndView mav = Routes.viewJobsList(req, res); assertTrue(params(mav).containsKey(Constants.TITLE)); assertEquals(((List) params(mav).get("jobs")).size(), 1); assertEquals(mav.getViewName(), "listJobs"); } @Test public void testViewJobsListException() throws IOException { Routes.initParams(); mocks(); when(jma.getJobMetadataList()).thenThrow(new IOException("exception")); inject("jobAccessor", jma); ModelAndView mav = Routes.viewJobsList(req, res); assertTrue(params(mav).containsKey(Constants.ERROR)); assertEquals(params(mav).get(Constants.ERROR), "exception"); assertEquals(mav.getViewName(), "listJobs"); } @Test @SuppressWarnings("unchecked") public void testViewDeletedJobsList() throws IOException { Routes.initParams(); mocks(); List<JobMetadata> ljm = (List<JobMetadata>) mock(List.class); DeletedJobMetadataAccessor dma = mock(DeletedJobMetadataAccessor.class); inject("deletedJobAccessor", dma); when(dma.getDeletedJobMetadataList()).thenReturn(ljm); ModelAndView mav = Routes.viewDeletedJobsList(req, res); assertTrue(params(mav).containsKey(Constants.TITLE)); assertEquals(params(mav).get(Constants.DELETEDJOBSVIEW), "true"); assertEquals(mav.getViewName(), "listJobs"); assertEquals(params(mav).get("jobs"), ljm); } @Test public void testDeletedJobsListException() throws IOException { Routes.initParams(); mocks(); DeletedJobMetadataAccessor dma = mock(DeletedJobMetadataAccessor.class); inject("deletedJobAccessor", dma); when(dma.getDeletedJobMetadataList()).thenThrow(new IOException("exception")); ModelAndView mav = Routes.viewDeletedJobsList(req, res); assertTrue(params(mav).containsKey(Constants.ERROR)); } @Test public void testViewJobInfo() throws IOException, JobNotFoundException, ClusterNotFoundException { Routes.initParams(); mocks(); inject("jobAccessor", jma); inject("clusterAccessor", dca); when(req.params(Constants.ID)).thenReturn("1"); JobMetadata jm = mock(JobMetadata.class); DruidCluster dc = mock(DruidCluster.class); when(jma.getJobMetadata("1")).thenReturn(jm); when(jm.getClusterId()).thenReturn(2); when(dca.getDruidCluster(anyInt())).thenReturn(dc); when(dc.getClusterName()).thenReturn("druid"); ModelAndView mav = Routes.viewJobInfo(req, res); assertTrue(params(mav).containsKey(Constants.TITLE)); assertEquals(params(mav).get("job"), jm); } @Test(expectedExceptions = HaltException.class) public void testViewJobInfoException() throws IOException, JobNotFoundException { Routes.initParams(); mocks(); inject("jobAccessor", jma); when(jma.getJobMetadata(anyString())).thenThrow(new IOException("fake")); inject("thymeleaf", tte); when(tte.render(anyObject())).thenReturn("404"); Routes.viewJobInfo(req, res); } @Test public void testViewDeletedJobInfo() throws IOException, JobNotFoundException, ClusterNotFoundException { Routes.initParams(); mocks(); when(dca.getDruidCluster(anyInt())).thenReturn(new DruidCluster()); inject("clusterAccessor", dca); JobMetadata jm = mock(JobMetadata.class); when(req.params(Constants.ID)).thenReturn("1"); DeletedJobMetadataAccessor dma = mock(DeletedJobMetadataAccessor.class); when(dma.getDeletedJobMetadata("1")).thenReturn(jm); inject("deletedJobAccessor", dma); ModelAndView mav = Routes.viewDeletedJobInfo(req, res); assertEquals(params(mav).get(Constants.DELETEDJOBSVIEW), "true"); assertEquals(params(mav).get("job"), jm); } @Test(expectedExceptions = HaltException.class) public void testViewDeletedJobInfoException() throws IOException, JobNotFoundException { Routes.initParams(); mocks(); DeletedJobMetadataAccessor dma = mock(DeletedJobMetadataAccessor.class); when(dma.getDeletedJobMetadata(anyString())).thenThrow(new IOException("fake")); inject("deletedJobAccessor", dma); inject("thymeleaf", tte); when(tte.render(anyObject())).thenReturn("404"); Routes.viewDeletedJobInfo(req, res); } @Test public void testUpdateJobInfo() throws Exception { String body = "{\"granularity\":\"day\",\"frequency\":\"day\",\"sigmaThreshold\":\"3\",\"ownerEmail\":\"[email protected]\",\"query\":\"{}\"}"; mocks(); when(req.body()).thenReturn(body); when(req.params(Constants.ID)).thenReturn("1"); JobMetadata jm = new JobMetadata(); jm.setJobId(1); jm.setGranularity("day"); jm.setFrequency("day"); jm.setSigmaThreshold(3.0); jm.setJobStatus("RUNNING"); jm.setUserQuery("query"); jm.setQuery("query"); when(jma.getJobMetadata("1")).thenReturn(jm); DruidQueryService dqs = mock(DruidQueryService.class); Query q = mock(Query.class); JsonObject j = new JsonObject(); when(sf.newEmailServiceInstance()).thenCallRealMethod(); when(q.getQueryJsonObject()).thenReturn(j); when(dqs.build(anyString(), any(Granularity.class), anyInt(), anyInt(), anyInt())).thenReturn(q); when(sf.newDruidQueryServiceInstance()).thenReturn(dqs); inject("serviceFactory", sf); inject("jobAccessor", jma); inject("schedulerService", ss); assertEquals(Routes.updateJobInfo(req, res), Constants.SUCCESS); verify(res, times(1)).status(200); verify(jma, times(1)).putJobMetadata(jm); } @Test public void testUpdateJobInfoException() throws IOException, JobNotFoundException { mocks(); when(req.params(anyString())).thenReturn("1"); when(jma.getJobMetadata(anyString())).thenThrow(new IOException("exception")); when(req.body()).thenReturn("{\"ownerEmail\":\"[email protected]\"}"); inject("jobAccessor", jma); inject("serviceFactory", sf); assertEquals(Routes.updateJobInfo(req, res), "exception"); verify(res, times(1)).status(500); when(req.params(anyString())).thenReturn("1%24%"); assertEquals(Routes.updateJobInfo(req, res), "Invalid Job!"); } @Test public void testLaunchJob() throws IOException, JobNotFoundException, SchedulerException, ClusterNotFoundException { mocks(); JobMetadata jm = new JobMetadata(); inject("jobAccessor", jma); inject("schedulerService", ss); DruidCluster cluster = new DruidCluster(); cluster.setHoursOfLag(10); jm.setClusterId(123); jm.setJobId(12); when(dca.getDruidCluster(123)).thenReturn(cluster); when(req.params(Constants.ID)).thenReturn("1"); when(jma.getJobMetadata("1")).thenReturn(jm); assertEquals(Routes.launchJob(req, res), Constants.SUCCESS); verify(res, times(1)).status(200); verify(jma, times(1)).putJobMetadata(jm); verify(ss, times(1)).scheduleJob(jm); assertEquals(jm.getJobStatus(), "RUNNING"); } @Test public void testLaunchJobException() throws IOException, JobNotFoundException { mocks(); when(req.params(anyString())).thenReturn("1"); inject("jobAccessor", jma); when(jma.getJobMetadata(anyString())).thenThrow(new IOException("exception")); assertEquals(Routes.launchJob(req, res), "exception"); verify(res, times(1)).status(500); when(req.params(anyString())).thenReturn("%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%31%29%3c%2f%73%63%72%69%70%74%3e"); assertEquals(Routes.deleteJob(req, res), "Invalid Job!"); } @Test public void testStopJob() throws IOException, JobNotFoundException, SchedulerException { mocks(); JobMetadata jm = new JobMetadata(); when(req.params(Constants.ID)).thenReturn("1"); inject("jobAccessor", jma); jm.setJobId(1); jm.setFrequency("day"); inject("schedulerService", ss); when(jma.getJobMetadata("1")).thenReturn(jm); assertEquals(Routes.stopJob(req, res), Constants.SUCCESS); verify(res, times(1)).status(200); verify(jma, times(1)).putJobMetadata(jm); verify(ss, times(1)).stopJob(1); } @Test public void testStopJobException() throws IOException, JobNotFoundException { mocks(); when(req.params(Constants.ID)).thenReturn("1"); when(jma.getJobMetadata(anyString())).thenThrow(new IOException("exception")); inject("jobAccessor", jma); assertEquals(Routes.stopJob(req, res), "exception"); verify(res, times(1)).status(500); } @Test public void testViewJobReport() throws Exception { Routes.initParams(); mocks(); when(req.params(Constants.ID)).thenReturn("1"); when(req.params(Constants.FREQUENCY_PARAM)).thenReturn("day"); JobMetadata jm = new JobMetadata(); List<AnomalyReport> lar = Collections.emptyList(); when(ara.getAnomalyReportsForJob(anyString(), anyString())).thenReturn(lar); inject("reportAccessor", ara); inject("jobAccessor", jma); when(jma.getJobMetadata(anyString())).thenReturn(jm); ModelAndView mav = Routes.viewJobReport(req, res); assertEquals(mav.getViewName(), "report"); assertEquals(params(mav).get(Constants.FREQUENCY), "day"); assertTrue(params(mav).containsKey(Constants.TIMELINE_POINTS)); } @Test public void testSendJobReport() throws IOException { Routes.initParams(); mocks(); when(tte.render(any(ModelAndView.class))).thenReturn("table"); AnomalyReport a = mock(AnomalyReport.class); List<AnomalyReport> lar = Collections.singletonList(a); when(ara.getAnomalyReportsForJobAtTime(anyString(), anyString(), anyString())).thenReturn(lar); when(req.body()).thenReturn("{\"" + Constants.SELECTED_DATE + "\":\"5678\", \"frequency\":\"day\"}"); when(req.params(anyString())).thenReturn("4"); inject("thymeleaf", tte); inject("reportAccessor", ara); assertEquals(Routes.sendJobReport(req, res), "table"); verify(res, times(1)).status(200); when(req.body()).thenReturn("{\"" + Constants.SELECTED_DATE + "\":\"-5678\", \"frequency\":\"dayss\"}"); assertEquals(Routes.sendJobReport(req, res), "Invalid Request"); } @Test public void testSendJobReportException() throws IOException { Routes.initParams(); mocks(); when(ara.getAnomalyReportsForJobAtTime(anyString(), anyString(), anyString())).thenThrow(new IOException("exception")); inject("reportAccessor", ara); when(req.body()).thenReturn("{\"" + Constants.SELECTED_DATE + "\":\"5678\", \"frequency\":\"day\"}"); when(req.params(anyString())).thenReturn("2"); assertTrue(Routes.sendJobReport(req, res).contains("exception")); verify(res, times(1)).status(500); } @Test public void testViewNewDruidClusterForm() { Routes.initParams(); ModelAndView mav = Routes.viewNewDruidClusterForm(req, res); assertEquals(mav.getViewName(), "druidForm"); } @Test public void testAddNewDruidCluster() throws IOException { mocks(); String body = "{" + "\"clusterName\":\"name\"," + "\"brokerHost\":\"localhost\"," + "\"brokerPort\":\"5080\"," + "\"brokerEndpoint\":\"druid/v2\"" + "}"; when(req.body()).thenReturn(body); inject("clusterAccessor", dca); doAnswer(iom -> { ((DruidCluster) iom.getArguments()[0]).setClusterId(1); return null; } ).when(dca).putDruidCluster(any(DruidCluster.class)); assertEquals(Routes.addNewDruidCluster(req, res), "1"); verify(res, times(1)).status(200); } @Test public void testAddNewClusterInvalidCluster() { mocks(); String body = "{}"; when(req.body()).thenReturn(body); assertEquals(Routes.addNewDruidCluster(req, res), "Cluster name cannot be empty"); verify(res, times(1)).status(500); } @Test public void testAddNewClusterException() throws IOException { mocks(); String body = "{" + "\"clusterName\":\"name\"," + "\"brokerHost\":\"localhost\"," + "\"brokerPort\":\"5080\"," + "\"brokerEndpoint\":\"druid/v2\"" + "}"; when(req.body()).thenReturn(body); inject("clusterAccessor", dca); doThrow(new IOException("exception")).when(dca).putDruidCluster(any(DruidCluster.class)); assertEquals(Routes.addNewDruidCluster(req, res), "exception"); verify(res, times(1)).status(500); } @Test public void testViewDruidClusterList() throws IOException { mocks(); Routes.initParams(); inject("clusterAccessor", dca); when(dca.getDruidClusterList()).thenReturn(Collections.emptyList()); ModelAndView mav = Routes.viewDruidClusterList(req, res); assertEquals(mav.getViewName(), "druidList"); assertEquals(((List) params(mav).get("clusters")).size(), 0); } @Test public void testViewDruidClusterListException() throws IOException { mocks(); Routes.initParams(); inject("clusterAccessor", dca); when(dca.getDruidClusterList()).thenThrow(new IOException("exception")); ModelAndView mav = Routes.viewDruidClusterList(req, res); assertEquals(params(mav).get(Constants.ERROR), "exception"); } @Test public void testViewDruidCluster() throws IOException, ClusterNotFoundException { mocks(); when(req.params(Constants.ID)).thenReturn("1"); DruidCluster dc = new DruidCluster(); dc.setClusterId(1); when(dca.getDruidCluster("1")).thenReturn(dc); inject("clusterAccessor", dca); ModelAndView mav = Routes.viewDruidCluster(req, res); assertEquals(mav.getViewName(), "druidInfo"); assertEquals(((DruidCluster) params(mav).get("cluster")).getClusterId(), (Integer) 1); } @Test(expectedExceptions = HaltException.class) public void testViewDruidClusterException() throws IOException, ClusterNotFoundException { mocks(); when(dca.getDruidCluster(anyString())).thenThrow(new IOException("exception")); inject("clusterAccessor", dca); inject("thymeleaf", tte); when(tte.render(anyObject())).thenReturn("404"); Routes.viewDruidCluster(req, res); } @Test public void testDeleteDruidCluster() throws IOException, ClusterNotFoundException { mocks(); when(req.params(Constants.ID)).thenReturn("1"); when(jma.getJobsAssociatedWithCluster(anyString())).thenReturn(Collections.emptyList()); inject("jobAccessor", jma); inject("clusterAccessor", dca); assertEquals(Routes.deleteDruidCluster(req, res), Constants.SUCCESS); verify(res, times(1)).status(200); verify(dca, times(1)).deleteDruidCluster("1"); } @Test public void testDeleteDruidClusterAssociatedJobs() throws IOException { mocks(); inject("jobAccessor", jma); JobMetadata jm = new JobMetadata(); jm.setJobId(2); List<JobMetadata> jlist = Collections.singletonList(jm); when(jma.getJobsAssociatedWithCluster(anyString())).thenReturn(jlist); when(req.params(anyString())).thenReturn("1"); assertEquals( Routes.deleteDruidCluster(req, res), "Cannot delete cluster with 1 associated jobs" ); verify(res, times(1)).status(400); when(req.params(anyString())).thenReturn("1cv%32c%32"); assertEquals( Routes.deleteDruidCluster(req, res), "Invalid Cluster!" ); } @Test public void testDeleteDruidClusterException() throws IOException, ClusterNotFoundException { mocks(); when(jma.getJobsAssociatedWithCluster(anyString())).thenReturn(Collections.emptyList()); when(req.params(anyString())).thenReturn("100"); doThrow(new IOException("exception")).when(dca).deleteDruidCluster(anyString()); inject("jobAccessor", jma); inject("clusterAccessor", dca); assertEquals(Routes.deleteDruidCluster(req, res), "exception"); verify(res, times(1)).status(500); } @Test public void testUpdateDruidCluster() throws IOException, ClusterNotFoundException, SchedulerException { mocks(); when(req.params(Constants.ID)).thenReturn("1"); JsonObject dcjson = new JsonObject(); dcjson.addProperty("clusterName", "name"); dcjson.addProperty("clusterDescription", "descr"); dcjson.addProperty("brokerHost", "hostname"); dcjson.addProperty("brokerPort", "431"); dcjson.addProperty("brokerEndpoint", "druid/v2"); String body = new Gson().toJson(dcjson); when(req.body()).thenReturn(body); when(jma.getRunningJobsAssociatedWithCluster(anyString())).thenReturn(new ArrayList<>()); inject("schedulerService", ss); doNothing().when(ss).stopAndReschedule(anyList()); doNothing().when(jma).putJobMetadata(anyList()); DruidCluster dc = new DruidCluster(); dc.setClusterId(1); dc.setBrokerHost("localhost"); dc.setHoursOfLag(0); dc.setBrokerEndpoint("druid/v2"); dc.setBrokerPort(1234); dc.setClusterName("name"); when(dca.getDruidCluster(anyString())).thenReturn(dc); inject("clusterAccessor", dca); assertEquals(Routes.updateDruidCluster(req, res), Constants.SUCCESS); assertEquals(dc.getBrokerHost(), "hostname"); assertEquals(dc.getBrokerPort(), (Integer) 431); verify(res, times(1)).status(200); verify(dca, times(1)).putDruidCluster(dc); } @Test public void testUpdateDruidClusterException() throws IOException, ClusterNotFoundException { mocks(); inject("clusterAccessor", dca); when(req.params(Constants.ID)).thenReturn("1"); when(dca.getDruidCluster(anyString())).thenThrow(new IOException("exception")); assertEquals(Routes.updateDruidCluster(req, res), "exception"); verify(res, times(1)).status(500); when(req.params(Constants.ID)).thenReturn("!%41"); assertEquals(Routes.updateDruidCluster(req, res), "Invalid Cluster!"); } @Test public void testGetDatabaseJsonDump() throws IOException { JsonObject o = new JsonObject(); o.addProperty("key", "val"); JsonDumper jd = mock(JsonDumper.class); when(jd.getRawData()).thenReturn(o); inject("jsonDumper", jd); assertEquals(Routes.getDatabaseJsonDump(req, res), "{\"key\":\"val\"}"); when(jd.getRawData()).thenThrow(new IOException("exception")); assertEquals(Routes.getDatabaseJsonDump(req, res), "exception"); } @Test public void testWriteDatabaseJsonDump() throws IOException { mocks(); JsonDumper jd = mock(JsonDumper.class); when(req.body()).thenReturn("{\"key\":\"val\"}"); inject("jsonDumper", jd); assertEquals(Routes.writeDatabaseJsonDump(req, res), "OK"); verify(jd, times(1)).writeRawData(any(JsonObject.class)); verify(res, times(1)).status(200); doThrow(new IOException("exception")).when(jd).writeRawData(any(JsonObject.class)); assertEquals(Routes.writeDatabaseJsonDump(req, res), "exception"); verify(res, times(1)).status(500); } @Test public void testProcessInstantAnomalyJob() throws Exception { Routes.initParams(); mocks(); Query query = mock(Query.class); when(query.getQueryJsonObject()).thenReturn(new JsonObject()); when(qs.build(anyString(), any(), anyInt(), anyInt(), anyInt())).thenReturn(query); when(req.body()).thenReturn( "{" + "\"clusterId\":\"1\"," + "\"granularity\":\"hour\"," + "\"sigmaThreshold\":\"3.5\"," + "\"frequency\":\"hour\"," + "\"queryEndTimeText\":\"2017-06-02T12:00\"," + "\"granularityRange\":\"1\"," + "\"timeseriesRange\":\"24\"," + "\"detectionWindow\":\"24\"," + "\"tsModels\":\"OlympicModel\"," + "\"adModels\":\"KSigmaModel\"" + "}" ); DruidCluster dc = mock(DruidCluster.class); when(dca.getDruidCluster(anyString())).thenReturn(dc); when(dc.getHoursOfLag()).thenReturn(0); inject("clusterAccessor", dca); EgadsResult eres = mock(EgadsResult.class); EgadsResult.Series[] series = { new EgadsResult.Series(), new EgadsResult.Series(), new EgadsResult.Series() }; when(eres.getData()).thenReturn(series); when(eres.getAnomalies()).thenReturn(Lists.newArrayList(new Anomaly())); List<EgadsResult> reslist = Lists.newArrayList(eres); when(ds.detectWithResults(any(), anyDouble(), any(), any(), any())) .thenReturn(reslist); when(tte.render(any(ModelAndView.class))).thenReturn("<div></div>"); String a = Routes.processInstantAnomalyJob(req, res); verify(tte, times(1)).render(any(ModelAndView.class)); verify(jes, times(1)).getReports(any(), any()); when(qs.build(any(), any(), anyInt(), anyInt(), anyInt())).thenThrow(new SherlockException("error")); assertEquals(a, Constants.SUCCESS); String error = Routes.processInstantAnomalyJob(req, res); assertEquals(error, Constants.ERROR); } @Test public void testDebugInstantReport() throws Exception { mocks(); DruidCluster c = mock(DruidCluster.class); List<DruidCluster> dclist = Collections.singletonList(c); when(dca.getDruidClusterList()).thenReturn(dclist); ModelAndView mav = Routes.debugInstantReport(req, res); assertEquals(mav.getViewName(), "debugForm"); assertEquals(params(mav).get(Constants.DRUID_CLUSTERS), dclist); } @Test public void testDebugPowerQuery() throws Exception { mocks(); QueryParamsMap qmap = mock(QueryParamsMap.class); Map<String, String[]> smap = new HashMap<>(); when(qmap.toMap()).thenReturn(smap); when(req.queryMap()).thenReturn(qmap); JsonObject jo = new JsonObject(); jo.addProperty(QueryConstants.POSTAGGREGATIONS, "a"); jo.addProperty(QueryConstants.AGGREGATIONS, "a"); JsonObject gran = new JsonObject(); gran.addProperty(QueryConstants.PERIOD, "P1H"); jo.add(QueryConstants.GRANULARITY, gran); jo.addProperty(QueryConstants.INTERVALS, "1"); smap.put("query", new String[]{new Gson().toJson(jo)}); smap.put("ownerEmail", new String[]{"[email protected],[email protected]"}); @SuppressWarnings("unchecked") List<AnomalyReport> arlist = mock(List.class); when(jes.getReports(any(), any(JobMetadata.class))).thenReturn(arlist); when(tte.render(any(ModelAndView.class))).thenReturn("html"); doAnswer(iom -> { JobMetadata j = (JobMetadata) iom.getArguments()[0]; j.setJobId(12); return null; } ).when(jma).putJobMetadata(any(JobMetadata.class)); ModelAndView mav = Routes.debugPowerQuery(req, res); assertEquals(params(mav).get("tableHtml"), "html"); verify(jes, times(1)).getReports(any(), any()); verify(jes, times(1)).executeJob(any(JobMetadata.class), any(DruidCluster.class), any(Query.class)); } @Test public void testDebugPowerQueryException() throws Exception { mocks(); @SuppressWarnings("unchecked") List<AnomalyReport> arlist = mock(List.class); when(jes.getReports(any(), any(JobMetadata.class))).thenReturn(arlist); doAnswer(iom -> { JobMetadata j = (JobMetadata) iom.getArguments()[0]; j.setJobId(12); return null; } ).when(jma).putJobMetadata(any(JobMetadata.class)); QueryParamsMap qmap = mock(QueryParamsMap.class); Map<String, String[]> smap = new HashMap<>(); smap.put("query", new String[]{""}); smap.put("ownerEmail", new String[]{"[email protected],[email protected]"}); when(qmap.toMap()).thenReturn(smap); when(req.queryMap()).thenReturn(qmap); ModelAndView mav = Routes.debugPowerQuery(req, res); assertEquals(params(mav).get(Constants.ERROR), "Empty query string provided"); } @Test public void testDebugBackfillForm() throws Exception { @SuppressWarnings("unchecked") List<JobMetadata> jmlist = new ArrayList<>(); mocks(); when(jma.getJobMetadataList()).thenReturn(jmlist); ModelAndView mav = Routes.debugBackfillForm(req, res); assertEquals(params(mav).get("jobs"), jmlist); } @Test public void testDebugRunBackfillJob() throws Exception { mocks(); when(req.body()).thenReturn("{\"query\":\"{}\",\"granularity\":\"day\",\"jobId\":\"1,2\",\"fillStartTime\":\"2017-10-08T02:00\"}"); JobMetadata jm = mock(JobMetadata.class); when(jma.getJobMetadata(anyString())).thenReturn(jm); when(jm.getHoursOfLag()).thenReturn(0); when(jm.getGranularity()).thenReturn("day"); when(jm.getClusterId()).thenReturn(1); when(jm.getGranularityRange()).thenReturn(1); TestUtilities.inject(jes, JobExecutionService.class, "druidClusterAccessor", dca); String queryString = new String(Files.readAllBytes(Paths.get("src/test/resources/druid_query_2.json"))); when(jm.getUserQuery()).thenReturn(queryString); DruidCluster dc = mock(DruidCluster.class); when(dca.getDruidCluster(anyString())).thenReturn(dc); doCallRealMethod().when(jes).performBackfillJob(any(), any(), any()); assertEquals(Routes.debugRunBackfillJob(req, res), "Success"); verify(dca, times(2)).getDruidCluster(anyInt()); verify(jma, times(2)).getJobMetadata(anyString()); verify(jes, times(2)).performBackfillJob(any(), any(), any(), anyInt(), anyInt(), any(), anyInt()); } @Test public void testDebugRunBackfillJobException() throws Exception { mocks(); when(req.body()).thenReturn("{\"query\":\"{}\",\"granularity\":\"day\",\"jobId\":\"1,2\"}"); when(jma.getJobMetadata(anyString())).thenThrow(new IOException("error")); assertEquals(Routes.debugRunBackfillJob(req, res), "error"); } @Test public void testDebugClearJobReports() throws Exception { mocks(); when(req.params(anyString())).thenReturn("123"); assertEquals(Routes.debugClearJobReports(req, res), "Success"); verify(ara, times(1)).deleteAnomalyReportsForJob("123"); } @Test public void testDebugClearJobReportsException() throws Exception { mocks(); doThrow(new IOException("error")).when(ara).deleteAnomalyReportsForJob(anyString()); assertEquals("error", Routes.debugClearJobReports(req, res)); } @Test public void testDebugClearDebugJobs() throws Exception { mocks(); assertEquals("Success", Routes.debugClearDebugJobs(req, res)); verify(jma, times(1)).deleteDebugJobs(); } @Test public void testDebugClearDebugJobsException() throws Exception { mocks(); doThrow(new IOException("error")).when(jma).deleteDebugJobs(); assertEquals("error", Routes.debugClearDebugJobs(req, res)); } @Test public void testDebugEgadsQueryView() throws IOException { @SuppressWarnings("unchecked") List<DruidCluster> dclist = mock(List.class); mocks(); when(dca.getDruidClusterList()).thenReturn(dclist); ModelAndView mav = Routes.debugShowEgadsConfigurableQuery(req, res); assertEquals(params(mav).get(Constants.DRUID_CLUSTERS), dclist); verify(dca, times(1)).getDruidClusterList(); Assert.assertEquals(params(mav).get("filteringMethods"), EgadsConfig.FilteringMethod.values()); } @Test public void testDebugPerformEgadsQuery() throws Exception { mocks(); JsonObject jo = new JsonObject(); jo.addProperty(QueryConstants.POSTAGGREGATIONS, "a"); jo.addProperty(QueryConstants.AGGREGATIONS, "a"); JsonObject gran = new JsonObject(); gran.addProperty(QueryConstants.PERIOD, "P1H"); jo.add(QueryConstants.GRANULARITY, gran); jo.addProperty(QueryConstants.INTERVALS, "1"); QueryParamsMap map = mock(QueryParamsMap.class); Map<String, String[]> smap = new HashMap<>(); when(map.toMap()).thenReturn(smap); when(req.queryMap()).thenReturn(map); smap.put("query", new String[]{new Gson().toJson(jo)}); smap.put("ownerEmail", new String[]{"[email protected],[email protected]"}); EgadsResult er = mock(EgadsResult.class); EgadsResult.Series series = mock(EgadsResult.Series.class); EgadsResult.Series[] data = {series, series, series}; when(er.getData()).thenReturn(data); List<Anomaly> anomalies = Lists.newArrayList(new Anomaly()); when(er.getAnomalies()).thenReturn(anomalies); List<EgadsResult> erlist = Lists.newArrayList(er); when(tte.render(any(ModelAndView.class))).thenReturn("html"); when(ds.detectWithResults(any(), anyDouble(), any(), anyInt(), any())).thenReturn(erlist); ModelAndView mav = Routes.debugPerformEgadsQuery(req, res); assertTrue(params(mav).containsKey("tableHtml")); assertEquals(params(mav).get("tableHtml"), "html"); assertTrue(params(mav).containsKey("data")); verify(ds, times(1)).detectWithResults(any(), anyDouble(), any(), anyInt(), any()); verify(jes, times(1)).getReports(any(), any()); } @Test public void testCloneJob() throws Exception { mocks(); JobMetadata jm = new JobMetadata(); when(req.params(Constants.ID)).thenReturn("1"); inject("jobAccessor", jma); jm.setJobId(1); jm.setTestName("test1"); jm.setFrequency("day"); when(jma.getJobMetadata(anyString())).thenReturn(jm); doAnswer(iom -> { Object[] args = iom.getArguments(); ((JobMetadata) args[0]).setJobId(10); assertEquals(((JobMetadata) args[0]).getJobId(), (Integer) 10); assertEquals(((JobMetadata) args[0]).getTestName(), "test1_cloned"); return ((JobMetadata) args[0]).getJobId().toString(); } ).when(jma).putJobMetadata(any(JobMetadata.class)); assertEquals(Routes.cloneJob(req, res), "10"); verify(res, times(1)).status(200); when(jma.getJobMetadata(anyString())).thenThrow(new IOException("clone error")); assertEquals(Routes.cloneJob(req, res), "clone error"); verify(res, times(1)).status(500); } @Test public void testRerunJob() throws Exception { mocks(); ServiceFactory sf = mock(ServiceFactory.class); JobMetadata jm = new JobMetadata(); when(req.body()).thenReturn("{\"query\":\"{}\",\"granularity\":\"day\",\"jobId\":\"2\",\"timestamp\":\"20000000\"}"); inject("jobAccessor", jma); inject("serviceFactory", sf); jm.setJobId(2); jm.setGranularity("day"); when(jma.getJobMetadata(anyString())).thenReturn(jm); when(sf.newJobExecutionService()).thenReturn(jes); doNothing().when(jes).performBackfillJob(any(JobMetadata.class), any(ZonedDateTime.class), any(ZonedDateTime.class)); assertEquals(Routes.rerunJob(req, res), "success"); verify(jma, times(1)).getJobMetadata(anyString()); verify(jes, times(1)).performBackfillJob(any(JobMetadata.class), any(ZonedDateTime.class), any(ZonedDateTime.class)); when(jma.getJobMetadata(anyString())).thenThrow(new IOException("rerun error")); assertEquals(Routes.rerunJob(req, res), "rerun error"); verify(res, times(1)).status(500); when(req.body()).thenReturn("{\"query\":\"{}\",\"granularity\":\"day\",\"jobId\":\"%3c%73%\",\"timestamp\":\"20000000\"}"); assertEquals(Routes.rerunJob(req, res), "Invalid Job!"); } @Test public void testClearReportsOfSelectedJobs() throws Exception { mocks(); when(req.params(anyString())).thenReturn("1,2,3"); assertEquals(Routes.clearReportsOfSelectedJobs(req, res), "success"); verify(ara, times(3)).deleteAnomalyReportsForJob(anyString()); doThrow(new IOException("io error")).when(ara).deleteAnomalyReportsForJob("2"); assertEquals(Routes.clearReportsOfSelectedJobs(req, res), "io error"); verify(res, times(1)).status(500); } @Test public void testLaunchSelectedJobs() throws Exception { mocks(); SchedulerService ss = mock(SchedulerService.class); inject("schedulerService", ss); when(req.params(anyString())).thenReturn("1,2,3"); JobMetadata job1 = DBTestHelper.getNewJob(); job1.setJobId(1); job1.setJobStatus(JobStatus.RUNNING.getValue()); JobMetadata job2 = DBTestHelper.getNewJob(); job2.setJobId(2); job2.setJobStatus(JobStatus.NODATA.getValue()); JobMetadata job3 = DBTestHelper.getNewJob(); job3.setJobId(3); job3.setJobStatus(JobStatus.CREATED.getValue()); when(jma.getJobMetadata("1")).thenReturn(job1); when(jma.getJobMetadata("2")).thenReturn(job2); when(jma.getJobMetadata("3")).thenReturn(job3); DruidCluster cluster = DBTestHelper.getNewDruidCluster(); when(dca.getDruidCluster(anyInt())).thenReturn(cluster); doNothing().when(ss).scheduleJob(any(JobMetadata.class)); when(jma.putJobMetadata(any(JobMetadata.class))).thenReturn(""); assertEquals(Routes.launchSelectedJobs(req, res), Constants.SUCCESS); verify(res, times(1)).status(200); verify(jma, times(1)).putJobMetadata(any(JobMetadata.class)); verify(ss, times(1)).scheduleJob(any(JobMetadata.class)); assertEquals(job3.getJobStatus(), "RUNNING"); job3.setJobStatus("CREATED"); when(jma.putJobMetadata(any(JobMetadata.class))).thenThrow(new IOException("io error")); assertEquals(Routes.launchSelectedJobs(req, res), "io error"); verify(res, times(1)).status(500); } @Test public void testStopSelectedJobs() throws Exception { mocks(); SchedulerService ss = mock(SchedulerService.class); inject("schedulerService", ss); when(req.params(anyString())).thenReturn("1,2,3"); JobMetadata job1 = DBTestHelper.getNewJob(); job1.setJobId(1); job1.setJobStatus(JobStatus.RUNNING.getValue()); JobMetadata job2 = DBTestHelper.getNewJob(); job2.setJobId(2); job2.setJobStatus(JobStatus.NODATA.getValue()); JobMetadata job3 = DBTestHelper.getNewJob(); job3.setJobId(3); job3.setJobStatus(JobStatus.STOPPED.getValue()); when(jma.getJobMetadata("1")).thenReturn(job1); when(jma.getJobMetadata("2")).thenReturn(job2); when(jma.getJobMetadata("3")).thenReturn(job3); doNothing().when(ss).stopJob(anyInt()); when(jma.putJobMetadata(any(JobMetadata.class))).thenReturn(""); assertEquals(Routes.stopSelectedJobs(req, res), Constants.SUCCESS); verify(res, times(1)).status(200); verify(jma, times(3)).putJobMetadata(any(JobMetadata.class)); verify(ss, times(3)).stopJob(anyInt()); assertEquals(job3.getJobStatus(), "STOPPED"); assertEquals(job2.getJobStatus(), "STOPPED"); assertEquals(job1.getJobStatus(), "STOPPED"); when(jma.putJobMetadata(any(JobMetadata.class))).thenThrow(new IOException("io error")); assertEquals(Routes.stopSelectedJobs(req, res), "io error"); verify(res, times(1)).status(500); } @Test public void testdeleteSelectedJobs() throws Exception { mocks(); SchedulerService ss = mock(SchedulerService.class); inject("schedulerService", ss); when(req.params(anyString())).thenReturn("1,2,3"); Set<String> jobSet = new HashSet<String>() { { add("1"); add("2"); add("3"); } }; doNothing().when(jma).deleteJobs(jobSet); doNothing().when(ss).stopJob(jobSet); assertEquals(Routes.deleteSelectedJobs(req, res), Constants.SUCCESS); verify(jma, times(1)).deleteJobs(jobSet); verify(ss, times(1)).stopJob(jobSet); doThrow(new IOException("io error")).when(jma).deleteJobs(jobSet); assertEquals(Routes.deleteSelectedJobs(req, res), "io error"); verify(res, times(1)).status(500); } @Test public void testUpdateEmail() throws IOException, EmailNotFoundException { mocks(); when(req.body()).thenReturn( "{" + "\"emailId\":\"[email protected]\"," + "\"sendOutHour\":\"23\"," + "\"sendOutMinute\":\"54\"," + "\"repeatInterval\":\"day\"" + "}" ); when(ema.getEmailMetadata("[email protected]")).thenReturn(new EmailMetaData("[email protected]")); doNothing().when(ema).removeFromTriggerIndex(anyString(), anyString()); doNothing().when(ema).putEmailMetadata(any(EmailMetaData.class)); assertEquals(Routes.updateEmails(req, res), Constants.SUCCESS); verify(ema, times(1)).removeFromTriggerIndex(anyString(), anyString()); when(req.body()).thenReturn( "{" + "\"emailId\":\"invalid.com\"," + "\"sendOutHour\":\"23\"," + "\"sendOutMinute\":\"54\"," + "\"repeatInterval\":\"day\"" + "}" ); assertEquals(Routes.updateEmails(req, res), "Invalid Email!"); } @Test public void testViewEmail() throws IOException, EmailNotFoundException { mocks(); when(req.params(Constants.ID)).thenReturn("[email protected]"); when(ema.getEmailMetadata(anyString())).thenReturn(new EmailMetaData("[email protected]")); ModelAndView mav = Routes.viewEmails(req, res); assertEquals(mav.getViewName(), "emailInfo"); } @Test public void testDeleteEmail() throws IOException, EmailNotFoundException { mocks(); when(req.body()).thenReturn("{\"emailId\":\"[email protected]\"}"); when(ema.getEmailMetadata(anyString())).thenReturn(new EmailMetaData("[email protected]")); doNothing().when(jma).deleteEmailFromJobs(any(EmailMetaData.class)); String result = Routes.deleteEmail(req, res); assertEquals(result, Constants.SUCCESS); // test exception doThrow(new IOException("error")).when(jma).deleteEmailFromJobs(any(EmailMetaData.class)); result = Routes.deleteEmail(req, res); assertEquals(result, Constants.ERROR); when(req.body()).thenReturn("{\"emailId\":\"xyzabc.com\"}"); result = Routes.deleteEmail(req, res); assertEquals(result, "Invalid Email!"); } @Test public void testRestoreRedisDBForm() throws IOException, EmailNotFoundException { mocks(); ModelAndView mav = Routes.restoreRedisDBForm(req, res); assertEquals(mav.getViewName(), "redisRestoreForm"); } @Test public void testRestoreRedisDB() throws IOException, EmailNotFoundException, SchedulerException { mocks(); when(req.body()).thenReturn("{\"path\":\"//path//test//file//dump.json\"}"); SchedulerService ss = mock(SchedulerService.class); inject("schedulerService", ss); JsonDumper jd = mock(JsonDumper.class); inject("jsonDumper", jd); doNothing().when(jd).writeRawData(any()); doNothing().when(ss).removeAllJobsFromQueue(); String response = Routes.restoreRedisDB(req, res); assertNotEquals(response, Constants.SUCCESS); } }