// This file is part of OpenTSDB. // Copyright (C) 2013 The OpenTSDB Authors. // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 2.1 of the License, or (at your // option) any later version. This program is distributed in the hope that it // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser // General Public License for more details. You should have received a copy // of the GNU Lesser General Public License along with this program. If not, // see <http://www.gnu.org/licenses/>. package net.opentsdb.core; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.when; import java.lang.reflect.Field; import java.util.HashMap; import net.opentsdb.storage.MockBase; import net.opentsdb.uid.NoSuchUniqueId; import net.opentsdb.uid.NoSuchUniqueName; import net.opentsdb.uid.UniqueId; import net.opentsdb.uid.UniqueId.UniqueIdType; import net.opentsdb.utils.Config; import org.hbase.async.AtomicIncrementRequest; import org.hbase.async.GetRequest; import org.hbase.async.HBaseClient; import org.hbase.async.KeyValue; import org.hbase.async.PutRequest; import org.hbase.async.Scanner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.stumbleupon.async.Deferred; @RunWith(PowerMockRunner.class) @PowerMockIgnore({"javax.management.*", "javax.xml.*", "ch.qos.*", "org.slf4j.*", "com.sum.*", "org.xml.*"}) @PrepareForTest({TSDB.class, Config.class, UniqueId.class, HBaseClient.class, CompactionQueue.class, GetRequest.class, PutRequest.class, KeyValue.class, Scanner.class, AtomicIncrementRequest.class, Const.class}) public final class TestTSDB extends BaseTsdbTest { private MockBase storage; @Before public void beforeLocal() throws Exception { config.setFixDuplicates(true); // TODO(jat): test both ways } @Test public void ctorNullClient() throws Exception { assertNotNull(new TSDB(null, config)); } @Test (expected = NullPointerException.class) public void ctorNullConfig() throws Exception { new TSDB(client, null); } @Test public void ctorOverrideUIDWidths() throws Exception { // assert defaults assertEquals(3, TSDB.metrics_width()); assertEquals(3, TSDB.tagk_width()); assertEquals(3, TSDB.tagv_width()); config.overrideConfig("tsd.storage.uid.width.metric", "1"); config.overrideConfig("tsd.storage.uid.width.tagk", "4"); config.overrideConfig("tsd.storage.uid.width.tagv", "5"); final TSDB tsdb = new TSDB(client, config); assertEquals(1, TSDB.metrics_width()); assertEquals(4, TSDB.tagk_width()); assertEquals(5, TSDB.tagv_width()); assertEquals(1, tsdb.metrics.width()); assertEquals(4, tsdb.tag_names.width()); assertEquals(5, tsdb.tag_values.width()); // IMPORTANT Restore config.overrideConfig("tsd.storage.uid.width.metric", "3"); config.overrideConfig("tsd.storage.uid.width.tagk", "3"); config.overrideConfig("tsd.storage.uid.width.tagv", "3"); new TSDB(client, config); } @Test public void ctorOverrideMaxNumTags() throws Exception { assertEquals(8, Const.MAX_NUM_TAGS()); config.overrideConfig("tsd.storage.max_tags", "12"); new TSDB(client, config); assertEquals(12, Const.MAX_NUM_TAGS()); // IMPORTANT Restore config.overrideConfig("tsd.storage.max_tags", "8"); new TSDB(client, config); } @Test public void ctorOverrideSalt() throws Exception { assertEquals(20, Const.SALT_BUCKETS()); assertEquals(0, Const.SALT_WIDTH()); config.overrideConfig("tsd.storage.salt.buckets", "15"); config.overrideConfig("tsd.storage.salt.width", "2"); new TSDB(client, config); assertEquals(15, Const.SALT_BUCKETS()); assertEquals(2, Const.SALT_WIDTH()); // IMPORTANT Restore config.overrideConfig("tsd.storage.salt.buckets", "20"); config.overrideConfig("tsd.storage.salt.width", "0"); new TSDB(client, config); } @Test public void initializePluginsDefaults() { // no configured plugin path, plugins disabled, no exceptions tsdb.initializePlugins(true); } @Test public void initializePluginsPathSet() throws Exception { Field properties = config.getClass().getDeclaredField("properties"); properties.setAccessible(true); @SuppressWarnings("unchecked") HashMap<String, String> props = (HashMap<String, String>) properties.get(config); props.put("tsd.core.plugin_path", "./"); properties.setAccessible(false); tsdb.initializePlugins(true); } @Test (expected = RuntimeException.class) public void initializePluginsPathBad() throws Exception { Field properties = config.getClass().getDeclaredField("properties"); properties.setAccessible(true); @SuppressWarnings("unchecked") HashMap<String, String> props = (HashMap<String, String>) properties.get(config); props.put("tsd.core.plugin_path", "./doesnotexist"); properties.setAccessible(false); tsdb.initializePlugins(true); } @Test public void initializePluginsSearch() throws Exception { Field properties = config.getClass().getDeclaredField("properties"); properties.setAccessible(true); @SuppressWarnings("unchecked") HashMap<String, String> props = (HashMap<String, String>) properties.get(config); props.put("tsd.core.plugin_path", "./"); props.put("tsd.search.enable", "true"); props.put("tsd.search.plugin", "net.opentsdb.search.DummySearchPlugin"); props.put("tsd.search.DummySearchPlugin.hosts", "localhost"); props.put("tsd.search.DummySearchPlugin.port", "42"); properties.setAccessible(false); tsdb.initializePlugins(true); } @Test (expected = RuntimeException.class) public void initializePluginsSearchNotFound() throws Exception { Field properties = config.getClass().getDeclaredField("properties"); properties.setAccessible(true); @SuppressWarnings("unchecked") HashMap<String, String> props = (HashMap<String, String>) properties.get(config); props.put("tsd.search.enable", "true"); props.put("tsd.search.plugin", "net.opentsdb.search.DoesNotExist"); properties.setAccessible(false); tsdb.initializePlugins(true); } @Test public void initializePluginsSEH() throws Exception { config.overrideConfig("tsd.core.plugin_path", "./"); config.overrideConfig("tsd.core.storage_exception_handler.enable", "true"); config.overrideConfig("tsd.core.storage_exception_handler.plugin", "net.opentsdb.tsd.DummySEHPlugin"); config.overrideConfig( "tsd.core.storage_exception_handler.DummySEHPlugin.hosts", "localhost"); tsdb.initializePlugins(true); assertNotNull(tsdb.getStorageExceptionHandler()); } @Test (expected = RuntimeException.class) public void initializePluginsSEHBadConfig() throws Exception { config.overrideConfig("tsd.core.plugin_path", "./"); config.overrideConfig("tsd.core.storage_exception_handler.enable", "true"); config.overrideConfig("tsd.core.storage_exception_handler.plugin", "net.opentsdb.tsd.DummySEHPlugin"); tsdb.initializePlugins(true); assertNotNull(tsdb.getStorageExceptionHandler()); } @Test (expected = NullPointerException.class) public void initializePluginsSEHEnabledButNoName() throws Exception { config.overrideConfig("tsd.core.plugin_path", "./"); config.overrideConfig("tsd.core.storage_exception_handler.enable", "true"); tsdb.initializePlugins(true); } @Test (expected = IllegalArgumentException.class) public void initializePluginsSEHNotFound() throws Exception { config.overrideConfig("tsd.core.plugin_path", "./"); config.overrideConfig("tsd.core.storage_exception_handler.enable", "true"); config.overrideConfig("tsd.core.storage_exception_handler.plugin", "net.opentsdb.tsd.DoesNotExistSEHPlugin"); config.overrideConfig( "tsd.core.storage_exception_handler.DummySEHPlugin.hosts", "localhost"); tsdb.initializePlugins(true); } @Test public void getClient() { assertNotNull(tsdb.getClient()); } @Test public void getConfig() { assertNotNull(tsdb.getConfig()); } @Test public void getUidNameMetric() throws Exception { setGetUidName(); assertEquals("sys.cpu.0", tsdb.getUidName(UniqueIdType.METRIC, new byte[] { 0, 0, 1 }).joinUninterruptibly()); } @Test public void getUidNameTagk() throws Exception { setGetUidName(); assertEquals("host", tsdb.getUidName(UniqueIdType.TAGK, new byte[] { 0, 0, 1 }).joinUninterruptibly()); } @Test public void getUidNameTagv() throws Exception { setGetUidName(); assertEquals("web01", tsdb.getUidName(UniqueIdType.TAGV, new byte[] { 0, 0, 1 }).joinUninterruptibly()); } @Test (expected = NoSuchUniqueId.class) public void getUidNameMetricNSU() throws Exception { setGetUidName(); tsdb.getUidName(UniqueIdType.METRIC, new byte[] { 0, 0, 2 }) .joinUninterruptibly(); } @Test (expected = NoSuchUniqueId.class) public void getUidNameTagkNSU() throws Exception { setGetUidName(); tsdb.getUidName(UniqueIdType.TAGK, new byte[] { 0, 0, 2 }) .joinUninterruptibly(); } @Test (expected = NoSuchUniqueId.class) public void getUidNameTagvNSU() throws Exception { setGetUidName(); tsdb.getUidName(UniqueIdType.TAGV, new byte[] { 0, 0, 2 }) .joinUninterruptibly(); } @Test (expected = NullPointerException.class) public void getUidNameNullType() throws Exception { setGetUidName(); tsdb.getUidName(null, new byte[] { 0, 0, 2 }).joinUninterruptibly(); } @Test (expected = IllegalArgumentException.class) public void getUidNameNullUID() throws Exception { setGetUidName(); tsdb.getUidName(UniqueIdType.TAGV, null).joinUninterruptibly(); } @Test public void getUIDMetric() { assertArrayEquals(new byte[] { 0, 0, 1 }, tsdb.getUID(UniqueIdType.METRIC, METRIC_STRING)); } @Test public void getUIDTagk() { assertArrayEquals(new byte[] { 0, 0, 1 }, tsdb.getUID(UniqueIdType.TAGK, TAGK_STRING)); } @Test public void getUIDTagv() { assertArrayEquals(new byte[] { 0, 0, 1 }, tsdb.getUID(UniqueIdType.TAGV, TAGV_STRING)); } @Test (expected = NoSuchUniqueName.class) public void getUIDMetricNSU() { tsdb.getUID(UniqueIdType.METRIC, NSUN_METRIC); } @Test (expected = NoSuchUniqueName.class) public void getUIDTagkNSU() { tsdb.getUID(UniqueIdType.TAGK, NSUN_TAGK); } @Test (expected = NoSuchUniqueName.class) public void getUIDTagvNSU() { tsdb.getUID(UniqueIdType.TAGV, NSUN_TAGV); } @Test (expected = RuntimeException.class) public void getUIDNullType() { tsdb.getUID(null, METRIC_STRING); } @Test (expected = IllegalArgumentException.class) public void getUIDNullName() { tsdb.getUID(UniqueIdType.TAGV, null); } @Test (expected = IllegalArgumentException.class) public void getUIDEmptyName() { tsdb.getUID(UniqueIdType.TAGV, ""); } @Test public void assignUidMetric() { when(metrics.getId("sys.cpu.1")).thenThrow( new NoSuchUniqueName("metric", "sys.cpu.1")); when(metrics.getOrCreateId("sys.cpu.1")) .thenReturn(new byte[] { 0, 0, 2 }); assertArrayEquals(new byte[] { 0, 0, 2 }, tsdb.assignUid("metric", "sys.cpu.1")); } @Test (expected = IllegalArgumentException.class) public void assignUidMetricExists() { tsdb.assignUid("metric", METRIC_STRING); } @Test public void assignUidTagk() { when(tag_names.getId("datacenter")).thenThrow( new NoSuchUniqueName("tagk", "datacenter")); when(tag_names.getOrCreateId("datacenter")) .thenReturn(new byte[] { 0, 0, 2 }); assertArrayEquals(new byte[] { 0, 0, 2 }, tsdb.assignUid("tagk", "datacenter")); } @Test (expected = IllegalArgumentException.class) public void assignUidTagkExists() { tsdb.assignUid("tagk", TAGK_STRING); } @Test public void assignUidTagv() { when(tag_values.getId("localhost")).thenThrow( new NoSuchUniqueName("tagv", "localhost")); when(tag_values.getOrCreateId("localhost")) .thenReturn(new byte[] { 0, 0, 2 }); assertArrayEquals(new byte[] { 0, 0, 2 }, tsdb.assignUid("tagv", "localhost")); } @Test (expected = IllegalArgumentException.class) public void assignUidTagvExists() { tsdb.assignUid("tagv", TAGV_STRING); } @Test (expected = IllegalArgumentException.class) public void assignUidBadType() { tsdb.assignUid("nothere", METRIC_STRING); } @Test (expected = NullPointerException.class) public void assignUidNullType() { tsdb.assignUid(null, METRIC_STRING); } @Test (expected = IllegalArgumentException.class) public void assignUidNullName() { tsdb.assignUid("metric", null); } @Test (expected = IllegalArgumentException.class) public void assignUidInvalidCharacter() { tsdb.assignUid("metric", "Not!A:Valid@Name"); } @Test (expected = IllegalArgumentException.class) public void renameUidInvalidNewname() { tsdb.renameUid("metric", "existing", null); } @Test (expected = IllegalArgumentException.class) public void renameUidNonexistentMetric() { when(metrics.getId("sys.cpu.1")).thenThrow( new NoSuchUniqueName("metric", "sys.cpu.1")); tsdb.renameUid("metric", "sys.cpu.1", "sys.cpu.2"); } @Test public void renameUidMetric() { tsdb.renameUid("metric", "sys.cpu.1", "sys.cpu.2"); } @Test (expected = IllegalArgumentException.class) public void renameUidNonexistentTagk() { when(tag_names.getId("datacenter")).thenThrow( new NoSuchUniqueName("tagk", "datacenter")); tsdb.renameUid("tagk", "datacenter", "datacluster"); } @Test public void renameUidTagk() { tsdb.renameUid("tagk", "datacenter", "datacluster"); } @Test (expected = IllegalArgumentException.class) public void renameUidNonexistentTagv() { when(tag_values.getId("localhost")).thenThrow( new NoSuchUniqueName("tagv", "localhost")); tsdb.renameUid("tagv", "localhost", "127.0.0.1"); } @Test public void renameUidTagv() { tsdb.renameUid("tagv", "localhost", "127.0.0.1"); } @Test (expected = IllegalArgumentException.class) public void renameUidBadType() { tsdb.renameUid("wrongtype", METRIC_STRING, METRIC_STRING); } @Test public void uidTable() { assertNotNull(tsdb.uidTable()); assertArrayEquals("tsdb-uid".getBytes(), tsdb.uidTable()); } /** * Helper to mock the UID caches with valid responses */ private void setGetUidName() { when(metrics.getNameAsync(new byte[] { 0, 0, 1 })) .thenReturn(Deferred.fromResult("sys.cpu.0")); when(metrics.getNameAsync(new byte[] { 0, 0, 2 })).thenThrow( new NoSuchUniqueId("metric", new byte[] { 0, 0, 2})); when(tag_names.getNameAsync(new byte[] { 0, 0, 1 })) .thenReturn(Deferred.fromResult("host")); when(tag_names.getNameAsync(new byte[] { 0, 0, 2 })).thenThrow( new NoSuchUniqueId("tagk", new byte[] { 0, 0, 2})); when(tag_values.getNameAsync(new byte[] { 0, 0, 1 })) .thenReturn(Deferred.fromResult("web01")); when(tag_values.getNameAsync(new byte[] { 0, 0, 2 })).thenThrow( new NoSuchUniqueId("tag_values", new byte[] { 0, 0, 2})); } /** * Configures storage for the addPoint() tests to validate that we're storing * data points correctly. */ @Test public void setupAddPointStorage() throws Exception { storage = new MockBase(tsdb, client, true, true, true, true); } }