2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2022 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2024 Nordix Foundation.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.drools.healthcheck;
24 import static org.junit.jupiter.api.Assertions.assertEquals;
25 import static org.junit.jupiter.api.Assertions.assertFalse;
26 import static org.junit.jupiter.api.Assertions.assertNotEquals;
27 import static org.junit.jupiter.api.Assertions.assertNotNull;
28 import static org.junit.jupiter.api.Assertions.assertTrue;
29 import static org.mockito.ArgumentMatchers.anyString;
30 import static org.mockito.Mockito.doAnswer;
31 import static org.mockito.Mockito.mock;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when;
36 import jakarta.ws.rs.core.Response;
37 import java.net.HttpURLConnection;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.List;
42 import java.util.Properties;
43 import java.util.stream.Collectors;
44 import org.eclipse.jetty.http.HttpStatus;
45 import org.junit.jupiter.api.BeforeEach;
46 import org.junit.jupiter.api.Test;
47 import org.mockito.AdditionalAnswers;
48 import org.onap.policy.common.endpoints.http.client.HttpClient;
49 import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
50 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
51 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
52 import org.onap.policy.drools.controller.DroolsController;
53 import org.onap.policy.drools.healthcheck.HealthCheck.Report;
54 import org.onap.policy.drools.healthcheck.HealthCheck.Reports;
55 import org.onap.policy.drools.system.PolicyController;
56 import org.onap.policy.drools.system.PolicyControllerFactory;
57 import org.onap.policy.drools.system.PolicyEngine;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
61 class HealthCheckManagerTest {
63 private static final Logger logger = LoggerFactory.getLogger(HealthCheckManagerTest.class);
65 protected static List<Report> select(Reports reports, String name, long code, String message) {
66 return reports.getDetails().stream()
67 .filter(report -> name.equals(report.getName()))
68 .filter(report -> report.getCode() == code)
69 .filter(report -> message.equals(report.getMessage()))
70 .collect(Collectors.toList());
73 private static final String RPT_MSG = "report-message";
74 private static final String RPT_NAME = "report-name";
75 private static final String EXPECTED = "expected exception";
77 private static final String CLIENT_NAME1 = "name-a";
78 private static final String CLIENT_URL1 = "url-a";
79 private static final String CLIENT_NAME2 = "name-b";
80 private static final String CLIENT_URL2 = "url-b";
81 private static final String CLIENT_NAME3 = "name-c";
82 private static final String CLIENT_URL3 = "url-c";
84 private Properties properties;
85 private HttpServletServerFactory servletFactory;
86 private HttpServletServer server1;
87 private HttpServletServer server2;
88 private HttpClientFactory clientFactory;
89 private HttpClient client1;
90 private HttpClient client2;
91 private HttpClient client3;
93 private PolicyControllerFactory controllerFactory;
94 private PolicyController controller1;
95 private PolicyController controller2;
96 private DroolsController drools1;
97 private DroolsController drools2;
99 private PolicyEngine engineMgr;
100 private HealthCheckManager monitor;
103 * Initializes the object to be tested.
106 public void setUp() throws Exception {
107 properties = new Properties();
110 List<HttpServletServer> servers = Arrays.asList(server1, server2);
111 List<HttpClient> clients = Arrays.asList(client1, client2, client3);
115 when(servletFactory.build(properties)).thenReturn(servers);
116 when(clientFactory.build(properties)).thenReturn(clients);
120 when(engineMgr.isAlive()).thenReturn(true);
122 monitor = new HealthCheckMonitorImpl();
125 private void whenControllers() {
126 when(drools1.getGroupId()).thenReturn("1");
127 when(drools2.getGroupId()).thenReturn("2");
129 when(drools1.getArtifactId()).thenReturn("1");
130 when(drools2.getArtifactId()).thenReturn("2");
132 when(drools1.getVersion()).thenReturn("1");
133 when(drools2.getVersion()).thenReturn("2");
135 when(drools1.isAlive()).thenReturn(true);
136 when(drools2.isAlive()).thenReturn(true);
138 when(drools1.isBrained()).thenReturn(true);
139 when(drools2.isBrained()).thenReturn(true);
141 when(drools1.getSessionNames()).thenReturn(List.of("session1"));
142 when(drools2.getSessionNames()).thenReturn(List.of("session2"));
144 doAnswer(AdditionalAnswers
145 .answersWithDelay(15000L, invocationOnMock -> Map.of("TIMEOUT", 1)))
146 .when(drools1).factClassNames(anyString());
148 when(drools2.factClassNames(anyString()))
149 .thenReturn(Map.of("java.lang.Integer", 2));
151 when(drools1.factCount("session1")).thenReturn(1L);
152 when(drools2.factCount("session2")).thenReturn(2L);
154 when(controller1.getDrools()).thenReturn(drools1);
155 when(controller2.getDrools()).thenReturn(drools2);
157 when(controller1.getName()).thenReturn("drools1");
158 when(controller2.getName()).thenReturn("drools2");
160 when(controller1.isAlive()).thenReturn(true);
161 when(controller2.isAlive()).thenReturn(true);
164 private void whenClients() {
165 when(client1.getName()).thenReturn(CLIENT_NAME1);
166 when(client1.getBaseUrl()).thenReturn(CLIENT_URL1);
167 when(client2.getName()).thenReturn(CLIENT_NAME2);
168 when(client2.getBaseUrl()).thenReturn(CLIENT_URL2);
169 when(client3.getName()).thenReturn(CLIENT_NAME3);
170 when(client3.getBaseUrl()).thenReturn(CLIENT_URL3);
173 private void mocks() {
174 servletFactory = mock(HttpServletServerFactory.class);
175 server1 = mock(HttpServletServer.class);
176 server2 = mock(HttpServletServer.class);
177 clientFactory = mock(HttpClientFactory.class);
178 client1 = mock(HttpClient.class);
179 client2 = mock(HttpClient.class);
180 client3 = mock(HttpClient.class);
181 controllerFactory = mock(PolicyControllerFactory.class);
182 controller1 = mock(PolicyController.class);
183 controller2 = mock(PolicyController.class);
184 drools1 = mock(DroolsController.class);
185 drools2 = mock(DroolsController.class);
186 engineMgr = mock(PolicyEngine.class);
190 void testHealthcheck() {
191 /* engine not alive */
193 when(engineMgr.isAlive()).thenReturn(false);
194 assertEngineDisabled(monitor.healthCheck());
196 /* engine alive + controllers + clients */
198 when(engineMgr.isAlive()).thenReturn(true);
199 assertEngineEnabled(monitor.healthCheck());
201 monitor.controllers = List.of(controller1, controller2);
205 var reports = monitor.healthCheck();
206 logger.info("{}", reports);
208 assertSummary(reports, 6, false);
209 assertClients(reports);
210 assertControllers(reports);
214 void testControllerHealthcheck() {
215 /* engine not alive */
217 when(engineMgr.isAlive()).thenReturn(false);
218 assertEngineDisabled(monitor.controllerHealthcheck());
222 when(engineMgr.isAlive()).thenReturn(true);
223 assertEngineEnabled(monitor.healthCheck());
225 /* engine + controllers */
227 monitor.controllers = List.of(controller1, controller2);
228 var reports = monitor.healthCheck();
229 logger.info("{}", reports);
231 assertSummary(reports, 3, false);
233 assertReport(reports, true,
234 HealthCheckManager.ENGINE_NAME, "engine",
235 HealthCheckManager.SUCCESS_CODE,
236 HealthCheckManager.ENABLED_MESSAGE);
238 assertControllers(reports);
242 reports = monitor.controllerHealthcheck(controller1);
243 logger.info("{}", reports);
245 reports = monitor.controllerHealthcheck(controller2);
246 logger.info("{}", reports);
248 assertSummary(reports, 2, true);
252 void testClientHealthcheck() {
253 /* engine not alive */
255 when(engineMgr.isAlive()).thenReturn(false);
256 assertEngineDisabled(monitor.clientHealthcheck());
260 when(engineMgr.isAlive()).thenReturn(true);
261 assertEngineEnabled(monitor.clientHealthcheck());
263 /* engine alive + clients */
267 var reports = monitor.clientHealthcheck();
268 logger.info("{}", reports);
270 assertSummary(reports, 4, false);
271 assertClients(reports);
275 reports = monitor.clientHealthcheck(client1);
276 logger.info("{}", reports);
278 assertSummary(reports, 2, true);
282 void reportOnController() {
284 /* controller not alive */
286 when(controller1.isAlive()).thenReturn(false);
288 var reports = monitor.controllerHealthcheck(controller1);
289 assertSummary(reports, 2, false);
290 assertReport(reports, true,
291 HealthCheckManager.ENGINE_NAME, "engine",
292 HealthCheckManager.SUCCESS_CODE,
293 HealthCheckManager.ENABLED_MESSAGE);
294 assertReport(reports, false,
295 controller1.getName(), controller1.getName(),
296 HealthCheckManager.DISABLED_CODE, HealthCheckManager.DISABLED_MESSAGE);
298 /* drools not brained */
300 when(controller1.isAlive()).thenReturn(true);
301 when(drools1.isBrained()).thenReturn(false);
303 reports = monitor.controllerHealthcheck(controller1);
304 logger.info("{}", reports);
306 assertSummary(reports, 2, true);
307 assertReport(reports, true,
308 HealthCheckManager.ENGINE_NAME, "engine",
309 HealthCheckManager.SUCCESS_CODE,
310 HealthCheckManager.ENABLED_MESSAGE);
311 assertReport(reports, true,
312 controller1.getName(), "1:1:1",
313 HealthCheckManager.BRAINLESS_CODE, HealthCheckManager.BRAINLESS_MESSAGE);
315 /* drools not alive */
317 when(drools1.isBrained()).thenReturn(true);
318 when(drools1.isAlive()).thenReturn(false);
320 reports = monitor.controllerHealthcheck(controller1);
321 logger.info("{}", reports);
323 assertSummary(reports, 2, false);
324 assertReport(reports, true,
325 HealthCheckManager.ENGINE_NAME, "engine",
326 HealthCheckManager.SUCCESS_CODE,
327 HealthCheckManager.ENABLED_MESSAGE);
328 assertReport(reports, false,
329 controller1.getName(), "1:1:1",
330 HealthCheckManager.DISABLED_CODE, HealthCheckManager.DISABLED_MESSAGE);
334 when(drools1.isAlive()).thenReturn(true);
336 assertController2(monitor.controllerHealthcheck(controller2));
340 void testReportOnUnknown() {
341 var reports = monitor.summary(monitor.engineHealthcheck(), monitor.futures(List.of(1)));
342 logger.info("{}", reports);
344 assertReport(reports, false,
345 HealthCheckManager.UNKNOWN_ENTITY, "java.lang.Integer",
346 HealthCheckManager.UNKNOWN_ENTITY_CODE, HealthCheckManager.UNKNOWN_ENTITY_MESSAGE);
353 when(server1.start()).thenReturn(true);
354 when(server1.getName()).thenReturn(HealthCheckManager.HEALTHCHECK_SERVER);
355 when(server2.getName()).thenReturn(HealthCheckManager.LIVENESS_SERVER);
356 assertTrue(monitor.start());
358 verify(server1).start();
359 verify(server2, never()).start();
361 assertEquals(server1, monitor.getHealthcheckServer());
362 assertEquals(server2, monitor.getLivenessServer());
364 // healthcheck server start error
366 when(server1.start()).thenThrow(new RuntimeException(EXPECTED));
367 assertFalse(monitor.start());
370 * Generate exception during building.
374 monitor = new HealthCheckMonitorImpl() {
376 protected HttpServletServerFactory getServerFactory() {
377 throw new RuntimeException(EXPECTED);
380 assertFalse(monitor.start());
389 monitor.healthCheckProperties = new Properties();
391 assertEquals(List.of(), monitor.controllers);
393 /* star-controllers */
395 monitor.livenessServer = server1;
396 monitor.healthCheckProperties = new Properties();
397 monitor.healthCheckProperties.setProperty("liveness.controllers", "*");
398 when(server1.start()).thenReturn(true);
401 assertEquals(controllerFactory.inventory(), monitor.controllers);
402 verify(server1).start();
404 /* comma-list-controllers */
406 monitor.controllers = new ArrayList<>();
407 monitor.healthCheckProperties.setProperty("liveness.controllers", "controller1,controller2,controller3");
408 when(controllerFactory.get("controller1")).thenReturn(controller1);
409 when(controllerFactory.get("controller2")).thenReturn(controller2);
410 when(controllerFactory.get("controller3")).thenThrow(new RuntimeException("no controller3"));
412 assertEquals(List.of(controller1, controller2), monitor.controllers);
416 void testShutdown() {
417 monitor.healthcheckServer = server1;
418 monitor.livenessServer = server2;
419 monitor.clients = List.of(client1, client2, client3);
420 when(server1.stop()).thenReturn(true);
421 when(server2.stop()).thenReturn(true);
425 verify(server1).stop();
426 verify(server2).stop();
427 verify(client1).stop();
428 verify(client2).stop();
429 verify(client3).stop();
434 assertFalse(monitor.isAlive());
438 void testToString() {
439 assertTrue(monitor.toString().contains("HealthCheckManager"));
442 private void mockClient1() {
443 // first client is healthy
444 Response resp = mock(Response.class);
445 when(resp.getStatus()).thenReturn(HttpURLConnection.HTTP_OK);
446 when(resp.readEntity(String.class)).thenReturn(RPT_MSG);
447 when(resp.getStatusInfo()).thenReturn(Response.Status.OK);
448 when(client1.get()).thenReturn(resp);
451 private void mockClient2() {
452 // second client throws an exception
453 when(client2.get()).thenThrow(new RuntimeException(EXPECTED));
456 private void mockClient3() {
457 // third client is not healthy
458 Response resp = mock(Response.class);
459 when(resp.getStatus()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND);
460 when(resp.readEntity(String.class)).thenReturn(RPT_NAME);
461 when(resp.getStatusInfo()).thenReturn(Response.Status.NOT_FOUND);
462 when(client3.get()).thenReturn(resp);
465 private void mockClients() {
466 monitor.clients = List.of(client1, client2, client3);
473 private void assertEngineEnabled(Reports summary) {
474 assertEquals(1, summary.getDetails().size());
475 assertReport(summary, true, HealthCheckManager.ENGINE_NAME, "engine",
476 HealthCheckManager.SUCCESS_CODE, HealthCheckManager.ENABLED_MESSAGE);
479 private void assertEngineDisabled(Reports summary) {
480 var report = summary.getDetails().get(0);
481 assertFalse(summary.isHealthy());
482 assertEquals(1, summary.getDetails().size());
483 assertFalse(report.isHealthy());
484 assertEquals(HealthCheckManager.ENGINE_NAME, report.getName());
485 assertEquals(HealthCheckManager.DISABLED_CODE, report.getCode());
486 assertEquals(HealthCheckManager.DISABLED_MESSAGE, report.getMessage());
487 assertNotEquals(0L, report.getStartTime());
488 assertNotEquals(0L, report.getEndTime());
489 assertEquals(report.getEndTime() - report.getStartTime(), report.getElapsedTime());
492 private void assertSummary(Reports reports, int size, boolean healthy) {
493 assertNotNull(reports);
494 assertEquals(size, reports.getDetails().size());
495 assertEquals(healthy, reports.isHealthy());
496 assertNotEquals(0L, reports.getStartTime());
497 assertNotEquals(0L, reports.getEndTime());
498 assertEquals(reports.getEndTime() - reports.getStartTime(), reports.getElapsedTime());
501 private void assertReport(Reports summary,
502 boolean healthy, String name, String url, long successCode, String message) {
503 var report = select(summary, name, successCode, message).get(0);
505 assertEquals(healthy, report.isHealthy());
506 assertEquals(name, report.getName());
507 assertEquals(url, report.getUrl());
508 assertEquals(successCode, report.getCode());
509 assertEquals(message, report.getMessage());
510 assertNotEquals(0L, report.getStartTime());
511 assertNotEquals(0L, report.getEndTime());
512 assertEquals(report.getEndTime() - report.getStartTime(), report.getElapsedTime());
515 private void assertClient1(Reports reports) {
516 assertReport(reports, true,
517 client1.getName(), client1.getBaseUrl(),
519 HttpStatus.getMessage(200));
522 private void assertClient2(Reports reports) {
523 assertReport(reports, false,
524 client2.getName(), client2.getBaseUrl(),
525 HealthCheckManager.UNREACHABLE_CODE,
526 HealthCheckManager.UNREACHABLE_MESSAGE);
529 private void assertClient3(Reports reports) {
530 assertReport(reports, false,
531 client3.getName(), client3.getBaseUrl(),
532 HttpStatus.NOT_FOUND_404,
533 HttpStatus.getMessage(404));
536 private void assertClients(Reports reports) {
537 assertReport(reports, true,
538 HealthCheckManager.ENGINE_NAME, "engine",
539 HealthCheckManager.SUCCESS_CODE,
540 HealthCheckManager.ENABLED_MESSAGE);
542 assertClient1(reports);
543 assertClient2(reports);
544 assertClient3(reports);
547 private void assertController1(Reports reports) {
548 assertReport(reports, false,
549 controller1.getName(), "1:1:1",
550 HealthCheckManager.TIMEOUT_CODE, HealthCheckManager.TIMEOUT_MESSAGE);
553 private void assertController2(Reports reports) {
554 assertReport(reports, true,
555 controller2.getName(), "2:2:2",
556 2, "[session2:{java.lang.Integer=2}]");
559 private void assertControllers(Reports reports) {
560 assertReport(reports, true,
561 HealthCheckManager.ENGINE_NAME, "engine",
562 HealthCheckManager.SUCCESS_CODE,
563 HealthCheckManager.ENABLED_MESSAGE);
565 assertController1(reports);
566 assertController2(reports);
570 * Monitor with overrides.
572 private class HealthCheckMonitorImpl extends HealthCheckManager {
575 protected PolicyEngine getEngineManager() {
580 protected HttpServletServerFactory getServerFactory() {
581 return servletFactory;
585 protected HttpClientFactory getClientFactory() {
586 return clientFactory;
590 protected Properties getPersistentProperties() {
595 protected PolicyControllerFactory getControllerFactory() {
596 return controllerFactory;