You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by he...@apache.org on 2023/11/13 07:19:22 UTC

(shenyu) branch master updated: [type:test][ISSUE #5278] add unit test for shenyu-discovery-etcd (#5291)

This is an automated email from the ASF dual-hosted git repository.

hefengen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new be60ba2405 [type:test][ISSUE #5278] add unit test for shenyu-discovery-etcd (#5291)
be60ba2405 is described below

commit be60ba24051c5dbaa1fd5d0510f4c12af2a61787
Author: JJellyfish <24...@qq.com>
AuthorDate: Mon Nov 13 15:19:14 2023 +0800

    [type:test][ISSUE #5278] add unit test for shenyu-discovery-etcd (#5291)
---
 .../discovery/etcd/EtcdDiscoveryServiceTest.java   | 248 +++++++++++++++++++++
 1 file changed, 248 insertions(+)

diff --git a/shenyu-discovery/shenyu-discovery-etcd/src/test/java/org/apache/shenyu/discovery/etcd/EtcdDiscoveryServiceTest.java b/shenyu-discovery/shenyu-discovery-etcd/src/test/java/org/apache/shenyu/discovery/etcd/EtcdDiscoveryServiceTest.java
new file mode 100644
index 0000000000..c5bb66bb9c
--- /dev/null
+++ b/shenyu-discovery/shenyu-discovery-etcd/src/test/java/org/apache/shenyu/discovery/etcd/EtcdDiscoveryServiceTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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 ASF 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.shenyu.discovery.etcd;
+
+import io.etcd.jetcd.ByteSequence;
+import io.etcd.jetcd.Client;
+import io.etcd.jetcd.ClientBuilder;
+import io.etcd.jetcd.KV;
+import io.etcd.jetcd.KeyValue;
+import io.etcd.jetcd.Lease;
+import io.etcd.jetcd.Watch;
+import io.etcd.jetcd.kv.GetResponse;
+import io.etcd.jetcd.kv.PutResponse;
+import io.etcd.jetcd.lease.LeaseGrantResponse;
+import io.etcd.jetcd.lease.LeaseKeepAliveResponse;
+import io.etcd.jetcd.options.GetOption;
+import io.etcd.jetcd.options.PutOption;
+import io.etcd.jetcd.options.WatchOption;
+import io.etcd.jetcd.watch.WatchEvent;
+import io.etcd.jetcd.watch.WatchResponse;
+import io.grpc.stub.StreamObserver;
+import org.apache.shenyu.common.exception.ShenyuException;
+import org.apache.shenyu.discovery.api.config.DiscoveryConfig;
+import org.apache.shenyu.discovery.api.listener.DataChangedEventListener;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Properties;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+/**
+ * The test for  {@link EtcdDiscoveryService } .
+ */
+public class EtcdDiscoveryServiceTest {
+
+    private EtcdDiscoveryService etcdDiscoveryServiceUnderTest;
+
+    private Client etcdClient;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        etcdDiscoveryServiceUnderTest = new EtcdDiscoveryService();
+        etcdClient = mock(Client.class);
+        setField(EtcdDiscoveryService.class, "etcdClient", etcdClient);
+    }
+
+    @AfterEach
+    void downTest() {
+        etcdDiscoveryServiceUnderTest.shutdown();
+        verify(etcdClient).close();
+    }
+
+    private <T> void setField(final Class<T> clazz, final String fieldName, final Object value) throws NoSuchFieldException, IllegalAccessException {
+        Field field = clazz.getDeclaredField(fieldName);
+        field.setAccessible(true);
+        field.set(etcdDiscoveryServiceUnderTest, value);
+        field.setAccessible(false);
+    }
+
+    @Test
+    public void testInit() throws ExecutionException, InterruptedException, NoSuchFieldException, IllegalAccessException {
+        setField(EtcdDiscoveryService.class, "etcdClient", null);
+        final ClientBuilder builder = mock(ClientBuilder.class);
+        when(builder.endpoints(anyString())).thenReturn(builder);
+        when(builder.build()).thenReturn(etcdClient);
+        final MockedStatic<Client> client = mockStatic(Client.class);
+        client.when(Client::builder).thenReturn(builder);
+
+        final Lease lease = mock(Lease.class);
+        final CompletableFuture<LeaseGrantResponse> leaseGrantFuture = mock(CompletableFuture.class);
+        when(lease.grant(anyLong())).thenReturn(leaseGrantFuture);
+        final LeaseGrantResponse leaseGrantResponse = mock(LeaseGrantResponse.class);
+        when(leaseGrantFuture.get()).thenReturn(leaseGrantResponse);
+        when(etcdClient.getLeaseClient()).thenReturn(lease);
+
+        ArrayList<StreamObserver<LeaseKeepAliveResponse>> observerList = new ArrayList<>();
+        doAnswer(invocationOnMock -> {
+            observerList.add(invocationOnMock.getArgument(1));
+            return lease;
+        }).when(lease).keepAlive(anyLong(), any());
+
+        final Properties props = new Properties();
+        props.put("etcdTimeout", "3000");
+        props.put("etcdTTL", "5");
+        final DiscoveryConfig discoveryConfig = new DiscoveryConfig();
+        discoveryConfig.setProps(props);
+        discoveryConfig.setServerList("localhost:2379");
+        etcdDiscoveryServiceUnderTest.init(discoveryConfig);
+        final LeaseKeepAliveResponse leaseKeepAliveResponse = mock(LeaseKeepAliveResponse.class);
+
+        observerList.forEach(observer -> {
+            observer.onNext(leaseKeepAliveResponse);
+            observer.onError(new ShenyuException("test"));
+            observer.onCompleted();
+        });
+
+        doThrow(new InterruptedException("test")).when(leaseGrantFuture).get();
+        assertThrows(ShenyuException.class, () -> etcdDiscoveryServiceUnderTest.init(discoveryConfig));
+    }
+
+    @Test
+    void testWatch() throws NoSuchFieldException, IllegalAccessException {
+        final String eventKey = "event_key";
+        final String eventValue = "event_value";
+        final String key = "key";
+
+        final Watch watch = mock(Watch.class);
+        when(watch.watch(any(ByteSequence.class), any(WatchOption.class), any(Watch.Listener.class)))
+                .thenReturn(mock(Watch.Watcher.class));
+        when(etcdClient.getWatchClient()).thenReturn(watch);
+
+        ArrayList<Watch.Listener> listeners = new ArrayList<>();
+        doAnswer(invocationOnMock -> {
+            listeners.add(invocationOnMock.getArgument(2));
+            return mock(Watch.Watcher.class);
+        }).when(watch).watch(any(ByteSequence.class), any(WatchOption.class), any(Watch.Listener.class));
+
+        final KeyValue keyValue = mock(KeyValue.class);
+        when(keyValue.getKey()).thenReturn(mock(ByteSequence.class));
+        when(keyValue.getKey().toString(any())).thenReturn(eventKey);
+        when(keyValue.getValue()).thenReturn(mock(ByteSequence.class));
+        when(keyValue.getValue().toString(any())).thenReturn(eventValue);
+
+        ArrayList<WatchEvent> events = new ArrayList<>();
+        for (WatchEvent.EventType eventType : WatchEvent.EventType.values()) {
+            WatchEvent event = mock(WatchEvent.class);
+            when(event.getEventType()).thenReturn(eventType);
+            when(event.getKeyValue()).thenReturn(keyValue);
+            events.add(event);
+        }
+        final WatchResponse watchResponse = mock(WatchResponse.class);
+        when(watchResponse.getEvents()).thenReturn(events);
+        final DataChangedEventListener mockListener = mock(DataChangedEventListener.class);
+        etcdDiscoveryServiceUnderTest.watch(key, mockListener);
+        listeners.forEach(listener -> {
+            listener.onNext(watchResponse);
+        });
+
+        final Field cacheField = etcdDiscoveryServiceUnderTest.getClass().getDeclaredField("watchCache");
+        cacheField.setAccessible(true);
+        ConcurrentMap<String, Watch.Watcher> watchCache = (ConcurrentMap) cacheField.get(etcdDiscoveryServiceUnderTest);
+        Assertions.assertNotNull(watchCache.get(key));
+    }
+
+    @Test
+    public void testUnWatch() throws NoSuchFieldException, IllegalAccessException {
+        final String key = "key";
+        etcdDiscoveryServiceUnderTest.unwatch(key);
+        final Field watchCacheField = etcdDiscoveryServiceUnderTest.getClass().getDeclaredField("watchCache");
+        watchCacheField.setAccessible(true);
+        ConcurrentMap<String, Watch.Watcher> o = (ConcurrentMap) watchCacheField.get(etcdDiscoveryServiceUnderTest);
+        assertFalse(o.containsKey(key));
+    }
+
+    @Test
+    void registerTest() throws ExecutionException, InterruptedException, TimeoutException {
+        final String key = "key";
+        final String value = "value";
+
+        final KV kvClient = mock(KV.class);
+        when(etcdClient.getKVClient()).thenReturn(kvClient);
+
+        final PutResponse putResponse = mock(PutResponse.class);
+        final CompletableFuture<PutResponse> completableFuture = mock(CompletableFuture.class);
+        when(completableFuture.get(anyLong(), any(TimeUnit.class)))
+                .thenReturn(putResponse);
+        when(kvClient.put(any(ByteSequence.class), any(ByteSequence.class), any(PutOption.class)))
+                .thenReturn(completableFuture);
+        etcdDiscoveryServiceUnderTest.register(key, value);
+
+        doThrow(new InterruptedException()).when(completableFuture).get(anyLong(), any(TimeUnit.class));
+        assertThrows(ShenyuException.class, () -> etcdDiscoveryServiceUnderTest.register(key, value));
+    }
+
+    @Test
+    void testGetRegisterData() throws InterruptedException, ExecutionException {
+        final String key = "key";
+        final KV kv = mock(KV.class);
+        when(etcdClient.getKVClient()).thenReturn(kv);
+        final GetResponse getResponse = mock(GetResponse.class);
+        when(getResponse.getKvs()).thenReturn(Collections.emptyList());
+        final CompletableFuture<GetResponse> completableFuture = mock(CompletableFuture.class);
+        when(completableFuture.get()).thenReturn(getResponse);
+        when(kv.get(any(ByteSequence.class), any(GetOption.class))).thenReturn(completableFuture);
+        assertDoesNotThrow(() -> etcdDiscoveryServiceUnderTest.getRegisterData(key));
+        doThrow(new InterruptedException("test")).when(completableFuture).get();
+        assertThrows(ShenyuException.class, () -> etcdDiscoveryServiceUnderTest.getRegisterData(key));
+    }
+
+    @Test
+    void testExists() throws ExecutionException, InterruptedException {
+        final String key = "key";
+        final KV kvClient = mock(KV.class);
+        when(etcdClient.getKVClient()).thenReturn(kvClient);
+
+        final GetResponse getResponse = mock(GetResponse.class);
+        when(getResponse.getCount()).thenReturn(1L);
+        final CompletableFuture<GetResponse> completableFuture = mock(CompletableFuture.class);
+        when(completableFuture.get()).thenReturn(getResponse);
+        when(kvClient.get(any(ByteSequence.class), any(GetOption.class))).thenReturn(completableFuture);
+
+        final Boolean result = etcdDiscoveryServiceUnderTest.exists(key);
+        assertTrue(result);
+        doThrow(new InterruptedException("test")).when(completableFuture).get();
+        assertThrows(ShenyuException.class, () -> etcdDiscoveryServiceUnderTest.exists(key));
+    }
+
+}