package org.openmrs.module.reporting.report.service; import org.apache.commons.io.FileUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.openmrs.api.context.Context; import org.openmrs.module.reporting.cohort.definition.GenderCohortDefinition; import org.openmrs.module.reporting.common.TestUtil; import org.openmrs.module.reporting.dataset.definition.CohortCrossTabDataSetDefinition; import org.openmrs.module.reporting.dataset.definition.SqlDataSetDefinition; import org.openmrs.module.reporting.definition.DefinitionUtil; import org.openmrs.module.reporting.evaluation.parameter.Mapped; import org.openmrs.module.reporting.evaluation.parameter.Parameter; import org.openmrs.module.reporting.evaluation.parameter.ParameterizableUtil; import org.openmrs.module.reporting.report.Report; import org.openmrs.module.reporting.report.ReportDesign; import org.openmrs.module.reporting.report.ReportProcessorConfiguration; import org.openmrs.module.reporting.report.ReportRequest; import org.openmrs.module.reporting.report.ReportRequest.Priority; import org.openmrs.module.reporting.report.definition.ReportDefinition; import org.openmrs.module.reporting.report.definition.service.ReportDefinitionService; import org.openmrs.module.reporting.report.processor.LoggingReportProcessor; import org.openmrs.module.reporting.report.renderer.CsvReportRenderer; import org.openmrs.module.reporting.report.renderer.RenderingMode; import org.openmrs.module.reporting.report.renderer.ReportRenderer; import org.openmrs.module.reporting.report.renderer.TsvReportRenderer; import org.openmrs.module.reporting.web.renderers.DefaultWebRenderer; import org.openmrs.module.reporting.web.renderers.WebReportRenderer; import org.openmrs.test.BaseContextSensitiveTest; import org.openmrs.test.BaseModuleContextSensitiveTest; import org.openmrs.test.Verifies; import java.io.File; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Properties; import java.util.UUID; public class ReportServiceTest extends BaseModuleContextSensitiveTest { protected static final String XML_DATASET_PATH = "org/openmrs/module/reporting/include/"; protected static final String XML_REPORT_TEST_DATASET = "ReportTestDataset"; /** * Run this before each unit test in this class. The "@Before" method in * {@link BaseContextSensitiveTest} is run right before this method. * * @throws Exception */ @Before public void setup() throws Exception { executeDataSet(XML_DATASET_PATH + new TestUtil().getTestDatasetFilename(XML_REPORT_TEST_DATASET)); } @Test public void shouldSaveReportDefinition() throws Exception { ReportDefinitionService service = Context.getService(ReportDefinitionService.class); ReportDefinition reportDefinition = new ReportDefinition(); reportDefinition.setName("Testing"); ReportDefinition savedReportDefinition = service.saveDefinition(reportDefinition); Assert.assertTrue(savedReportDefinition.getId() != null); } /** * @see {@link ReportService#runReport(ReportRequest)} */ @Test @Verifies(value = "should set uuid on the request", method = "runReport(ReportRequest)") public void runReport_shouldSetUuidOnTheRequest() throws Exception { ReportDefinition def = new ReportDefinition(); ReportRequest request = new ReportRequest(new Mapped<ReportDefinition>(def, null), null, null, Priority.NORMAL, null); Context.getService(ReportService.class).runReport(request); Assert.assertNotNull(request.getUuid()); } /** * @see {@link ReportService#runReport(ReportRequest)} */ @Test @Verifies(value = "should render the report if a plain renderer is specified", method = "runReport(ReportRequest)") public void runReport_shouldRenderTheReportIfAPlainRendererIsSpecified() throws Exception { ReportDefinition def = new ReportDefinition(); SqlDataSetDefinition dsd = new SqlDataSetDefinition(); dsd.setSqlQuery("select count(*) from patient"); def.addDataSetDefinition("patients", dsd, null); ReportRenderer renderer = new TsvReportRenderer(); ReportRequest request = new ReportRequest(new Mapped<ReportDefinition>(def, null), null, new RenderingMode(renderer, "TSV", null, 100), Priority.NORMAL, null); Report result = Context.getService(ReportService.class).runReport(request); Assert.assertNotNull(result.getRenderedOutput()); } /** * @see {@link ReportService#runReport(ReportRequest)} */ @Test @Verifies(value = "should not render the report if a web renderer is specified", method = "runReport(ReportRequest)") public void runReport_shouldNotRenderTheReportIfAWebRendererIsSpecified() throws Exception { ReportDefinition def = new ReportDefinition(); WebReportRenderer renderer = new DefaultWebRenderer(); ReportRequest request = new ReportRequest(new Mapped<ReportDefinition>(def, null), null, new RenderingMode(renderer, "Web", null, 100), Priority.NORMAL, null); Report result = Context.getService(ReportService.class).runReport(request); Assert.assertNotNull(result.getReportData()); Assert.assertNull(result.getRenderedOutput()); } /** * @see {@link ReportService#runReport(ReportRequest)} */ @Test @Verifies(value = "should allow dynamic parameters", method = "runReport(ReportRequest)") public void runReport_shouldAllowDynamicParameters() throws Exception { ReportDefinition rptDef = new ReportDefinition(); rptDef.addParameter(new Parameter("effectiveDate", "Effective Date", Date.class)); SqlDataSetDefinition sqlDef = new SqlDataSetDefinition("test sql dsd", null, "select person_id, birthdate from person where birthdate < :effectiveDate"); sqlDef.addParameter(new Parameter("effectiveDate", "Effective Date", Date.class)); rptDef.addDataSetDefinition(sqlDef, ParameterizableUtil.createParameterMappings("effectiveDate=${effectiveDate}")); RenderingMode mode = new RenderingMode(new CsvReportRenderer(), "CSV", null, 100); Mapped<ReportDefinition> mappedReport = new Mapped<ReportDefinition>(); mappedReport.setParameterizable(rptDef); mappedReport.addParameterMapping("effectiveDate", "${now-50y}"); ReportRequest request = new ReportRequest(mappedReport, null, mode, Priority.HIGHEST, null); Report report = Context.getService(ReportService.class).runReport(request); String s = new String(report.getRenderedOutput()); } /** * @verifies save a report processor configuration * @see ReportService#saveReportProcessorConfiguration(ReportProcessorConfiguration) */ @Test public void saveReportProcessorConfiguration_shouldSaveAReportProcessorConfiguration() throws Exception { ReportService rs = Context.getService(ReportService.class); ReportProcessorConfiguration c = new ReportProcessorConfiguration(); c.setName("New Processor"); c.setProcessorType(LoggingReportProcessor.class.getName()); c = rs.saveReportProcessorConfiguration(c); Assert.assertNotNull(c.getId()); Assert.assertNotNull(c.getUuid()); Assert.assertEquals(3, rs.getAllReportProcessorConfigurations(true).size()); } /** * @verifies retrieve all saved report processor configurations including retired if specified * @see ReportService#getAllReportProcessorConfigurations(boolean) */ @Test public void getAllReportProcessorConfigurations_shouldRetrieveAllSavedReportProcessorConfigurationsIncludingRetiredIfSpecified() throws Exception { ReportService rs = Context.getService(ReportService.class); Assert.assertEquals(2, rs.getAllReportProcessorConfigurations(true).size()); Assert.assertEquals(1, rs.getAllReportProcessorConfigurations(false).size()); } /** * @verifies retrieve a saved report processor configuration by id * @see ReportService#getReportProcessorConfiguration(Integer) */ @Test public void getReportProcessorConfiguration_shouldRetrieveASavedReportProcessorConfigurationById() throws Exception { ReportService rs = Context.getService(ReportService.class); ReportProcessorConfiguration c = rs.getReportProcessorConfiguration(2); Assert.assertEquals("Logging processor", c.getName()); } /** * @verifies retrieve a saved report processor configuration by uuid * @see ReportService#getReportProcessorConfigurationByUuid(String) */ @Test public void getReportProcessorConfigurationByUuid_shouldRetrieveASavedReportProcessorConfigurationByUuid() throws Exception { ReportService rs = Context.getService(ReportService.class); ReportProcessorConfiguration c = rs.getReportProcessorConfigurationByUuid("c11117dd-4478-4a0e-84fe-ee62c5f0676a"); Assert.assertEquals("Logging processor", c.getName()); } /** * @verifies retrieve all non-retired report processor configurations that are assignable to the passed type * @see ReportService#getReportProcessorConfigurations(Class) */ @Test public void getReportProcessorConfigurations_shouldRetrieveAllNonretiredReportProcessorConfigurationsThatAreAssignableToThePassedType() throws Exception { ReportService rs = Context.getService(ReportService.class); Assert.assertEquals(1, rs.getReportProcessorConfigurations(LoggingReportProcessor.class).size()); } /** * @verifies delete a saved report processor configuration * @see ReportService#purgeReportProcessorConfiguration(ReportProcessorConfiguration) */ @Test public void purgeReportProcessorConfiguration_shouldDeleteASavedReportProcessorConfiguration() throws Exception { ReportService rs = Context.getService(ReportService.class); ReportProcessorConfiguration c = rs.getReportProcessorConfiguration(1); rs.purgeReportProcessorConfiguration(c); Assert.assertEquals(1, rs.getAllReportProcessorConfigurations(true).size()); } @Test @Verifies(value = "should retrieve all global processors after creating a non-global processor", method = "getGlobalReportProcessor") public void shouldRetrieveAllGlobalProcessors() throws Exception { //now we should have three total ReportProcessorConfigs in the db, 2 of which don't have reportDesign set (the two in the dbunit file), meaning that they're global. // but 1 is retired, so there should only be 1 List<ReportProcessorConfiguration> ret = Context.getService(ReportService.class).getGlobalReportProcessorConfigurations(); Assert.assertTrue(ret.size() == 1); } @Test @Verifies(value = "should retrieve all global processors after creating a non-global processor", method = "getGlobalReportProcessor") public void shouldRetrieveAllGlobalProcessorsAfterAddingGlobalProcessor() throws Exception { //create a report processor config Properties props = new Properties(); ReportProcessorConfiguration procConfig = new ReportProcessorConfiguration("Test Processor", LoggingReportProcessor.class, props, true, true); String procUuid = UUID.randomUUID().toString(); procConfig.setUuid(procUuid); procConfig.setProcessorMode(ReportProcessorConfiguration.ProcessorMode.ON_DEMAND_AND_AUTOMATIC); Context.getService(ReportService.class).saveReportProcessorConfiguration(procConfig); //there was 1 to start with, now there should be 2 List<ReportProcessorConfiguration> ret = Context.getService(ReportService.class).getGlobalReportProcessorConfigurations(); Assert.assertTrue(ret.size() == 2); } /** * @verifies execute any configured report processors * @see ReportService#runReport(ReportRequest) */ @Test public void runReport_shouldExecuteTestReportProcessor() throws Exception { ReportDefinition def = new ReportDefinition(); def.setName("My report"); SqlDataSetDefinition dsd = new SqlDataSetDefinition(); dsd.setSqlQuery("select count(*) from patient"); def.addDataSetDefinition("patients", dsd, null); Context.getService(ReportDefinitionService.class).saveDefinition(def); RenderingMode rm = new RenderingMode(new TsvReportRenderer(), "TSV", null, 100); ReportRequest request = new ReportRequest(new Mapped<ReportDefinition>(def, null), null, rm, Priority.NORMAL, null); request.setProcessAutomatically(true); //build a processor Properties props = new Properties(); ReportProcessorConfiguration procConfig = new ReportProcessorConfiguration("LoggingProcessorTest", TestReportProcessor.class, props, true, true); String procUuid = UUID.randomUUID().toString(); procConfig.setUuid(procUuid); procConfig.setProcessorMode(ReportProcessorConfiguration.ProcessorMode.AUTOMATIC); //test processor can run because processing mode is automatic //create and save a report design, containing the processor ReportDesign rd = new ReportDesign(); rd.setName("myReportDesign"); rd.addReportProcessor(procConfig); rd.setReportDefinition(def); rd.setRendererType(TsvReportRenderer.class); String uuid = UUID.randomUUID().toString(); rd.setUuid(uuid); Context.getService(ReportService.class).saveReportDesign(rd); //run the report Report report = Context.getService(ReportService.class).runReport(request); //TestReportProcessor is a simple processor that set a report error message -- just a simple way to ensure the processor was run... Assert.assertTrue(report.getErrorMessage().equals("TestReportProcessor.process was called corretly.")); //sanity check on global processors -- the one we create here isn't global, so there should only be 1 List<ReportProcessorConfiguration> ret = Context.getService(ReportService.class).getGlobalReportProcessorConfigurations(); Assert.assertTrue(ret.size() == 1); } /** * @verifies execute any configured report processors * @see ReportService#runReport(ReportRequest) */ @Test public void runReport_shouldNotExecuteTestReportProcessorDifferentRenderers() throws Exception { ReportDefinition def = new ReportDefinition(); def.setName("My report"); SqlDataSetDefinition dsd = new SqlDataSetDefinition(); dsd.setSqlQuery("select count(*) from patient"); def.addDataSetDefinition("patients", dsd, null); Context.getService(ReportDefinitionService.class).saveDefinition(def); RenderingMode rm = new RenderingMode(new TsvReportRenderer(), "TSV", null, 100); ReportRequest request = new ReportRequest(new Mapped<ReportDefinition>(def, null), null, rm, Priority.NORMAL, null); //build a processor Properties props = new Properties(); ReportProcessorConfiguration procConfig = new ReportProcessorConfiguration("LoggingProcessorTest", TestReportProcessor.class, props, true, true); String procUuid = UUID.randomUUID().toString(); procConfig.setUuid(procUuid); procConfig.setProcessorMode(ReportProcessorConfiguration.ProcessorMode.AUTOMATIC); //test processor can run because processing mode is automatic //create and save a report design, containing the processor ReportDesign rd = new ReportDesign(); rd.setName("myReportDesign"); rd.addReportProcessor(procConfig); rd.setReportDefinition(def); rd.setRendererType(CsvReportRenderer.class); // test processor won't run because report request is Tsv, reportDefinition is Csv String uuid = UUID.randomUUID().toString(); rd.setUuid(uuid); Context.getService(ReportService.class).saveReportDesign(rd); //run the report Report report = Context.getService(ReportService.class).runReport(request); //TestReportProcessor is a simple processor that set a report error message -- just a simple way to ensure the processor was run... Assert.assertTrue(report.getErrorMessage() == null); } /** * @verifies execute any configured report processors * @see ReportService#runReport(ReportRequest) */ @Test public void runReport_shouldNotExecuteTestReportProcessorNotAutomatic() throws Exception { ReportDefinition def = new ReportDefinition(); def.setName("My report"); SqlDataSetDefinition dsd = new SqlDataSetDefinition(); dsd.setSqlQuery("select count(*) from patient"); def.addDataSetDefinition("patients", dsd, null); Context.getService(ReportDefinitionService.class).saveDefinition(def); RenderingMode rm = new RenderingMode(new TsvReportRenderer(), "TSV", null, 100); ReportRequest request = new ReportRequest(new Mapped<ReportDefinition>(def, null), null, rm, Priority.NORMAL, null); //build a processor Properties props = new Properties(); ReportProcessorConfiguration procConfig = new ReportProcessorConfiguration("LoggingProcessorTest", TestReportProcessor.class, props, true, true); String procUuid = UUID.randomUUID().toString(); procConfig.setUuid(procUuid); procConfig.setProcessorMode(ReportProcessorConfiguration.ProcessorMode.ON_DEMAND); //test processor won't be run because its not automatic //create and save a report design, containing the processor ReportDesign rd = new ReportDesign(); rd.setName("myReportDesign"); rd.addReportProcessor(procConfig); rd.setReportDefinition(def); rd.setRendererType(TsvReportRenderer.class); String uuid = UUID.randomUUID().toString(); rd.setUuid(uuid); Context.getService(ReportService.class).saveReportDesign(rd); //run the report Report report = Context.getService(ReportService.class).runReport(request); //TestReportProcessor is a simple processor that set a report error message -- just a simple way to ensure the processor was run... Assert.assertTrue(report.getErrorMessage() == null); } @Test @Verifies(value = "should save the ReportProcessor", method = "saveReportDesign(ReportDesign)") public void shouldSaveReportDefinitionWithProcessor() throws Exception { //save a blank report definition ReportDefinitionService service = Context.getService(ReportDefinitionService.class); ReportService rs = Context.getService(ReportService.class); ReportDefinition reportDefinition = new ReportDefinition(); reportDefinition.setName("Testing"); service.saveDefinition(reportDefinition); //create a report processor config Properties props = new Properties(); ReportProcessorConfiguration procConfig = new ReportProcessorConfiguration("LoggingProcessorTest", LoggingReportProcessor.class, props, true, true); String procUuid = UUID.randomUUID().toString(); procConfig.setUuid(procUuid); procConfig.setProcessorMode(ReportProcessorConfiguration.ProcessorMode.ON_DEMAND_AND_AUTOMATIC); //create and save a report design, containing the processor ReportDesign rd = new ReportDesign(); rd.setName("myReportDesign"); rd.addReportProcessor(procConfig); rd.setReportDefinition(reportDefinition); rd.setRendererType(CsvReportRenderer.class); String uuid = UUID.randomUUID().toString(); rd.setUuid(uuid); rs.saveReportDesign(rd); //retreive and verify the processor ReportProcessorConfiguration rpc = rs.getReportProcessorConfigurationByUuid(procUuid); Assert.assertTrue(rpc != null); Assert.assertTrue(rpc.getProcessorMode().equals(ReportProcessorConfiguration.ProcessorMode.ON_DEMAND_AND_AUTOMATIC)); rpc = null; //retrieve and verify that the processor is retreived with ReportDesign ReportDesign ret = rs.getReportDesignByUuid(uuid); Assert.assertTrue(ret != null); Assert.assertTrue(ret.getReportProcessors().size() == 1); ReportProcessorConfiguration rp = ret.getReportProcessors().iterator().next(); Assert.assertTrue(rp.getProcessorMode().equals(ReportProcessorConfiguration.ProcessorMode.ON_DEMAND_AND_AUTOMATIC)); } @Test @Verifies(value = "readProcessorModeCorrectly", method = "getReportProcessorConfiguration(id)") public void shouldReadProcessorModeEnumCorrectly() throws Exception { ReportService rs = Context.getService(ReportService.class); ReportProcessorConfiguration rpc = rs.getReportProcessorConfiguration(1); Assert.assertTrue(rpc.getProcessorMode().equals(ReportProcessorConfiguration.ProcessorMode.DISABLED)); rpc = rs.getReportProcessorConfiguration(2); Assert.assertTrue(rpc.getProcessorMode().equals(ReportProcessorConfiguration.ProcessorMode.AUTOMATIC)); } /** * @verifies set the evaluationDate on the context from the request * @see ReportService#runReport(org.openmrs.module.reporting.report.ReportRequest) */ @Test public void runReport_shouldSetTheEvaluationDateOnTheContextFromTheRequest() throws Exception { ReportDefinition def = new ReportDefinition(); ReportRequest request = new ReportRequest(new Mapped<ReportDefinition>(def, null), null, null, Priority.NORMAL, null); Calendar c = Calendar.getInstance(); c.set(1975, Calendar.OCTOBER, 16); request.setEvaluationDate(c.getTime()); Report actual = Context.getService(ReportService.class).runReport(request); Assert.assertEquals(actual.getReportData().getContext().getEvaluationDate(), c.getTime()); } /** * @verifies use current date as evaluationDate if not provided by the request * @see ReportService#runReport(org.openmrs.module.reporting.report.ReportRequest) */ @Test public void runReport_shouldUseCurrentDateAsEvaluationDateIfNotProvidedByTheRequest() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); ReportDefinition def = new ReportDefinition(); ReportRequest request = new ReportRequest(new Mapped<ReportDefinition>(def, null), null, null, Priority.NORMAL, null); Report actual = Context.getService(ReportService.class).runReport(request); Assert.assertEquals(sdf.format(actual.getReportData().getContext().getEvaluationDate()), sdf.format(new Date())); } @Test public void saveReport_shouldSaveSuccessfullyIfNotCached() throws Exception { ReportDefinition def = new ReportDefinition(); SqlDataSetDefinition dsd = new SqlDataSetDefinition(); dsd.setSqlQuery("select count(*) from patient"); def.addDataSetDefinition("patients", dsd, null); ReportRenderer renderer = new TsvReportRenderer(); ReportRequest request = new ReportRequest(new Mapped<ReportDefinition>(def, null), null, new RenderingMode(renderer, "TSV", null, 100), Priority.NORMAL, null); Report result = Context.getService(ReportService.class).runReport(request); Context.getService(ReportService.class).saveReport(result, "Test Saving"); } @Test public void runReport_shouldLogMessagesToReportRequestLogFile() throws Exception { ReportDefinition def = new ReportDefinition(); def.setName("A Test Report"); CohortCrossTabDataSetDefinition dsd = new CohortCrossTabDataSetDefinition(); dsd.setName("Patients By Gender"); GenderCohortDefinition males = new GenderCohortDefinition(); males.setName("Males"); males.setMaleIncluded(true); dsd.addColumn("Males", males, null); GenderCohortDefinition females = new GenderCohortDefinition(); females.setFemaleIncluded(true); dsd.addColumn("Females", females, null); def.addDataSetDefinition("patients", dsd, null); ReportRenderer renderer = new TsvReportRenderer(); ReportRequest request = new ReportRequest(new Mapped<ReportDefinition>(def, null), null, new RenderingMode(renderer, "TSV", null, 100), Priority.NORMAL, null); Report result = getReportService().runReport(request); File reportLog = getReportService().getReportLogFile(request); String s = FileUtils.readFileToString(reportLog, "UTF-8"); Assert.assertTrue(s.contains("Evaluating A Test Report")); Assert.assertTrue(s.contains("Evaluating Patients By Gender")); Assert.assertTrue(s.contains("Evaluating " + DefinitionUtil.format(females))); } public ReportService getReportService() { return Context.getService(ReportService.class); } }