Add more coverage to drools-pdp health check 90/69690/3
authorJim Hahn <jrh3@att.com>
Tue, 2 Oct 2018 19:29:54 +0000 (15:29 -0400)
committerJim Hahn <jrh3@att.com>
Wed, 3 Oct 2018 20:56:11 +0000 (16:56 -0400)
Added getProperties() method to Factory, as the system properties could
not be found in the jenkins build, for some reason.
Removed Factory, as not needed for junit testing.

Change-Id: I5070b9502ae06be6995cccbb005653928591512c
Issue-ID: POLICY-1148
Signed-off-by: Jim Hahn <jrh3@att.com>
feature-healthcheck/pom.xml
feature-healthcheck/src/main/java/org/onap/policy/drools/healthcheck/HealthCheck.java
feature-healthcheck/src/test/java/org/onap/policy/drools/healthcheck/HealthCheckTest.java

index 17a5053..1fc853e 100644 (file)
   ============LICENSE_END=========================================================
   -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-         
-  <modelVersion>4.0.0</modelVersion>
-  
-  <parent>
-    <groupId>org.onap.policy.drools-pdp</groupId>
-    <artifactId>drools-pdp</artifactId>
-    <version>1.3.0-SNAPSHOT</version>
-  </parent>
-  
-  <artifactId>feature-healthcheck</artifactId>
-  
-  <name>feature-healthcheck</name>
-  <description>Loadable module that performs remote system healthchecks</description>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onap.policy.drools-pdp</groupId>
+        <artifactId>drools-pdp</artifactId>
+        <version>1.3.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>feature-healthcheck</artifactId>
+
+    <name>feature-healthcheck</name>
+    <description>Loadable module that performs remote system healthchecks</description>
 
-  <properties>
-          <maven.compiler.source>1.8</maven.compiler.source>
-          <maven.compiler.target>1.8</maven.compiler.target>
-  </properties>
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+    </properties>
 
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>zipfile</id>
-            <goals>
-              <goal>single</goal>
-            </goals>
-            <phase>package</phase>
-            <configuration>
-              <attach>true</attach>
-              <finalName>${project.artifactId}-${project.version}</finalName>
-              <descriptors>
-                <descriptor>src/assembly/assemble_zip.xml</descriptor>
-              </descriptors>
-              <appendAssemblyId>false</appendAssemblyId>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-dependency-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>copy-dependencies</id>
-            <goals>
-              <goal>copy-dependencies</goal>
-            </goals>
-            <phase>prepare-package</phase>
-            <configuration>
-              <transitive>false</transitive>
-              <outputDirectory>${project.build.directory}/assembly/lib</outputDirectory>
-              <overWriteReleases>false</overWriteReleases>
-              <overWriteSnapshots>true</overWriteSnapshots>
-              <overWriteIfNewer>true</overWriteIfNewer>
-              <useRepositoryLayout>false</useRepositoryLayout>
-              <addParentPoms>false</addParentPoms>
-              <copyPom>false</copyPom>
-                         <includeScope>runtime</includeScope>
-                         <excludeTransitive>true</excludeTransitive>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-          <plugin>
-              <artifactId>maven-checkstyle-plugin</artifactId>
-              <executions>
-                  <execution>
-                      <id>onap-java-style</id>
-                      <goals>
-                          <goal>check</goal>
-                      </goals>
-                      <phase>process-sources</phase>
-                      <configuration>
-                          <!-- Use Google Java Style Guide: https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml 
-                              with minor changes -->
-                          <configLocation>onap-checkstyle/onap-java-style.xml</configLocation>
-                          <!-- <sourceDirectory> is needed so that checkstyle ignores the generated sources directory -->
-                          <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
-                          <includeResources>true</includeResources>
-                          <includeTestSourceDirectory>true</includeTestSourceDirectory>
-                          <includeTestResources>true</includeTestResources>
-                          <excludes>
-                          </excludes>
-                          <suppressionsLocation>${project.baseUri}checkstyle-suppressions.xml</suppressionsLocation>
-                          <consoleOutput>true</consoleOutput>
-                          <failsOnViolation>true</failsOnViolation>
-                          <violationSeverity>warning</violationSeverity>
-                      </configuration>
-                  </execution>
-              </executions>
-              <dependencies>
-                  <dependency>
-                      <groupId>org.onap.oparent</groupId>
-                      <artifactId>checkstyle</artifactId>
-                      <version>${oparent.version}</version>
-                      <scope>compile</scope>
-                  </dependency>
-              </dependencies>
-          </plugin>
-    </plugins>
-  </build>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>zipfile</id>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <attach>true</attach>
+                            <finalName>${project.artifactId}-${project.version}</finalName>
+                            <descriptors>
+                                <descriptor>src/assembly/assemble_zip.xml</descriptor>
+                            </descriptors>
+                            <appendAssemblyId>false</appendAssemblyId>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <phase>prepare-package</phase>
+                        <configuration>
+                            <transitive>false</transitive>
+                            <outputDirectory>${project.build.directory}/assembly/lib</outputDirectory>
+                            <overWriteReleases>false</overWriteReleases>
+                            <overWriteSnapshots>true</overWriteSnapshots>
+                            <overWriteIfNewer>true</overWriteIfNewer>
+                            <useRepositoryLayout>false</useRepositoryLayout>
+                            <addParentPoms>false</addParentPoms>
+                            <copyPom>false</copyPom>
+                            <includeScope>runtime</includeScope>
+                            <excludeTransitive>true</excludeTransitive>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>onap-java-style</id>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                        <configuration>
+                            <!-- Use Google Java Style Guide: https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml 
+                                with minor changes -->
+                            <configLocation>onap-checkstyle/onap-java-style.xml</configLocation>
+                            <!-- <sourceDirectory> is needed so that checkstyle ignores the generated sources directory -->
+                            <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
+                            <includeResources>true</includeResources>
+                            <includeTestSourceDirectory>true</includeTestSourceDirectory>
+                            <includeTestResources>true</includeTestResources>
+                            <excludes>
+                            </excludes>
+                            <suppressionsLocation>${project.baseUri}checkstyle-suppressions.xml</suppressionsLocation>
+                            <consoleOutput>true</consoleOutput>
+                            <failsOnViolation>true</failsOnViolation>
+                            <violationSeverity>warning</violationSeverity>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.onap.oparent</groupId>
+                        <artifactId>checkstyle</artifactId>
+                        <version>${oparent.version}</version>
+                        <scope>compile</scope>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
 
