blob: eab74705297f81737bf10e0113f30e0670534c0d [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;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.felix.hc.api.HealthCheck;
import org.apache.felix.hc.api.Result;
import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions;
import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
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.util.HealthCheckFilter;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.component.ComponentContext;
public class CompositeHealthCheckTest {
@Spy
private CompositeHealthCheck compositeHealthCheck = new CompositeHealthCheck();
@Mock
private HealthCheckExecutor healthCheckExecutor;
@Mock
private ComponentContext componentContext;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
compositeHealthCheck.setHealthCheckExecutor(healthCheckExecutor);
compositeHealthCheck.setFilterTags(new String[] {});
compositeHealthCheck.setComponentContext(componentContext);
}
@Test
public void testExecution() {
doReturn((Result) null).when(compositeHealthCheck).checkForRecursion(Matchers.<ServiceReference> any(),
Matchers.<Set<String>> any());
String[] testTags = new String[] { "tag1" };
compositeHealthCheck.setFilterTags(testTags);
List<HealthCheckExecutionResult> executionResults = new LinkedList<HealthCheckExecutionResult>();
executionResults.add(createExecutionResult("Check 1", testTags, new Result(Result.Status.OK, "Good")));
executionResults.add(createExecutionResult("Check 2", testTags, new Result(Result.Status.CRITICAL, "Bad")));
when(healthCheckExecutor.execute(any(HealthCheckSelector.class), any(HealthCheckExecutionOptions.class)))
.thenReturn(executionResults);
Result result = compositeHealthCheck.execute();
verify(healthCheckExecutor, times(1)).execute(argThat(selectorWithTags(testTags)), argThat(andOptions));
assertEquals(Result.Status.CRITICAL, result.getStatus());
}
private ArgumentMatcher<HealthCheckSelector> selectorWithTags(final String[] tags) {
return new ArgumentMatcher<HealthCheckSelector>() {
@Override
public boolean matches(HealthCheckSelector healthCheckSelector) {
return Arrays.equals(healthCheckSelector.tags(), tags) && healthCheckSelector.names() == null;
}
@Override
public String toString() {
return "a select with tags (" + Arrays.toString(tags) + ") and no names.";
}
};
}
private HealthCheckExecutionResult createExecutionResult(String name, String[] testTags, Result result) {
HealthCheckExecutionResult healthCheckExecutionResult = new ExecutionResult(
new HealthCheckMetadata(new DummyHcServiceReference(name, testTags,
new String[0])),
result, 0L);
return healthCheckExecutionResult;
}
@Test
public void testSimpleRecursion() {
// composite check referencing itself
final String[] filterTags = new String[] { "check1" };
final DummyHcServiceReference hcRef = new DummyHcServiceReference("Check 1", new String[] { "check1" }, filterTags);
// test check is hcRef
doReturn(hcRef).when(componentContext).getServiceReference();
compositeHealthCheck.setFilterTags(filterTags);
compositeHealthCheck.setHealthCheckFilter(new HealthCheckFilter(null) {
@Override
public ServiceReference[] getHealthCheckServiceReferences(HealthCheckSelector selector) {
String[] tags = selector.tags();
ServiceReference[] result = new ServiceReference[] {};
if (tags.length > 0) {
if (tags[0].equals(filterTags[0])) {
result = new ServiceReference[] { hcRef };
}
}
return result;
}
});
Result result = compositeHealthCheck.execute();
verify(healthCheckExecutor, never()).execute(any(HealthCheckSelector.class));
assertEquals(Result.Status.HEALTH_CHECK_ERROR, result.getStatus());
}
@Test
public void testCyclicRecursion() {
// three checks, cyclic
final String[] filterTags = new String[] { "check2" };
final DummyHcServiceReference hcRef1 = new DummyHcServiceReference("Check 1", new String[] { "check1" }, filterTags);
final DummyHcServiceReference hcRef2 = new DummyHcServiceReference("Check 2", new String[] { "check2" }, new String[] { "check3" });
final DummyHcServiceReference hcRef3 = new DummyHcServiceReference("Check 3", new String[] { "check3" }, new String[] { "check1" });
// test check is hcRef1
doReturn(hcRef1).when(componentContext).getServiceReference();
compositeHealthCheck.setFilterTags(filterTags);
compositeHealthCheck.setHealthCheckFilter(new HealthCheckFilter(null) {
@Override
public ServiceReference[] getHealthCheckServiceReferences(HealthCheckSelector selector, boolean combineTagsWithOr) {
String[] tags = selector.tags();
ServiceReference[] result = new ServiceReference[] {};
if (tags.length > 0) {
if (tags[0].equals(filterTags[0])) {
result = new ServiceReference[] { hcRef2 };
} else if (tags[0].equals("check3")) {
result = new ServiceReference[] { hcRef3 };
} else if (tags[0].equals("check1")) {
result = new ServiceReference[] { hcRef1 };
}
}
return result;
}
});
Result result = compositeHealthCheck.execute();
verify(healthCheckExecutor, never()).execute(any(HealthCheckSelector.class));
assertEquals(Result.Status.HEALTH_CHECK_ERROR, result.getStatus());
}
@Test
public void testCombineWithOr() {
// composite check referencing itself
final String[] filterTags = new String[] { "check1" };
compositeHealthCheck.setFilterTags(filterTags);
compositeHealthCheck.setCombineTagsWithOr(true);
compositeHealthCheck.execute();
verify(healthCheckExecutor, times(1)).execute(argThat(selectorWithTags(filterTags)), argThat(orOptions));
}
private ArgumentMatcher<HealthCheckExecutionOptions> orOptions = new ArgumentMatcher<HealthCheckExecutionOptions>() {
@Override
public boolean matches(HealthCheckExecutionOptions options) {
return options.isCombineTagsWithOr();
}
@Override
public String toString() {
return "options combining tags with or.";
}
};
private ArgumentMatcher<HealthCheckExecutionOptions> andOptions = new ArgumentMatcher<HealthCheckExecutionOptions>() {
@Override
public boolean matches(HealthCheckExecutionOptions options) {
return !options.isCombineTagsWithOr();
}
@Override
public String toString() {
return "options combining tags with and.";
}
};
private static class DummyHcServiceReference implements ServiceReference {
private long id;
private String name;
private String[] tags;
private String[] filterTags;
public DummyHcServiceReference(String name, String[] tags, String[] filterTags) {
super();
this.id = (long) (Math.random() * Long.MAX_VALUE);
this.name = name;
this.tags = tags;
this.filterTags = filterTags;
}
@Override
public Object getProperty(String key) {
if (Constants.SERVICE_ID.equals(key)) {
return id;
} else if (HealthCheck.NAME.equals(key)) {
return name;
} else if (HealthCheck.MBEAN_NAME.equals(key)) {
return name;
} else if (HealthCheck.TAGS.equals(key)) {
return tags;
} else if (CompositeHealthCheck.PROP_FILTER_TAGS.equals(key)) {
return filterTags;
} else if (ComponentConstants.COMPONENT_NAME.equals(key)) {
return filterTags != null ? CompositeHealthCheck.class.getName() : "some.other.HealthCheck";
} else {
return null;
}
}
@Override
public String[] getPropertyKeys() {
throw new UnsupportedOperationException();
}
@Override
public Bundle getBundle() {
throw new UnsupportedOperationException();
}
@Override
public Bundle[] getUsingBundles() {
throw new UnsupportedOperationException();
}
@Override
public boolean isAssignableTo(Bundle bundle, String className) {
throw new UnsupportedOperationException();
}
@Override
public int compareTo(Object reference) {
throw new UnsupportedOperationException();
}
}
}