summaryrefslogtreecommitdiffstats
path: root/container-core/src/test/java
diff options
context:
space:
mode:
Diffstat (limited to 'container-core/src/test/java')
-rw-r--r--container-core/src/test/java/ai/vespa/cloud/SystemInfoTest.java49
-rw-r--r--container-core/src/test/java/com/yahoo/container/core/config/BundleManagerTest.java106
-rw-r--r--container-core/src/test/java/com/yahoo/container/core/config/TestBundle.java102
-rw-r--r--container-core/src/test/java/com/yahoo/container/core/config/TestBundleInstaller.java20
-rw-r--r--container-core/src/test/java/com/yahoo/container/core/config/TestOsgi.java57
-rw-r--r--container-core/src/test/java/com/yahoo/container/handler/VipStatusTestCase.java114
-rw-r--r--container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java143
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/ThreadedHttpRequestHandlerTest.java57
-rw-r--r--container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java3
9 files changed, 623 insertions, 28 deletions
diff --git a/container-core/src/test/java/ai/vespa/cloud/SystemInfoTest.java b/container-core/src/test/java/ai/vespa/cloud/SystemInfoTest.java
new file mode 100644
index 00000000000..6bc8b395e00
--- /dev/null
+++ b/container-core/src/test/java/ai/vespa/cloud/SystemInfoTest.java
@@ -0,0 +1,49 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.cloud;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author bratseth
+ */
+public class SystemInfoTest {
+
+ @Test
+ public void testSystemInfo() {
+ Zone zone = new Zone(Environment.dev, "us-west-1");
+ SystemInfo info = new SystemInfo(zone);
+ assertEquals(zone, info.zone());
+ }
+
+ @Test
+ public void testZone() {
+ Zone zone = Zone.from("dev.us-west-1");
+ zone = Zone.from(zone.toString());
+ assertEquals(Environment.dev, zone.environment());
+ assertEquals("us-west-1", zone.region());
+ Zone sameZone = Zone.from("dev.us-west-1");
+ assertEquals(sameZone.hashCode(), zone.hashCode());
+ assertEquals(sameZone, zone);
+
+ try {
+ Zone.from("invalid");
+ fail("Expected exception");
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("A zone string must be on the form [environment].[region], but was 'invalid'",
+ e.getMessage());
+ }
+
+ try {
+ Zone.from("invalid.us-west-1");
+ fail("Expected exception");
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("Invalid zone 'invalid.us-west-1': No environment named 'invalid'", e.getMessage());
+ }
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/container/core/config/BundleManagerTest.java b/container-core/src/test/java/com/yahoo/container/core/config/BundleManagerTest.java
new file mode 100644
index 00000000000..414e6b05128
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/container/core/config/BundleManagerTest.java
@@ -0,0 +1,106 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.core.config;
+
+import com.yahoo.config.FileReference;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author gjoranv
+ */
+public class BundleManagerTest {
+
+ private static final FileReference BUNDLE_1_REF = new FileReference("bundle-1");
+ private static final Bundle BUNDLE_1 = new TestBundle(BUNDLE_1_REF.value());
+ private static final FileReference BUNDLE_2_REF = new FileReference("bundle-2");
+ private static final Bundle BUNDLE_2 = new TestBundle(BUNDLE_2_REF.value());
+
+ private BundleManager bundleLoader;
+ private TestOsgi osgi;
+
+ @Before
+ public void setup() {
+ osgi = new TestOsgi(testBundles());
+ var bundleInstaller = new TestBundleInstaller();
+ bundleLoader = new BundleManager(osgi);
+ bundleLoader.useCustomBundleInstaller(bundleInstaller);
+ }
+
+ @Test
+ public void bundles_are_installed_and_started() {
+ bundleLoader.use(List.of(BUNDLE_1_REF));
+ assertEquals(1, osgi.getInstalledBundles().size());
+
+ // The bundle is installed and started
+ TestBundle installedBundle = (TestBundle)osgi.getInstalledBundles().get(0);
+ assertEquals(BUNDLE_1.getSymbolicName(), installedBundle.getSymbolicName());
+ assertTrue(installedBundle.started);
+
+ // The file reference is active
+ assertEquals(1, bundleLoader.getActiveFileReferences().size());
+ assertEquals(BUNDLE_1_REF, bundleLoader.getActiveFileReferences().get(0));
+ }
+
+ @Test
+ public void new_bundle_can_be_installed_in_reconfig() {
+ bundleLoader.use(List.of(BUNDLE_1_REF));
+ Set<Bundle> obsoleteBundles = bundleLoader.use(List.of(BUNDLE_1_REF, BUNDLE_2_REF));
+
+ // No bundles are obsolete
+ assertTrue(obsoleteBundles.isEmpty());
+
+ // Both bundles are installed
+ assertEquals(2, osgi.getInstalledBundles().size());
+ assertEquals(BUNDLE_1.getSymbolicName(), osgi.getInstalledBundles().get(0).getSymbolicName());
+ assertEquals(BUNDLE_2.getSymbolicName(), osgi.getInstalledBundles().get(1).getSymbolicName());
+
+ // Both bundles are current
+ assertEquals(2, osgi.getCurrentBundles().size());
+ assertEquals(BUNDLE_1.getSymbolicName(), osgi.getCurrentBundles().get(0).getSymbolicName());
+ assertEquals(BUNDLE_2.getSymbolicName(), osgi.getCurrentBundles().get(1).getSymbolicName());
+
+
+ // Both file references are active
+ assertEquals(2, bundleLoader.getActiveFileReferences().size());
+ assertEquals(BUNDLE_1_REF, bundleLoader.getActiveFileReferences().get(0));
+ assertEquals(BUNDLE_2_REF, bundleLoader.getActiveFileReferences().get(1));
+ }
+
+ @Test
+ public void unused_bundle_is_marked_obsolete_after_reconfig() {
+ bundleLoader.use(List.of(BUNDLE_1_REF));
+ Set<Bundle> obsoleteBundles = bundleLoader.use(List.of(BUNDLE_2_REF));
+
+ // The returned set of obsolete bundles contains bundle-1
+ assertEquals(1, obsoleteBundles.size());
+ assertEquals(BUNDLE_1.getSymbolicName(), obsoleteBundles.iterator().next().getSymbolicName());
+
+ // Both bundles are installed
+ assertEquals(2, osgi.getInstalledBundles().size());
+ assertEquals(BUNDLE_1.getSymbolicName(), osgi.getInstalledBundles().get(0).getSymbolicName());
+ assertEquals(BUNDLE_2.getSymbolicName(), osgi.getInstalledBundles().get(1).getSymbolicName());
+
+ // Only bundle-2 is current
+ assertEquals(1, osgi.getCurrentBundles().size());
+ assertEquals(BUNDLE_2.getSymbolicName(), osgi.getCurrentBundles().get(0).getSymbolicName());
+
+ // Only the bundle-2 file reference is active
+ assertEquals(1, bundleLoader.getActiveFileReferences().size());
+ assertEquals(BUNDLE_2_REF, bundleLoader.getActiveFileReferences().get(0));
+ }
+
+
+ private static Map<String, Bundle> testBundles() {
+ return Map.of(BUNDLE_1_REF.value(), BUNDLE_1,
+ BUNDLE_2_REF.value(), BUNDLE_2);
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/container/core/config/TestBundle.java b/container-core/src/test/java/com/yahoo/container/core/config/TestBundle.java
new file mode 100644
index 00000000000..421f4302c27
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/container/core/config/TestBundle.java
@@ -0,0 +1,102 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.core.config;
+
+import com.yahoo.container.bundle.MockBundle;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+
+import java.util.List;
+
+/**
+ * @author gjoranv
+ */
+class TestBundle extends MockBundle {
+
+ private static final BundleRevision revision = new TestBundleRevision();
+
+ private final String symbolicName;
+
+ boolean started = false;
+
+ TestBundle(String symbolicName) {
+ this.symbolicName = symbolicName;
+ }
+
+ @Override
+ public void start() {
+ started = true;
+ }
+
+ @Override
+ public String getSymbolicName() {
+ return symbolicName;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T adapt(Class<T> type) {
+ if (type.equals(BundleRevision.class)) {
+ return (T) revision;
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+
+ static class TestBundleRevision implements BundleRevision {
+
+ // Ensure this is not seen as a fragment bundle.
+ @Override
+ public int getTypes() {
+ return 0;
+ }
+
+ @Override
+ public String getSymbolicName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Version getVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<BundleCapability> getDeclaredCapabilities(String namespace) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<BundleRequirement> getDeclaredRequirements(String namespace) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public BundleWiring getWiring() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Capability> getCapabilities(String namespace) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Requirement> getRequirements(String namespace) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle getBundle() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/container/core/config/TestBundleInstaller.java b/container-core/src/test/java/com/yahoo/container/core/config/TestBundleInstaller.java
new file mode 100644
index 00000000000..43a5268eabf
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/container/core/config/TestBundleInstaller.java
@@ -0,0 +1,20 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.core.config;
+
+import com.yahoo.config.FileReference;
+import com.yahoo.osgi.Osgi;
+import org.osgi.framework.Bundle;
+
+import java.util.List;
+
+/**
+ * @author gjoranv
+ */
+class TestBundleInstaller implements BundleInstaller {
+
+ @Override
+ public List<Bundle> installBundles(FileReference reference, Osgi osgi) {
+ return osgi.install(reference.value());
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/container/core/config/TestOsgi.java b/container-core/src/test/java/com/yahoo/container/core/config/TestOsgi.java
new file mode 100644
index 00000000000..54a3159239c
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/container/core/config/TestOsgi.java
@@ -0,0 +1,57 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.core.config;
+
+import com.yahoo.osgi.MockOsgi;
+import org.osgi.framework.Bundle;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author gjoranv
+ */
+class TestOsgi extends MockOsgi {
+
+ private final Map<String, Bundle> availableBundles;
+
+ private final List<Bundle> installedBundles = new ArrayList<>();
+ private final List<Bundle> allowedDuplicates = new ArrayList<>();
+
+ TestOsgi(Map<String, Bundle> availableBundles) {
+ this.availableBundles = availableBundles;
+ }
+
+ @Override
+ public List<Bundle> install(String fileReferenceValue) {
+ if (! availableBundles.containsKey(fileReferenceValue))
+ throw new IllegalArgumentException("No such bundle: " + fileReferenceValue);
+
+ Bundle bundle = availableBundles.get(fileReferenceValue);
+ installedBundles.add(bundle);
+ return List.of(bundle);
+ }
+
+ @Override
+ public Bundle[] getBundles() {
+ return installedBundles.toArray(new Bundle[0]);
+ }
+
+ public List<Bundle> getInstalledBundles() {
+ return installedBundles;
+ }
+
+ @Override
+ public List<Bundle> getCurrentBundles() {
+ var currentBundles = new ArrayList<>(installedBundles);
+ currentBundles.removeAll(allowedDuplicates);
+ return currentBundles;
+ }
+
+ @Override
+ public void allowDuplicateBundles(Collection<Bundle> bundles) {
+ allowedDuplicates.addAll(bundles);
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/container/handler/VipStatusTestCase.java b/container-core/src/test/java/com/yahoo/container/handler/VipStatusTestCase.java
index 52679c15957..e13debcddda 100644
--- a/container-core/src/test/java/com/yahoo/container/handler/VipStatusTestCase.java
+++ b/container-core/src/test/java/com/yahoo/container/handler/VipStatusTestCase.java
@@ -4,6 +4,9 @@ package com.yahoo.container.handler;
import static org.junit.Assert.*;
import com.yahoo.container.QrSearchersConfig;
+import com.yahoo.container.core.VipStatusConfig;
+import com.yahoo.container.jdisc.state.StateMonitor;
+import com.yahoo.jdisc.core.SystemTimer;
import org.junit.Test;
/**
@@ -13,44 +16,101 @@ import org.junit.Test;
*/
public class VipStatusTestCase {
- @Test
- public void testVipStatusWorksWithClusters() {
+ private static QrSearchersConfig getSearchersConfig(String[] clusters) {
var b = new QrSearchersConfig.Builder();
- var searchClusterB = new QrSearchersConfig.Searchcluster.Builder();
- searchClusterB.name("cluster1");
- searchClusterB.name("cluster2");
- searchClusterB.name("cluster3");
- b.searchcluster(searchClusterB);
- VipStatus v = new VipStatus(b.build());
+ if (clusters.length > 0) {
+ var searchClusterB = new QrSearchersConfig.Searchcluster.Builder();
+ for (String cluster : clusters) {
+ searchClusterB.name(cluster);
+ }
+ b.searchcluster(searchClusterB);
+ }
+ return b.build();
+ }
+
+ private static VipStatus getVipStatus(String[] clusters, StateMonitor.Status startState, boolean initiallyInRotation) {
+ return new VipStatus(getSearchersConfig(clusters),
+ new VipStatusConfig.Builder().initiallyInRotation(initiallyInRotation).build(),
+ new ClustersStatus(),
+ new StateMonitor(1000, startState, new SystemTimer(), runnable -> {
+ Thread thread = new Thread(runnable, "StateMonitor");
+ thread.setDaemon(true);
+ return thread;
+ }));
+ }
- String cluster1 = "cluster1";
- String cluster2 = "cluster2";
- String cluster3 = "cluster3";
+ private static void remove(String[] clusters, VipStatus v) {
+ for (String s : clusters) {
+ v.removeFromRotation(s);
+ }
+ }
+
+ private static void add(String[] clusters, VipStatus v) {
+ for (String s : clusters) {
+ v.addToRotation(s);
+ }
+ }
+ private static void verifyUpOrDown(String[] clusters, StateMonitor.Status status) {
+ VipStatus v = getVipStatus(clusters, status, true);
+ remove(clusters, v);
// initial state
assertFalse(v.isInRotation());
+ v.addToRotation(clusters[0]);
+ assertFalse(v.isInRotation());
+ v.addToRotation(clusters[1]);
+ assertFalse(v.isInRotation());
+ v.addToRotation(clusters[2]);
+ assertTrue(v.isInRotation());
+ }
+
+ @Test
+ public void testInitializingOrDownRequireAllUp() {
+ String[] clusters = {"cluster1", "cluster2", "cluster3"};
+ verifyUpOrDown(clusters, StateMonitor.Status.initializing);
+ verifyUpOrDown(clusters, StateMonitor.Status.down);
+ }
+
+ @Test
+ public void testUpRequireAllDown() {
+ String[] clusters = {"cluster1", "cluster2", "cluster3"};
- // one cluster becomes up
- v.addToRotation(cluster1);
+ VipStatus v = getVipStatus(clusters, StateMonitor.Status.initializing, true);
+ assertFalse(v.isInRotation());
+ add(clusters, v);
assertTrue(v.isInRotation());
- // all clusters down
- v.removeFromRotation(cluster1);
- v.removeFromRotation(cluster2);
- v.removeFromRotation(cluster3);
+ v.removeFromRotation(clusters[0]);
+ assertTrue(v.isInRotation());
+ v.removeFromRotation(clusters[1]);
+ assertTrue(v.isInRotation());
+ v.removeFromRotation(clusters[2]);
+ assertFalse(v.isInRotation()); // All down
+ v.addToRotation(clusters[1]);
assertFalse(v.isInRotation());
- // some clusters down
- v.addToRotation(cluster2);
+ v.addToRotation(clusters[0]);
+ v.addToRotation(clusters[2]);
+ assertTrue(v.isInRotation()); // All up
+ v.removeFromRotation(clusters[0]);
+ v.removeFromRotation(clusters[2]);
assertTrue(v.isInRotation());
- // all clusters up
- v.addToRotation(cluster1);
- v.addToRotation(cluster3);
+ v.addToRotation(clusters[0]);
+ v.addToRotation(clusters[2]);
assertTrue(v.isInRotation());
- // and down again
- v.removeFromRotation(cluster1);
- v.removeFromRotation(cluster2);
- v.removeFromRotation(cluster3);
+ }
+
+ @Test
+ public void testNoClustersConfiguringInitiallyInRotationFalse() {
+ String[] clusters = {};
+ VipStatus v = getVipStatus(clusters, StateMonitor.Status.initializing, false);
assertFalse(v.isInRotation());
}
-}
+ @Test
+ public void testNoClustersConfiguringInitiallyInRotationTrue() {
+ String[] clusters = {};
+ VipStatus v = getVipStatus(clusters, StateMonitor.Status.initializing, true);
+ assertTrue(v.isInRotation());
+ }
+
+} \ No newline at end of file
diff --git a/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java b/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java
new file mode 100644
index 00000000000..b57814e50aa
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/container/handler/metrics/MetricsV2HandlerTest.java
@@ -0,0 +1,143 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.handler.metrics;
+
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static com.yahoo.container.handler.metrics.MetricsV2Handler.V2_PATH;
+import static com.yahoo.container.handler.metrics.MetricsV2Handler.VALUES_PATH;
+import static com.yahoo.container.handler.metrics.MetricsV2Handler.consumerQuery;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author gjoranv
+ */
+public class MetricsV2HandlerTest {
+
+ private static final String URI_BASE = "http://localhost";
+
+ private static final String V2_URI = URI_BASE + V2_PATH;
+ private static final String VALUES_URI = URI_BASE + VALUES_PATH;
+
+ // Mock applicationmetrics api
+ private static final String MOCK_METRICS_PATH = "/node0";
+
+ private static final String TEST_FILE = "application-metrics.json";
+ private static final String RESPONSE = getFileContents(TEST_FILE);
+ private static final String CPU_METRIC = "cpu.util";
+ private static final String REPLACED_CPU_METRIC = "replaced_cpu_util";
+ private static final String CUSTOM_CONSUMER = "custom-consumer";
+
+ private static RequestHandlerTestDriver testDriver;
+
+ @Rule
+ public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort());
+
+ @Before
+ public void setup() {
+ setupWireMock();
+ var handler = new MetricsV2Handler(Executors.newSingleThreadExecutor(),
+ new MetricsProxyApiConfig.Builder()
+ .metricsPort(wireMockRule.port())
+ .metricsApiPath(MOCK_METRICS_PATH)
+ .build());
+ testDriver = new RequestHandlerTestDriver(handler);
+ }
+
+ private void setupWireMock() {
+ wireMockRule.stubFor(get(urlPathEqualTo(MOCK_METRICS_PATH))
+ .willReturn(aResponse().withBody(RESPONSE)));
+
+ // Add a slightly different response for a custom consumer.
+ String myConsumerResponse = RESPONSE.replaceAll(CPU_METRIC, REPLACED_CPU_METRIC);
+ wireMockRule.stubFor(get(urlPathEqualTo(MOCK_METRICS_PATH))
+ .withQueryParam("consumer", equalTo(CUSTOM_CONSUMER))
+ .willReturn(aResponse().withBody(myConsumerResponse)));
+ }
+
+ @Test
+ public void v2_response_contains_values_uri() throws Exception {
+ String response = testDriver.sendRequest(V2_URI).readAll();
+ JSONObject root = new JSONObject(response);
+ assertTrue(root.has("resources"));
+
+ JSONArray resources = root.getJSONArray("resources");
+ assertEquals(1, resources.length());
+
+ JSONObject valuesUri = resources.getJSONObject(0);
+ assertEquals(VALUES_URI, valuesUri.getString("url"));
+ }
+
+ @Ignore
+ @Test
+ public void visually_inspect_values_response() throws Exception {
+ JSONObject responseJson = getResponseAsJson(null);
+ System.out.println(responseJson.toString(4));
+ }
+
+ @Test
+ public void invalid_path_yields_error_response() throws Exception {
+ String response = testDriver.sendRequest(V2_URI + "/invalid").readAll();
+ JSONObject root = new JSONObject(response);
+ assertTrue(root.has("error"));
+ assertTrue(root.getString("error" ).startsWith("No content"));
+ }
+
+ @Test
+ public void values_response_is_equal_to_test_file() {
+ String response = testDriver.sendRequest(VALUES_URI).readAll();
+ assertEquals(RESPONSE, response);
+ }
+
+ @Test
+ public void consumer_is_propagated_to_metrics_proxy_api() throws JSONException {
+ JSONObject responseJson = getResponseAsJson(CUSTOM_CONSUMER);
+
+ JSONObject firstNodeMetricsValues =
+ responseJson.getJSONArray("nodes").getJSONObject(0)
+ .getJSONObject("node")
+ .getJSONArray("metrics").getJSONObject(0)
+ .getJSONObject("values");
+
+ assertTrue(firstNodeMetricsValues.has(REPLACED_CPU_METRIC));
+ }
+
+ private JSONObject getResponseAsJson(String consumer) {
+ String response = testDriver.sendRequest(VALUES_URI + consumerQuery(consumer)).readAll();
+ try {
+ return new JSONObject(response);
+ } catch (JSONException e) {
+ fail("Failed to create json object: " + e.getMessage());
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String getFileContents(String filename) {
+ InputStream in = MetricsV2HandlerTest.class.getClassLoader().getResourceAsStream(filename);
+ if (in == null) {
+ throw new RuntimeException("File not found: " + filename);
+ }
+ return new BufferedReader(new InputStreamReader(in)).lines().collect(Collectors.joining("\n"));
+ }
+
+}
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/ThreadedHttpRequestHandlerTest.java b/container-core/src/test/java/com/yahoo/container/jdisc/ThreadedHttpRequestHandlerTest.java
new file mode 100644
index 00000000000..07dba21e5b6
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/ThreadedHttpRequestHandlerTest.java
@@ -0,0 +1,57 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.jdisc;
+
+import com.yahoo.jdisc.Metric;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author bjorncs
+ */
+public class ThreadedHttpRequestHandlerTest {
+
+ @Test
+ public void unhandled_exceptions_metric_is_incremented_if_subclassed_handler_throws_exception() {
+ MetricMock metricMock = new MetricMock();
+ ThreadedHttpRequestHandlerThrowingException handler = new ThreadedHttpRequestHandlerThrowingException(metricMock);
+ RequestHandlerTestDriver driver = new RequestHandlerTestDriver(handler);
+
+ driver.sendRequest("http://localhost/myhandler");
+ String expectedMetricName = "jdisc.http.handler.unhandled_exceptions";
+ assertThat(metricMock.addInvocations)
+ .containsKey(expectedMetricName);
+ assertThat(metricMock.addInvocations.get(expectedMetricName).dimensions)
+ .containsEntry("exception", "DummyException");
+ }
+
+ private static class MetricMock implements Metric {
+ final ConcurrentHashMap<String, SimpleMetricContext> addInvocations = new ConcurrentHashMap<>();
+
+ @Override public void add(String key, Number val, Context ctx) {
+ addInvocations.put(key, (SimpleMetricContext)ctx);
+ }
+ @Override public void set(String key, Number val, Context ctx) {}
+ @Override public Context createContext(Map<String, ?> properties) { return new SimpleMetricContext(properties); }
+ }
+
+ private static class SimpleMetricContext implements Metric.Context {
+ final Map<String, String> dimensions;
+
+ @SuppressWarnings("unchecked")
+ SimpleMetricContext(Map<String, ?> dimensions) { this.dimensions = (Map<String, String>)dimensions; }
+ }
+
+ private static class ThreadedHttpRequestHandlerThrowingException extends ThreadedHttpRequestHandler {
+ ThreadedHttpRequestHandlerThrowingException(Metric metric) {
+ super(Executors.newSingleThreadExecutor(), metric);
+ }
+ @Override public HttpResponse handle(HttpRequest request) { throw new DummyException(); }
+ }
+
+ private static class DummyException extends RuntimeException {}
+} \ No newline at end of file
diff --git a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java
index 78541137db5..8a1640e2c0e 100644
--- a/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java
+++ b/container-core/src/test/java/com/yahoo/container/jdisc/state/StateHandlerTestBase.java
@@ -57,7 +57,8 @@ public class StateHandlerTestBase {
HealthMonitorConfig healthMonitorConfig =
new HealthMonitorConfig(
new HealthMonitorConfig.Builder()
- .snapshot_interval(TimeUnit.MILLISECONDS.toSeconds(SNAPSHOT_INTERVAL)));
+ .snapshot_interval(TimeUnit.MILLISECONDS.toSeconds(SNAPSHOT_INTERVAL))
+ .initialStatus("up"));
ThreadFactory threadFactory = ignored -> mock(Thread.class);
this.monitor = new StateMonitor(healthMonitorConfig, timer, threadFactory);
builder.guiceModules().install(new AbstractModule() {