-  <dependencies>
-       <dependency>
-               <groupId>io.swagger</groupId>
-               <artifactId>swagger-jersey2-jaxrs</artifactId>
-               <scope>provided</scope>
-       </dependency>
-       <dependency>
-              <groupId>org.onap.policy.drools-pdp</groupId>
-              <artifactId>policy-core</artifactId>
-              <version>${project.version}</version>
-              <scope>provided</scope>
-       </dependency>
-       <dependency>
-               <groupId>org.onap.policy.common</groupId>
-               <artifactId>policy-endpoints</artifactId>
-               <version>${project.version}</version>
-               <scope>provided</scope>
-       </dependency>
-       <dependency>
-               <groupId>org.onap.policy.drools-pdp</groupId>
-               <artifactId>policy-management</artifactId>
-               <version>${project.version}</version>
-               <scope>provided</scope>
-       </dependency>
-       <dependency>
-               <groupId>junit</groupId>
-               <artifactId>junit</artifactId>
-               <scope>test</scope>
-       </dependency>
-    <dependency>
-        <groupId>org.powermock</groupId>
-        <artifactId>powermock-api-mockito</artifactId>
-        <scope>test</scope>
-    </dependency>
-  </dependencies>
+    <dependencies>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-jersey2-jaxrs</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.drools-pdp</groupId>
+            <artifactId>policy-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.common</groupId>
+            <artifactId>policy-endpoints</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.drools-pdp</groupId>
+            <artifactId>policy-management</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
 
 </project>
index 40e4f35..36444f8 100644 (file)
@@ -7,9 +7,9 @@
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -28,7 +28,9 @@ import javax.ws.rs.core.Response;
 
 import org.onap.policy.common.capabilities.Startable;
 import org.onap.policy.common.endpoints.http.client.HttpClient;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
 import org.onap.policy.drools.persistence.SystemPersistence;
 import org.onap.policy.drools.system.PolicyEngine;
 import org.slf4j.Logger;
