/* * Copyright (c) 2016 Couchbase, Inc. * * Licensed 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 com.couchbase.client.core.config; import com.couchbase.client.core.ClusterFacade; import com.couchbase.client.core.config.loader.Loader; import com.couchbase.client.core.config.refresher.Refresher; import com.couchbase.client.core.env.CoreEnvironment; import com.couchbase.client.core.env.DefaultCoreEnvironment; import com.couchbase.client.core.lang.Tuple; import com.couchbase.client.core.lang.Tuple2; import org.junit.Ignore; import org.junit.Test; import org.mockito.internal.util.collections.Sets; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import rx.Observable; import rx.functions.Action1; import rx.functions.Func1; import rx.subjects.AsyncSubject; import java.net.InetAddress; import java.util.Arrays; import java.util.HashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * Verifies the correct functionality of the {@link DefaultConfigurationProvider}. * * @author Michael Nitschinger * @since 1.0 */ public class DefaultConfigurationProviderTest { private static final CoreEnvironment environment = DefaultCoreEnvironment.create(); @Test @SuppressWarnings("unchecked") public void shouldOpenBucket() throws Exception { ClusterFacade cluster = mock(ClusterFacade.class); Loader loader = mock(Loader.class); BucketConfig bucketConfig = mock(BucketConfig.class); when(bucketConfig.name()).thenReturn("bucket"); when(loader.loadConfig(any(InetAddress.class), anyString(), anyString(), anyString())) .thenReturn(Observable.just(Tuple.create(LoaderType.Carrier, bucketConfig))); final Refresher refresher = mock(Refresher.class); when(refresher.configs()).thenReturn(Observable.<BucketConfig>empty()); when(refresher.registerBucket(anyString(), anyString(), anyString())).thenReturn(Observable.just(true)); ConfigurationProvider provider = new DefaultConfigurationProvider( cluster, environment, Arrays.asList(loader), new HashMap<LoaderType, Refresher>() {{ put(LoaderType.Carrier, refresher); }} ); provider.seedHosts(Sets.newSet(InetAddress.getByName("localhost")), true); Observable<ClusterConfig> configObservable = provider.openBucket("bucket", "password"); ClusterConfig config = configObservable.toBlocking().first(); assertTrue(config.hasBucket("bucket")); assertFalse(config.hasBucket("other")); } @Test @SuppressWarnings("unchecked") public void shouldDelegateLoadingToSecondProviderIfFirstFails() throws Exception { ClusterFacade cluster = mock(ClusterFacade.class); Loader successLoader = mock(Loader.class); Loader errorLoader = mock(Loader.class); BucketConfig bucketConfig = mock(BucketConfig.class); when(bucketConfig.name()).thenReturn("bucket"); when(successLoader.loadConfig(any(InetAddress.class), anyString(), anyString(), anyString())) .thenReturn(Observable.just(Tuple.create(LoaderType.Carrier, bucketConfig))); AsyncSubject<BucketConfig> errorSubject = AsyncSubject.create(); when(errorLoader.loadConfig(any(InetAddress.class), anyString(), anyString(), anyString())).thenReturn((Observable) errorSubject); errorSubject.onError(new IllegalStateException()); final Refresher refresher = mock(Refresher.class); when(refresher.configs()).thenReturn(Observable.<BucketConfig>empty()); when(refresher.registerBucket(anyString(), anyString(), anyString())).thenReturn(Observable.just(true)); ConfigurationProvider provider = new DefaultConfigurationProvider( cluster, environment, Arrays.asList(errorLoader, successLoader), new HashMap<LoaderType, Refresher>() {{ put(LoaderType.Carrier, refresher); put(LoaderType.HTTP, refresher); }} ); provider.seedHosts(Sets.newSet(InetAddress.getByName("localhost")), true); Observable<ClusterConfig> configObservable = provider.openBucket("bucket", "password"); ClusterConfig config = configObservable.toBlocking().first(); assertTrue(config.hasBucket("bucket")); assertFalse(config.hasBucket("other")); } @Test public void shouldOpenBucketIfSubsetOfNodesIsFailing() throws Exception { ClusterFacade cluster = mock(ClusterFacade.class); final Refresher refresher = mock(Refresher.class); when(refresher.configs()).thenReturn(Observable.<BucketConfig>empty()); when(refresher.registerBucket(anyString(), anyString(), anyString())).thenReturn(Observable.just(true)); Loader carrierLoader = mock(Loader.class); Loader httpLoader = mock(Loader.class); final InetAddress goodNode = InetAddress.getByName("5.6.7.8"); InetAddress badNode = InetAddress.getByName("1.2.3.4"); when(carrierLoader.loadConfig(any(InetAddress.class), any(String.class), any(String.class), any(String.class))) .thenAnswer(new Answer<Observable<Tuple2<LoaderType, BucketConfig>>>() { @Override public Observable<Tuple2<LoaderType, BucketConfig>> answer(InvocationOnMock in) throws Throwable { InetAddress target = (InetAddress) in.getArguments()[0]; if (target.equals(goodNode)) { final BucketConfig bucketConfig = mock(BucketConfig.class); when(bucketConfig.name()).thenReturn("bucket-carrier-"+target.getHostAddress()); return Observable.just(Tuple.create(LoaderType.Carrier, bucketConfig)); } else { return Observable.error(new Exception("Could not load config for some reason.")); } } }); when(httpLoader.loadConfig(any(InetAddress.class), any(String.class), any(String.class), any(String.class))) .thenAnswer(new Answer<Observable<Tuple2<LoaderType, BucketConfig>>>() { @Override public Observable<Tuple2<LoaderType, BucketConfig>> answer(InvocationOnMock in) throws Throwable { InetAddress target = (InetAddress) in.getArguments()[0]; if (target.equals(goodNode)) { final BucketConfig bucketConfig = mock(BucketConfig.class); when(bucketConfig.name()).thenReturn("bucket-http-"+target.getHostAddress()); return Observable.just(Tuple.create(LoaderType.HTTP, bucketConfig)); } else { return Observable.error(new Exception("Could not load config for some reason.")); } } }); ConfigurationProvider provider = new DefaultConfigurationProvider( cluster, environment, Arrays.asList(carrierLoader, httpLoader), new HashMap<LoaderType, Refresher>() {{ put(LoaderType.Carrier, refresher); put(LoaderType.HTTP, refresher); }} ); provider.seedHosts(Sets.newSet(badNode, goodNode), true); Observable<ClusterConfig> configObservable = provider.openBucket("bucket", "password"); ClusterConfig config = configObservable.toBlocking().first(); assertEquals(1, config.bucketConfigs().size()); assertTrue(config.hasBucket("bucket-carrier-" + goodNode.getHostAddress())); } @Test public void shouldOpenBucketIfSubsetOfNodesIsFailingAndOnlyHttpAvailable() throws Exception { ClusterFacade cluster = mock(ClusterFacade.class); final Refresher refresher = mock(Refresher.class); when(refresher.configs()).thenReturn(Observable.<BucketConfig>empty()); when(refresher.registerBucket(anyString(), anyString(), anyString())).thenReturn(Observable.just(true)); Loader carrierLoader = mock(Loader.class); Loader httpLoader = mock(Loader.class); final InetAddress goodNode = InetAddress.getByName("5.6.7.8"); InetAddress badNode = InetAddress.getByName("1.2.3.4"); when(carrierLoader.loadConfig(any(InetAddress.class), any(String.class), any(String.class), any(String.class))) .thenAnswer(new Answer<Observable<Tuple2<LoaderType, BucketConfig>>>() { @Override public Observable<Tuple2<LoaderType, BucketConfig>> answer(InvocationOnMock in) throws Throwable { return Observable.error(new Exception("Could not load config for some reason.")); } }); when(httpLoader.loadConfig(any(InetAddress.class), any(String.class), any(String.class), any(String.class))) .thenAnswer(new Answer<Observable<Tuple2<LoaderType, BucketConfig>>>() { @Override public Observable<Tuple2<LoaderType, BucketConfig>> answer(InvocationOnMock in) throws Throwable { InetAddress target = (InetAddress) in.getArguments()[0]; if (target.equals(goodNode)) { final BucketConfig bucketConfig = mock(BucketConfig.class); when(bucketConfig.name()).thenReturn("bucket-http-"+target.getHostAddress()); return Observable.just(Tuple.create(LoaderType.HTTP, bucketConfig)); } else { return Observable.error(new Exception("Could not load config for some reason.")); } } }); ConfigurationProvider provider = new DefaultConfigurationProvider( cluster, environment, Arrays.asList(carrierLoader, httpLoader), new HashMap<LoaderType, Refresher>() {{ put(LoaderType.Carrier, refresher); put(LoaderType.HTTP, refresher); }} ); provider.seedHosts(Sets.newSet(badNode, goodNode), true); Observable<ClusterConfig> configObservable = provider.openBucket("bucket", "password"); ClusterConfig config = configObservable.toBlocking().first(); assertEquals(1, config.bucketConfigs().size()); assertTrue(config.hasBucket("bucket-http-" + goodNode.getHostAddress())); } @Test public void shouldOpenBucketIfSubsetOfNodesIsNotResponding() throws Exception { ClusterFacade cluster = mock(ClusterFacade.class); final Refresher refresher = mock(Refresher.class); when(refresher.configs()).thenReturn(Observable.<BucketConfig>empty()); when(refresher.registerBucket(anyString(), anyString(), anyString())).thenReturn(Observable.just(true)); Loader carrierLoader = mock(Loader.class); Loader httpLoader = mock(Loader.class); final InetAddress goodNode = InetAddress.getByName("5.6.7.8"); InetAddress badNode = InetAddress.getByName("1.2.3.4"); when(carrierLoader.loadConfig(any(InetAddress.class), any(String.class), any(String.class), any(String.class))) .thenAnswer(new Answer<Observable<Tuple2<LoaderType, BucketConfig>>>() { @Override public Observable<Tuple2<LoaderType, BucketConfig>> answer(InvocationOnMock in) throws Throwable { InetAddress target = (InetAddress) in.getArguments()[0]; if (target.equals(goodNode)) { final BucketConfig bucketConfig = mock(BucketConfig.class); when(bucketConfig.name()).thenReturn("bucket-carrier-"+target.getHostAddress()); return Observable.just(Tuple.create(LoaderType.Carrier, bucketConfig)); } else { return Observable.timer(1, TimeUnit.MINUTES).map(new Func1<Long, Tuple2<LoaderType, BucketConfig>>() { @Override public Tuple2<LoaderType, BucketConfig> call(Long aLong) { throw new RuntimeException("Could not load config for some reason."); } }); } } }); when(httpLoader.loadConfig(any(InetAddress.class), any(String.class), any(String.class), any(String.class))) .thenAnswer(new Answer<Observable<Tuple2<LoaderType, BucketConfig>>>() { @Override public Observable<Tuple2<LoaderType, BucketConfig>> answer(InvocationOnMock in) throws Throwable { InetAddress target = (InetAddress) in.getArguments()[0]; if (target.equals(goodNode)) { final BucketConfig bucketConfig = mock(BucketConfig.class); when(bucketConfig.name()).thenReturn("bucket-http-"+target.getHostAddress()); return Observable.just(Tuple.create(LoaderType.HTTP, bucketConfig)); } else { return Observable.timer(1, TimeUnit.MINUTES).map(new Func1<Long, Tuple2<LoaderType, BucketConfig>>() { @Override public Tuple2<LoaderType, BucketConfig> call(Long aLong) { throw new RuntimeException("Could not load config for some reason."); } }); } } }); ConfigurationProvider provider = new DefaultConfigurationProvider( cluster, environment, Arrays.asList(carrierLoader, httpLoader), new HashMap<LoaderType, Refresher>() {{ put(LoaderType.Carrier, refresher); put(LoaderType.HTTP, refresher); }} ); provider.seedHosts(Sets.newSet(badNode, goodNode), true); Observable<ClusterConfig> configObservable = provider.openBucket("bucket", "password"); ClusterConfig config = configObservable.toBlocking().first(); assertEquals(1, config.bucketConfigs().size()); assertTrue(config.hasBucket("bucket-carrier-" + goodNode.getHostAddress())); } @Test public void shouldEmitNewClusterConfig() throws Exception { final ClusterFacade cluster = mock(ClusterFacade.class); Loader loader = mock(Loader.class); BucketConfig bucketConfig = mock(BucketConfig.class); when(bucketConfig.name()).thenReturn("bucket"); when(loader.loadConfig(any(InetAddress.class), anyString(), anyString(), anyString())) .thenReturn(Observable.just(Tuple.create(LoaderType.Carrier, bucketConfig))); final Refresher refresher = mock(Refresher.class); when(refresher.configs()).thenReturn(Observable.<BucketConfig>empty()); when(refresher.registerBucket(anyString(), anyString(), anyString())).thenReturn(Observable.just(true)); ConfigurationProvider provider = new DefaultConfigurationProvider( cluster, environment, Arrays.asList(loader), new HashMap<LoaderType, Refresher>() {{ put(LoaderType.Carrier, refresher); }} ); final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<ClusterConfig> configReference = new AtomicReference<ClusterConfig>(); provider.configs().subscribe(new Action1<ClusterConfig>() { @Override public void call(ClusterConfig clusterConfig) { configReference.set(clusterConfig); latch.countDown(); } }); provider.seedHosts(Sets.newSet(InetAddress.getByName("localhost")), true); Observable<ClusterConfig> configObservable = provider.openBucket("bucket", "password"); ClusterConfig config = configObservable.toBlocking().first(); assertTrue(config.hasBucket("bucket")); assertFalse(config.hasBucket("other")); assertTrue(latch.await(2, TimeUnit.SECONDS)); assertEquals(configReference.get(), config); } @SuppressWarnings("unchecked") @Test public void shouldFailOpeningBucketIfNoConfigLoaded() throws Exception { ClusterFacade cluster = mock(ClusterFacade.class); Loader errorLoader = mock(Loader.class); AsyncSubject<Tuple2<LoaderType, BucketConfig>> errorSubject = AsyncSubject.create(); when(errorLoader.loadConfig(any(InetAddress.class), anyString(), anyString(), anyString())).thenReturn(errorSubject); errorSubject.onError(new IllegalStateException()); final Refresher refresher = mock(Refresher.class); when(refresher.configs()).thenReturn(Observable.<BucketConfig>empty()); when(refresher.registerBucket(anyString(), anyString(), anyString())).thenReturn(Observable.just(true)); ConfigurationProvider provider = new DefaultConfigurationProvider( cluster, environment, Arrays.asList(errorLoader), new HashMap<LoaderType, Refresher>() {{ put(LoaderType.Carrier, refresher); }} ); provider.seedHosts(Sets.newSet(InetAddress.getByName("localhost")), true); Observable<ClusterConfig> configObservable = provider.openBucket("bucket", "password"); try { configObservable.toBlocking().single(); assertTrue(false); } catch(ConfigurationException ex) { assertEquals("Could not open bucket.", ex.getMessage()); } catch(Exception ex) { assertTrue(false); } } @Test @Ignore public void shouldCloseBucket() { } @Test @Ignore public void shouldCloseBuckets() { } @Test @Ignore public void shouldAcceptProposedConfig() { } }