diff --git a/server/src/main/java/com/mirth/connect/server/alert/AlertWorker.java b/server/src/main/java/com/mirth/connect/server/alert/AlertWorker.java index 4de7603b73..de391f230e 100644 --- a/server/src/main/java/com/mirth/connect/server/alert/AlertWorker.java +++ b/server/src/main/java/com/mirth/connect/server/alert/AlertWorker.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; @@ -207,12 +208,15 @@ public Void call() throws Exception { } // Dispatch a server event to notify that an alert was dispatched - ServerEvent serverEvent = new ServerEvent(serverId, "Alert Dispatched"); - for (Entry entry : context.entrySet()) { - String value = entry.getValue().toString(); - serverEvent.addAttribute(entry.getKey(), value); + try { + ServerEvent serverEvent = new ServerEvent(serverId, "Alert Dispatched"); + for (Entry entry : context.entrySet()) { + serverEvent.addAttribute(entry.getKey(), Objects.toString(entry.getValue(), "")); + } + eventController.dispatchEvent(serverEvent); + } catch (Exception e) { + logger.error("Error dispatching the Alert Dispatched event for alert \"" + alert.getModel().getName() + "\" (" + alertId + ").", e); } - eventController.dispatchEvent(serverEvent); } return null; diff --git a/server/src/test/java/com/mirth/connect/server/alert/AlertWorkerTest.java b/server/src/test/java/com/mirth/connect/server/alert/AlertWorkerTest.java new file mode 100644 index 0000000000..61c8610398 --- /dev/null +++ b/server/src/test/java/com/mirth/connect/server/alert/AlertWorkerTest.java @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: Open Integration Engine + +package com.mirth.connect.server.alert; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import org.apache.logging.log4j.Logger; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.mirth.connect.donkey.model.event.Event; +import com.mirth.connect.donkey.testing.StaticOverride; +import com.mirth.connect.model.ServerEvent; +import com.mirth.connect.model.alert.AlertActionGroup; +import com.mirth.connect.model.alert.AlertModel; +import com.mirth.connect.server.controllers.AlertController; +import com.mirth.connect.server.controllers.ConfigurationController; +import com.mirth.connect.server.controllers.ControllerFactory; +import com.mirth.connect.server.controllers.EventController; +import com.mirth.connect.server.controllers.ExtensionController; + +public class AlertWorkerTest { + + private static final String SERVER_ID = "0000-0000-0000"; + + private StaticOverride factoryOverride; + private ConfigurationController configurationController; + private EventController eventController; + private DefaultAlertWorker worker; + private AlertActionGroup actionGroup; + private Alert alert; + + @Before + public void setup() { + ControllerFactory controllerFactory = mock(ControllerFactory.class); + configurationController = mock(ConfigurationController.class); + eventController = mock(EventController.class); + when(controllerFactory.createConfigurationController()).thenReturn(configurationController); + when(controllerFactory.createEventController()).thenReturn(eventController); + when(controllerFactory.createExtensionController()).thenReturn(mock(ExtensionController.class)); + when(controllerFactory.createAlertController()).thenReturn(mock(AlertController.class)); + when(configurationController.getServerId()).thenReturn(SERVER_ID); + factoryOverride = StaticOverride.install(ControllerFactory.class, "factory", controllerFactory); + + worker = new DefaultAlertWorker(); + actionGroup = new AlertActionGroup(); + AlertModel model = new AlertModel(null, actionGroup); + model.setName("Test Alert"); + worker.enableAlert(model); + alert = worker.getEnabledAlert(model.getId()); + } + + @After + public void teardown() { + if (worker != null) { + worker.shutdown(); + } + if (factoryOverride != null) { + factoryOverride.close(); + } + } + + @Test + public void writesEventWhenServerNameIsNotSet() throws Exception { + // getServerName() is not stubbed, so it returns null, as it does on a server with no name set + Map context = alert.createContext(); + // Filter, transformer and alerts.sendAlert errors carry no connector type + context.put("connectorType", null); + context.put("channelName", "Throws Error"); + + runTask(context); + + ServerEvent event = captureEvent(); + assertEquals("Alert Dispatched", event.getName()); + assertEquals(SERVER_ID, event.getServerId()); + Map attributes = event.getAttributes(); + assertEquals("", attributes.get("serverName")); + assertEquals("", attributes.get("connectorType")); + assertEquals("Test Alert", attributes.get("alertName")); + assertEquals("Throws Error", attributes.get("channelName")); + } + + @Test + public void writesServerNameWhenSet() throws Exception { + when(configurationController.getServerName()).thenReturn("Server A"); + + runTask(alert.createContext()); + + assertEquals("Server A", captureEvent().getAttributes().get("serverName")); + } + + @Test + public void logsWhenTheEventCannotBeDispatched() throws Exception { + when(configurationController.getServerName()).thenReturn("Server A"); + RuntimeException failure = new RuntimeException("event queue unavailable"); + doThrow(failure).when(eventController).dispatchEvent(any(Event.class)); + Logger logger = mock(Logger.class); + worker.logger = logger; + + runTask(alert.createContext()); + + verify(logger).error(eq("Error dispatching the Alert Dispatched event for alert \"Test Alert\" (" + alert.getModel().getId() + ")."), + same(failure)); + } + + private void runTask(Map context) throws Exception { + worker.new ActionTask(alert.getModel().getId(), actionGroup, context).call(); + } + + private ServerEvent captureEvent() { + ArgumentCaptor captor = ArgumentCaptor.forClass(Event.class); + verify(eventController).dispatchEvent(captor.capture()); + return (ServerEvent) captor.getValue(); + } +}