@@ -168,7 +170,7 @@ public interface HealthCheck extends Startable {
 
     /**
      * Perform a healthcheck.
-     * 
+     *
      * @return a report
      */
     public Reports healthCheck();
@@ -186,17 +188,17 @@ class HealthCheckMonitor implements HealthCheck {
     private static Logger logger = LoggerFactory.getLogger(HealthCheckMonitor.class);
 
     /**
-     * attached http servers.
+     * Attached http servers.
      */
     protected volatile List<HttpServletServer> servers = new ArrayList<>();
 
     /**
-     * attached http clients.
+     * Attached http clients.
      */
     protected volatile List<HttpClient> clients = new ArrayList<>();
 
     /**
-     * healthcheck configuration.
+     * Healthcheck configuration.
      */
     protected volatile Properties healthCheckProperties = null;
 
@@ -206,14 +208,15 @@ class HealthCheckMonitor implements HealthCheck {
     @Override
     public Reports healthCheck() {
         Reports reports = new Reports();
-        reports.setHealthy(PolicyEngine.manager.isAlive());
+        boolean thisEngineIsAlive = getEngineManager().isAlive();
+        reports.setHealthy(thisEngineIsAlive);
 
         HealthCheck.Report engineReport = new Report();
-        engineReport.setHealthy(PolicyEngine.manager.isAlive());
+        engineReport.setHealthy(thisEngineIsAlive);
         engineReport.setName("PDP-D");
         engineReport.setUrl("self");
-        engineReport.setCode(PolicyEngine.manager.isAlive() ? 200 : 500);
-        engineReport.setMessage(PolicyEngine.manager.isAlive() ? "alive" : "not alive");
+        engineReport.setCode(thisEngineIsAlive ? 200 : 500);
+        engineReport.setMessage(thisEngineIsAlive ? "alive" : "not alive");
         reports.getDetails().add(engineReport);
 
         for (HttpClient client : clients) {
@@ -248,10 +251,9 @@ class HealthCheckMonitor implements HealthCheck {
     public boolean start() {
 
         try {
-            this.healthCheckProperties =
-                    SystemPersistence.manager.getProperties(HealthCheckFeature.CONFIGURATION_PROPERTIES_NAME);
-            this.servers = HttpServletServer.factory.build(healthCheckProperties);
-            this.clients = HttpClient.factory.build(healthCheckProperties);
+            this.healthCheckProperties = getPersistentProperties(HealthCheckFeature.CONFIGURATION_PROPERTIES_NAME);
+            this.servers = getServerFactory().build(healthCheckProperties);
+            this.clients = getClientFactory().build(healthCheckProperties);
 
             for (HttpServletServer server : servers) {
                 startServer(server);
@@ -307,7 +309,7 @@ class HealthCheckMonitor implements HealthCheck {
 
     /**
      * Get servers.
-     * 
+     *
      * @return list of attached Http Servers
      */
     public List<HttpServletServer> getServers() {
@@ -316,7 +318,7 @@ class HealthCheckMonitor implements HealthCheck {
 
     /**
      * Get clients.
-     * 
+     *
      * @return list of attached Http Clients
      */
     public List<HttpClient> getClients() {
@@ -354,4 +356,21 @@ class HealthCheckMonitor implements HealthCheck {
         return builder.toString();
     }
 
+    // the following methods may be overridden by junit tests
+
+    protected PolicyEngine getEngineManager() {
+        return PolicyEngine.manager;
+    }
+
+    protected HttpServletServerFactory getServerFactory() {
+        return HttpServletServer.factory;
+    }
+
+    protected HttpClientFactory getClientFactory() {
+        return HttpClient.factory;
+    }
+
+    protected Properties getPersistentProperties(String propertyName) {
+        return SystemPersistence.manager.getProperties(propertyName);
+    }
 }
index 850b1ed..2bf1cca 100644 (file)
 package org.onap.policy.drools.healthcheck;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import java.net.HttpURLConnection;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Properties;
+import javax.ws.rs.core.Response;
+import org.junit.Before;
 import org.junit.Test;
+import org.onap.policy.common.endpoints.http.client.HttpClient;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
+import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
 import org.onap.policy.drools.healthcheck.HealthCheck.Report;
 import org.onap.policy.drools.healthcheck.HealthCheck.Reports;
+import org.onap.policy.drools.system.PolicyEngine;
 
 public class HealthCheckTest {
 
@@ -36,9 +50,62 @@ public class HealthCheckTest {
     private static final String RPT_MSG = "report-message";
     private static final String RPT_NAME = "report-name";
     private static final String RPT_URL = "report-url";
+    private static final String EXPECTED = "expected exception";
+
+    private static final String CLIENT_NAME1 = "name-a";
+    private static final String CLIENT_URL1 = "url-a";
+    private static final String CLIENT_NAME2 = "name-b";
+    private static final String CLIENT_URL2 = "url-b";
+    private static final String CLIENT_NAME3 = "name-c";
+    private static final String CLIENT_URL3 = "url-c";
+
+    private Properties properties;
+    private HttpServletServerFactory servletFactory;
+    private HttpServletServer server1;
+    private HttpServletServer server2;
+    private HttpClientFactory clientFactory;
+    private HttpClient client1;
+    private HttpClient client2;
+    private HttpClient client3;
+    private List<HttpServletServer> servers;
+    private List<HttpClient> clients;
+    private PolicyEngine engineMgr;
+    private HealthCheckMonitor monitor;
+
+    /**
+     * Initializes the object to be tested.
+     *
+     * @throws Exception if an error occurs
+     */
+    @Before
+    public void setUp() throws Exception {
+        properties = new Properties();
+        servletFactory = mock(HttpServletServerFactory.class);
+        server1 = mock(HttpServletServer.class);
+        server2 = mock(HttpServletServer.class);
+        clientFactory = mock(HttpClientFactory.class);
+        client1 = mock(HttpClient.class);
+        client2 = mock(HttpClient.class);
+        client3 = mock(HttpClient.class);
+        servers = Arrays.asList(server1, server2);
+        clients = Arrays.asList(client1, client2, client3);
+        engineMgr = mock(PolicyEngine.class);
+
+        when(client1.getName()).thenReturn(CLIENT_NAME1);
+        when(client1.getBaseUrl()).thenReturn(CLIENT_URL1);
+        when(client2.getName()).thenReturn(CLIENT_NAME2);
+        when(client2.getBaseUrl()).thenReturn(CLIENT_URL2);
+        when(client3.getName()).thenReturn(CLIENT_NAME3);
+        when(client3.getBaseUrl()).thenReturn(CLIENT_URL3);
+        when(servletFactory.build(properties)).thenReturn(servers);
+        when(clientFactory.build(properties)).thenReturn(clients);
+        when(engineMgr.isAlive()).thenReturn(true);
+
+        monitor = new HealthCheckMonitorImpl();
+    }
 
     @Test
-    public void testHealthCheck_Report() {
+    public void testReport() {
         Report rpt = new Report();
 
         // toString should work with un-populated data
@@ -65,7 +132,7 @@ public class HealthCheckTest {
     }
 
     @Test
-    public void testHealthCheck_Reports() {
+    public void testReports() {
         Reports reports = new Reports();
 
         // toString should work with un-populated data
@@ -86,4 +153,200 @@ public class HealthCheckTest {
         assertNotNull(reports.toString());
     }
 
+    @Test
+    public void testHealthCheckMonitor_HealthCheck() {
+        monitor.start();
+
+        // first client is healthy
+        Response resp = mock(Response.class);
+        when(resp.getStatus()).thenReturn(HttpURLConnection.HTTP_OK);
+        when(resp.readEntity(String.class)).thenReturn(RPT_MSG);
+        when(client1.get()).thenReturn(resp);
+
+        // second client throws an exception
+        when(client2.get()).thenThrow(new RuntimeException(EXPECTED));
+
+        // third client is not healthy
+        resp = mock(Response.class);
+        when(resp.getStatus()).thenReturn(HttpURLConnection.HTTP_INTERNAL_ERROR);
+        when(resp.readEntity(String.class)).thenReturn(RPT_NAME);
+        when(client3.get()).thenReturn(resp);
+
+        Reports reports = monitor.healthCheck();
+        assertNotNull(reports);
+        assertEquals(4, reports.getDetails().size());
+        assertFalse(reports.isHealthy());
+
+        int index = 0;
+
+        Report report = reports.getDetails().get(index++);
+        assertEquals(true, report.isHealthy());
+        assertEquals("PDP-D", report.getName());
+        assertEquals("self", report.getUrl());
+        assertEquals("alive", report.getMessage());
+        assertEquals(HttpURLConnection.HTTP_OK, report.getCode());
+
+        report = reports.getDetails().get(index++);
+        assertEquals(true, report.isHealthy());
+        assertEquals(client1.getName(), report.getName());
+        assertEquals(client1.getBaseUrl(), report.getUrl());
+        assertEquals(RPT_MSG, report.getMessage());
+        assertEquals(HttpURLConnection.HTTP_OK, report.getCode());
+
+        report = reports.getDetails().get(index++);
+        assertEquals(false, report.isHealthy());
+        assertEquals(client2.getName(), report.getName());
+        assertEquals(client2.getBaseUrl(), report.getUrl());
+
+        report = reports.getDetails().get(index++);
+        assertEquals(false, report.isHealthy());
+        assertEquals(client3.getName(), report.getName());
+        assertEquals(client3.getBaseUrl(), report.getUrl());
+        assertEquals(RPT_NAME, report.getMessage());
+        assertEquals(HttpURLConnection.HTTP_INTERNAL_ERROR, report.getCode());
+
+        // indicate that engine is no longer healthy and re-run health check
+        when(engineMgr.isAlive()).thenReturn(false);
+
+        reports = monitor.healthCheck();
+        report = reports.getDetails().get(0);
+
+        assertEquals(false, report.isHealthy());
+        assertEquals("not alive", report.getMessage());
+        assertEquals(500, report.getCode());
+    }
+
+    @Test
+    public void testHealthCheckMonitor_Start() {
+        // arrange for one server to throw an exception
+        when(server1.start()).thenThrow(new RuntimeException(EXPECTED));
+
+        assertTrue(monitor.start());
+
+        verify(server1).start();
+        verify(server2).start();
+
+        /*
+         * Generate exception during building.
+         */
+
+        // new monitor
+        monitor = new HealthCheckMonitorImpl() {
+            @Override
+            protected HttpServletServerFactory getServerFactory() {
+                throw new RuntimeException(EXPECTED);
+            }
+        };
+
+        assertFalse(monitor.start());
+    }
+
+    @Test
+    public void testHealthCheckMonitor_Stop() {
+        monitor.start();
+
+        // arrange for one server and one client to throw an exception
+        when(server1.stop()).thenThrow(new RuntimeException(EXPECTED));
+        when(client2.stop()).thenThrow(new RuntimeException(EXPECTED));
+
+        assertTrue(monitor.stop());
+
+        verify(server1).stop();
+        verify(server2).stop();
+        verify(client1).stop();
+        verify(client2).stop();
+        verify(client3).stop();
+    }
+
+    @Test
+    public void testHealthCheckMonitor_Shutdown() {
+        monitor.start();
+        monitor.shutdown();
+
+        // at least one "stop" should have been called
+        verify(server1).stop();
+    }
+
+    @Test
+    public void testHealthCheckMonitor_IsAlive() {
+        assertFalse(monitor.isAlive());
+
+        monitor.start();
+        assertTrue(monitor.isAlive());
+    }
+
+    @Test
+    public void testHealthCheckMonitor_GetServers_GetClients() {
+        monitor.start();
+        assertEquals(servers, monitor.getServers());
+        assertEquals(clients, monitor.getClients());
+    }
+
+    @Test
+    public void testHealthCheckMonitor_GetHttpBody() {
+        Response response = mock(Response.class);
+        when(response.readEntity(String.class)).thenReturn(RPT_MSG);
+        assertEquals(RPT_MSG, monitor.getHttpBody(response, client1));
+
+        // readEntity() throws an exception
+        when(response.readEntity(String.class)).thenThrow(new RuntimeException(EXPECTED));
+        assertEquals(null, monitor.getHttpBody(response, client1));
+    }
+
+    @Test
+    public void testHealthCheckMonitor_StartServer() {
+        monitor.startServer(server1);
+        verify(server1).start();
+
+        // force start() to throw an exception - monitor should still work
+        when(server1.start()).thenThrow(new RuntimeException(EXPECTED));
+        monitor.startServer(server1);
+    }
+
+    @Test
+    public void testHealthCheckMonitor_ToString() {
+        assertTrue(monitor.toString().startsWith("HealthCheckMonitor ["));
+    }
+
+    @Test
+    public void testHealthCheckMonitor_GetEngineManager() {
+        assertNotNull(new HealthCheckMonitor().getEngineManager());
+    }
+
+    @Test
+    public void testHealthCheckMonitor_GetServerFactory() {
+        assertNotNull(new HealthCheckMonitor().getServerFactory());
+    }
+
+    @Test
+    public void testHealthCheckMonitor_GetClientFactory() {
+        assertNotNull(new HealthCheckMonitor().getClientFactory());
+    }
+
+    /**
+     * Monitor with overrides.
+     */
+    private class HealthCheckMonitorImpl extends HealthCheckMonitor {
+
+        @Override
+        protected PolicyEngine getEngineManager() {
+            return engineMgr;
+        }
+
+        @Override
+        protected HttpServletServerFactory getServerFactory() {
+            return servletFactory;
+        }
+
+        @Override
+        protected HttpClientFactory getClientFactory() {
+            return clientFactory;
+        }
+
+        @Override
+        protected Properties getPersistentProperties(String propertyName) {
+            return properties;
+        }
+
+    }
 }