blob: 7610015b601b4ae18940f10b86220a9b4770e041 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The SF licenses this file
* to you 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. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.apache.felix.hc.core.impl.monitor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import org.apache.felix.hc.api.HealthCheck;
import org.apache.felix.hc.api.Result;
import org.apache.felix.hc.api.condition.Healthy;
import org.apache.felix.hc.api.condition.Unhealthy;
import org.apache.felix.hc.api.execution.HealthCheckExecutor;
import org.apache.felix.hc.api.execution.HealthCheckMetadata;
import org.apache.felix.hc.api.execution.HealthCheckSelector;
import org.apache.felix.hc.core.impl.executor.ExecutionResult;
import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
import org.apache.felix.hc.core.impl.scheduling.AsyncIntervalJob;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
public class HealthCheckMonitorTest {
private static final String TEST_TAG = "test-tag";
@InjectMocks
private HealthCheckMonitor healthCheckMonitor = new HealthCheckMonitor();
@Mock
private BundleContext bundleContext;
@Mock
private ComponentContext componentContext;
@Mock
private EventAdmin eventAdmin;
@Mock
private HealthCheckMonitor.Config config;
@Mock
private HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
@Mock
private HealthCheckExecutor healthCheckExecutor;
@Mock
private HealthCheckMetadata healthCheckMetadata;
@Mock
private ServiceReference<HealthCheck> healthCheckServiceRef;
@Captor
private ArgumentCaptor<Event> postedEventsCaptor1;
@Captor
private ArgumentCaptor<Event> postedEventsCaptor2;
@Mock
private ServiceRegistration<? extends Healthy> healthyRegistration;
@Mock
private ServiceRegistration<Unhealthy> unhealthyRegistration;
@Before
public void before() throws ReflectiveOperationException {
MockitoAnnotations.initMocks(this);
for (Method m : HealthCheckMonitor.Config.class.getDeclaredMethods()) {
when(m.invoke(config)).thenReturn(m.getDefaultValue());
}
when(config.intervalInSec()).thenReturn(1000L);
when(config.tags()).thenReturn(new String[] { TEST_TAG });
when(healthCheckMetadata.getServiceReference()).thenReturn(healthCheckServiceRef);
Dictionary<String,Object> componentProps = new Hashtable<>();
componentProps.put(ComponentConstants.COMPONENT_ID, 7L);
when(componentContext.getProperties()).thenReturn(componentProps);
}
@Test
public void testActivate() throws InvalidSyntaxException {
healthCheckMonitor.activate(bundleContext, config, componentContext);
assertTrue(healthCheckMonitor.monitorJob instanceof AsyncIntervalJob);
verify(healthCheckExecutorThreadPool).scheduleAtFixedRate(any(Runnable.class), eq(1000L));
assertEquals("[HealthCheckMonitor tags=[test-tag]/names=[], intervalInSec=1000/cron=]", healthCheckMonitor.toString());
assertEquals(1, healthCheckMonitor.healthStates.size());
assertEquals("[HealthState tagOrName=test-tag, isTag=true, status=null, isHealthy=false, statusChanged=false]", healthCheckMonitor.healthStates.get(0).toString());
healthCheckMonitor.deactivate();
assertEquals(0, healthCheckMonitor.healthStates.size());
}
@Test
public void testRunRegisterMarkerServices() throws InvalidSyntaxException {
when(config.registerHealthyMarkerService()).thenReturn(true);
when(config.registerUnhealthyMarkerService()).thenReturn(true);
healthCheckMonitor.activate(bundleContext, config, componentContext);
resetMarkerServicesContext();
setHcResult(Result.Status.OK);
healthCheckMonitor.run();
verify(healthCheckExecutor).execute(HealthCheckSelector.tags(TEST_TAG));
verify(bundleContext).registerService(eq(Healthy.class), eq(HealthCheckMonitor.MARKER_SERVICE_HEALTHY), any());
verify(bundleContext, never()).registerService(eq(Unhealthy.class), eq(HealthCheckMonitor.MARKER_SERVICE_UNHEALTHY), any());
verifyNoInteractions(healthyRegistration, unhealthyRegistration);
resetMarkerServicesContext();
healthCheckMonitor.run();
// no status change, no interaction
verifyNoInteractions(bundleContext, healthyRegistration, unhealthyRegistration);
// change, unhealthy should be registered
resetMarkerServicesContext();
setHcResult(Result.Status.TEMPORARILY_UNAVAILABLE);
healthCheckMonitor.run();
verify(bundleContext, never()).registerService(eq(Healthy.class), eq(HealthCheckMonitor.MARKER_SERVICE_HEALTHY), any());
verify(bundleContext).registerService(eq(Unhealthy.class), eq(HealthCheckMonitor.MARKER_SERVICE_UNHEALTHY), any());
verify(healthyRegistration).unregister();
verifyNoInteractions(unhealthyRegistration);
// change, health should be registered
resetMarkerServicesContext();
setHcResult(Result.Status.WARN); // WARN is healthy by default config
healthCheckMonitor.run();
verify(bundleContext).registerService(eq(Healthy.class), eq(HealthCheckMonitor.MARKER_SERVICE_HEALTHY), any());
verify(bundleContext, never()).registerService(eq(Unhealthy.class), eq(HealthCheckMonitor.MARKER_SERVICE_UNHEALTHY), any());
verify(unhealthyRegistration).unregister();
verifyNoInteractions(healthyRegistration);
}
private void resetMarkerServicesContext() {
reset(bundleContext, healthyRegistration, unhealthyRegistration);
when(bundleContext.registerService(eq(Healthy.class), eq(HealthCheckMonitor.MARKER_SERVICE_HEALTHY), any())).thenReturn((ServiceRegistration<Healthy>) healthyRegistration);
when(bundleContext.registerService(eq(Unhealthy.class), eq(HealthCheckMonitor.MARKER_SERVICE_UNHEALTHY), any())).thenReturn(unhealthyRegistration);
}
@Test
public void testRunSendEvents() throws InvalidSyntaxException {
when(config.sendEvents()).thenReturn(true);
when(healthCheckServiceRef.getProperty(ComponentConstants.COMPONENT_NAME)).thenReturn("org.apache.felix.TestHealthCheck");
healthCheckMonitor.activate(bundleContext, config, componentContext);
setHcResult(Result.Status.OK);
healthCheckMonitor.run();
verify(healthCheckExecutor).execute(HealthCheckSelector.tags(TEST_TAG));
verify(eventAdmin, times(2)).postEvent(postedEventsCaptor1.capture());
List<Event> postedEvents = postedEventsCaptor1.getAllValues();
assertEquals(2, postedEvents.size());
assertEquals("org/apache/felix/healthchange/tag/test-tag", postedEvents.get(0).getTopic());
assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthCheckMonitor.EVENT_PROP_STATUS));
assertEquals("org/apache/felix/healthchange/component/org/apache/felix/TestHealthCheck", postedEvents.get(1).getTopic());
reset(eventAdmin);
// without status change
healthCheckMonitor.run();
// no event
verifyNoInteractions(eventAdmin);
setHcResult(Result.Status.CRITICAL);
reset(eventAdmin);
// with status change
healthCheckMonitor.run();
verify(eventAdmin, times(2)).postEvent(postedEventsCaptor2.capture());
postedEvents = postedEventsCaptor2.getAllValues();
assertEquals(2, postedEvents.size());
assertEquals("org/apache/felix/healthchange/tag/test-tag", postedEvents.get(0).getTopic());
assertEquals(Result.Status.CRITICAL, postedEvents.get(0).getProperty(HealthCheckMonitor.EVENT_PROP_STATUS));
assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthCheckMonitor.EVENT_PROP_PREVIOUS_STATUS));
assertEquals("org/apache/felix/healthchange/component/org/apache/felix/TestHealthCheck", postedEvents.get(1).getTopic());
reset(eventAdmin);
// without status change
healthCheckMonitor.run();
// no event
verifyNoInteractions(eventAdmin);
}
private void setHcResult(Result.Status status) {
when(healthCheckExecutor.execute(HealthCheckSelector.tags(TEST_TAG)))
.thenReturn(Arrays.asList(new ExecutionResult(healthCheckMetadata, new Result(status, status.toString()), 1)));
}
}