content: /*
   *
   * Copyright 1999-2018 Alibaba Group Holding Ltd.
   *
   * 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.alibaba.nacos.client.naming;
  
  import com.alibaba.nacos.api.PropertyKeyConst;
  import com.alibaba.nacos.api.common.Constants;
  import com.alibaba.nacos.api.exception.NacosException;
  import com.alibaba.nacos.api.naming.listener.EventListener;
  import com.alibaba.nacos.api.naming.pojo.Instance;
  import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
  import com.alibaba.nacos.api.selector.AbstractSelector;
  import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder;
  import com.alibaba.nacos.client.naming.event.InstancesChangeEvent;
  import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier;
  import com.alibaba.nacos.client.naming.remote.NamingClientProxy;
  import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy;
  import com.alibaba.nacos.client.naming.selector.NamingSelectorFactory;
  import com.alibaba.nacos.client.naming.selector.NamingSelectorWrapper;
  import com.alibaba.nacos.client.naming.utils.UtilAndComs;
  import com.alibaba.nacos.common.utils.CollectionUtils;
  import com.alibaba.nacos.common.utils.StringUtils;
  import org.junit.jupiter.api.AfterEach;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  import org.junit.jupiter.api.extension.ExtendWith;
  import org.mockito.Mock;
  import org.mockito.junit.jupiter.MockitoExtension;
  
  import java.lang.reflect.Field;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  import java.util.Properties;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  import static com.alibaba.nacos.client.naming.selector.NamingSelectorFactory.getUniqueClusterString;
  import static org.junit.jupiter.api.Assertions.assertEquals;
  import static org.junit.jupiter.api.Assertions.assertNotNull;
  import static org.junit.jupiter.api.Assertions.assertSame;
  import static org.junit.jupiter.api.Assertions.assertThrows;
  import static org.junit.jupiter.api.Assertions.assertTrue;
  import static org.mockito.ArgumentMatchers.anyBoolean;
  import static org.mockito.ArgumentMatchers.anyString;
  import static org.mockito.ArgumentMatchers.argThat;
  import static org.mockito.ArgumentMatchers.eq;
  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;
  
  @ExtendWith(MockitoExtension.class)
  class NacosNamingServiceTest {
      
      @Mock
      private NamingClientProxy proxy;
      
      @Mock
      private InstancesChangeNotifier changeNotifier;
      
      @Mock
      private ServiceInfoHolder serviceInfoHolder;
      
      private NacosNamingService client;
      
      @BeforeEach
      void before() throws NoSuchFieldException, NacosException, IllegalAccessException {
          Properties prop = new Properties();
          prop.setProperty("serverAddr", "localhost");
          prop.put(PropertyKeyConst.NAMESPACE, "test");
          client = new NacosNamingService(prop);
          injectMocks(client);
      }
      
      @AfterEach
      void tearDown() throws NacosException {
          client.shutDown();
      }
      
      private void injectMocks(NacosNamingService client) throws NoSuchFieldException, IllegalAccessException {
          // inject proxy
          Field serverProxyField = NacosNamingService.class.getDeclaredField("clientProxy");
          serverProxyField.setAccessible(true);
          try {
              ((NamingHttpClientProxy) serverProxyField.get(client)).shutdown();
          } catch (Throwable ignored) {
          }
          serverProxyField.set(client, proxy);
          
          // inject notifier
          doReturn(InstancesChangeEvent.class).when(changeNotifier).subscribeType();
          Field changeNotifierField = NacosNamingService.class.getDeclaredField("changeNotifier");
          changeNotifierField.setAccessible(true);
          changeNotifierField.set(client, changeNotifier);
          
          // inject service info holder
          Field serviceInfoHolderField = NacosNamingService.class.getDeclaredField("serviceInfoHolder");
          serviceInfoHolderField.setAccessible(true);
          try {
              ((ServiceInfoHolder) serviceInfoHolderField.get(client)).shutdown();
          } catch (Throwable ignored) {
          }
          serviceInfoHolderField.set(client, serviceInfoHolder);
      }
      
      @Test
      void testRegisterInstanceSingle() throws NacosException {
          //given
          String serviceName = "service1";
          String ip = "1.1.1.1";
          int port = 10000;
          //when
          client.registerInstance(serviceName, ip, port);
          //then
          verify(proxy, times(1)).registerService(eq(serviceName), eq(Constants.DEFAULT_GROUP),
                  argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port
                          && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName()
                          .equals(Constants.DEFAULT_CLUSTER_NAME)));
      }
      
      @Test
      void testRegisterInstanceSingleWithGroup() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          String ip = "1.1.1.1";
          int port = 10000;
          //when
          client.registerInstance(serviceName, groupName, ip, port);
          //then
          verify(proxy, times(1)).registerService(eq(serviceName), eq(groupName),
                  argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port
                          && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName()
                          .equals(Constants.DEFAULT_CLUSTER_NAME)));
      }
      
      @Test
      void testRegisterInstanceSingleWithCluster() throws NacosException {
          //given
          String serviceName = "service1";
          String clusterName = "cluster1";
          String ip = "1.1.1.1";
          int port = 10000;
          //when
          client.registerInstance(serviceName, ip, port, clusterName);
          //then
          verify(proxy, times(1)).registerService(eq(serviceName), eq(Constants.DEFAULT_GROUP),
                  argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port
                          && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName()
                          .equals(clusterName)));
      }
      
      @Test
      void testRegisterInstanceSingleFull() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          String clusterName = "cluster1";
          String ip = "1.1.1.1";
          int port = 10000;
          //when
          client.registerInstance(serviceName, groupName, ip, port, clusterName);
          //then
          verify(proxy, times(1)).registerService(eq(serviceName), eq(groupName),
                  argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port
                          && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName()
                          .equals(clusterName)));
      }
      
      @Test
      void testRegisterInstanceByInstanceOnlyService() throws NacosException {
          //given
          String serviceName = "service1";
          Instance instance = new Instance();
          //when
          client.registerInstance(serviceName, instance);
          //then
          verify(proxy, times(1)).registerService(serviceName, Constants.DEFAULT_GROUP, instance);
      }
      
      @Test
      void testRegisterInstanceByInstanceFullName() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          Instance instance = new Instance();
          //when
          client.registerInstance(serviceName, groupName, instance);
          //then
          verify(proxy, times(1)).registerService(serviceName, groupName, instance);
      }
      
      @Test
      void testRegisterInstanceByInstanceWithCluster() throws NacosException {
          Throwable exception = assertThrows(NacosException.class, () -> {
              
              //given
              String serviceName = "service1";
              String groupName = "group1";
              Instance instance = new Instance();
              instance.setClusterName("cluster1,cluster2");
              //when
              client.registerInstance(serviceName, groupName, instance);
          });
          assertTrue(exception.getMessage().contains(
                  "Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: cluster1,cluster2)"));
      }
      
      @Test
      void testBatchRegisterInstance() throws NacosException {
          Instance instance = new Instance();
          String serviceName = "service1";
          String ip = "1.1.1.1";
          int port = 10000;
          instance.setServiceName(serviceName);
          instance.setEphemeral(true);
          instance.setPort(port);
          instance.setIp(ip);
          List<Instance> instanceList = new ArrayList<>();
          instanceList.add(instance);
          //when
          client.batchRegisterInstance(serviceName, Constants.DEFAULT_GROUP, instanceList);
          //then
          verify(proxy, times(1)).batchRegisterService(eq(serviceName), eq(Constants.DEFAULT_GROUP),
                  argThat(instances -> CollectionUtils.isEqualCollection(instanceList, instances)));
      }
      
      @Test
      void testBatchRegisterInstanceWithGroupNamePrefix() throws NacosException {
          Instance instance = new Instance();
          String serviceName = "service1";
          String ip = "1.1.1.1";
          int port = 10000;
          instance.setServiceName(Constants.DEFAULT_GROUP + "@@" + serviceName);
          instance.setEphemeral(true);
          instance.setPort(port);
          instance.setIp(ip);
          List<Instance> instanceList = new ArrayList<>();
          instanceList.add(instance);
          //when
          client.batchRegisterInstance(serviceName, Constants.DEFAULT_GROUP, instanceList);
          //then
          verify(proxy, times(1)).batchRegisterService(eq(serviceName), eq(Constants.DEFAULT_GROUP),
                  argThat(instances -> CollectionUtils.isEqualCollection(instanceList, instances)));
      }
      
      @Test
      void testBatchRegisterInstanceWithWrongGroupNamePrefix() throws NacosException {
          Instance instance = new Instance();
          String serviceName = "service1";
          String ip = "1.1.1.1";
          int port = 10000;
          instance.setServiceName("WrongGroup" + "@@" + serviceName);
          instance.setEphemeral(true);
          instance.setPort(port);
          instance.setIp(ip);
          List<Instance> instanceList = new ArrayList<>();
          instanceList.add(instance);
          //when
          try {
              client.batchRegisterInstance(serviceName, Constants.DEFAULT_GROUP, instanceList);
          } catch (Exception e) {
              assertTrue(e instanceof NacosException);
              assertTrue(e.getMessage().contains("wrong group name prefix of instance service name"));
          }
      }
      
      @Test
      void testBatchDeRegisterInstance() throws NacosException {
          Instance instance = new Instance();
          String serviceName = "service1";
          String ip = "1.1.1.1";
          int port = 10000;
          instance.setServiceName(serviceName);
          instance.setEphemeral(true);
          instance.setPort(port);
          instance.setIp(ip);
          List<Instance> instanceList = new ArrayList<>();
          instanceList.add(instance);
          //when
          try {
              client.batchDeregisterInstance(serviceName, Constants.DEFAULT_GROUP, instanceList);
          } catch (Exception e) {
              assertTrue(e instanceof NacosException);
              assertTrue(e.getMessage().contains("not found"));
          }
      }
      
      @Test
      void testDeregisterInstanceSingle() throws NacosException {
          //given
          String serviceName = "service1";
          String ip = "1.1.1.1";
          int port = 10000;
          //when
          client.deregisterInstance(serviceName, ip, port);
          //then
          verify(proxy, times(1)).deregisterService(eq(serviceName), eq(Constants.DEFAULT_GROUP),
                  argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port
                          && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName()
                          .equals(Constants.DEFAULT_CLUSTER_NAME)));
      }
      
      @Test
      void testDeregisterInstanceSingleWithGroup() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          String ip = "1.1.1.1";
          int port = 10000;
          //when
          client.deregisterInstance(serviceName, groupName, ip, port);
          //then
          verify(proxy, times(1)).deregisterService(eq(serviceName), eq(groupName),
                  argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port
                          && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName()
                          .equals(Constants.DEFAULT_CLUSTER_NAME)));
      }
      
      @Test
      void testDeregisterInstanceSingleWithCluster() throws NacosException {
          //given
          String serviceName = "service1";
          String clusterName = "cluster1";
          String ip = "1.1.1.1";
          int port = 10000;
          //when
          client.deregisterInstance(serviceName, ip, port, clusterName);
          //then
          verify(proxy, times(1)).deregisterService(eq(serviceName), eq(Constants.DEFAULT_GROUP),
                  argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port
                          && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName()
                          .equals(clusterName)));
      }
      
      @Test
      void testDeregisterInstanceSingleFull() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          String clusterName = "cluster1";
          String ip = "1.1.1.1";
          int port = 10000;
          //when
          client.deregisterInstance(serviceName, groupName, ip, port, clusterName);
          //then
          verify(proxy, times(1)).deregisterService(eq(serviceName), eq(groupName),
                  argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port
                          && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName()
                          .equals(clusterName)));
      }
      
      @Test
      void testDeregisterInstanceByInstanceOnlyService() throws NacosException {
          //given
          String serviceName = "service1";
          Instance instance = new Instance();
          //when
          client.deregisterInstance(serviceName, instance);
          //then
          verify(proxy, times(1)).deregisterService(serviceName, Constants.DEFAULT_GROUP, instance);
      }
      
      @Test
      void testDeregisterInstanceByInstanceFullName() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          Instance instance = new Instance();
          //when
          client.deregisterInstance(serviceName, groupName, instance);
          //then
          verify(proxy, times(1)).deregisterService(serviceName, groupName, instance);
      }
      
      @Test
      void testGetAllInstancesOnlyService() throws NacosException {
          //given
          String serviceName = "service1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(new Instance());
          when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenReturn(serviceInfo);
          List<Instance> result = client.getAllInstances(serviceName);
          //then
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testGetAllInstancesFullName() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.setGroupName(groupName);
          serviceInfo.addHost(new Instance());
          when(proxy.subscribe(serviceName, groupName, "")).thenReturn(serviceInfo);
          List<Instance> result = client.getAllInstances(serviceName, groupName);
          //then
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testGetAllInstancesOnlyServiceNotSubscribe() throws NacosException {
          //given
          String serviceName = "service1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(new Instance());
          when(proxy.queryInstancesOfService(serviceName, Constants.DEFAULT_GROUP, "", false)).thenReturn(serviceInfo);
          List<Instance> result = client.getAllInstances(serviceName, false);
          //then
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testGetAllInstancesFullNameNotSubscribe() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.setGroupName(groupName);
          serviceInfo.addHost(new Instance());
          when(proxy.queryInstancesOfService(serviceName, groupName, "", false)).thenReturn(serviceInfo);
          List<Instance> result = client.getAllInstances(serviceName, groupName, false);
          //then
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
          
      }
      
      @Test
      void testGetAllInstancesWithServiceAndClusters() throws NacosException {
          //given
          String serviceName = "service1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(mockInstance("cluster1", false));
          serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, false));
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenReturn(serviceInfo);
          List<Instance> result = client.getAllInstances(serviceName, clusterList);
          //then
          assertEquals(1, result.size());
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testGetAllInstancesWithFullNameAndClusters() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          // when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.setGroupName(groupName);
          serviceInfo.addHost(mockInstance("cluster1", false));
          serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, false));
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          serviceInfo.getHosts().get(1).setClusterName(Constants.DEFAULT_CLUSTER_NAME);
          when(proxy.subscribe(serviceName, groupName, "")).thenReturn(serviceInfo);
          List<Instance> result = client.getAllInstances(serviceName, groupName, clusterList);
          //then
          assertEquals(1, result.size());
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
          
      }
      
      @Test
      void testGetAllInstancesWithServiceAndClustersNotSubscribe() throws NacosException {
          //given
          String serviceName = "service1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(mockInstance("cluster1", false));
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          when(proxy.queryInstancesOfService(serviceName, Constants.DEFAULT_GROUP, "cluster1,cluster2",
                  false)).thenReturn(serviceInfo);
          List<Instance> result = client.getAllInstances(serviceName, clusterList, false);
          //then
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testGetAllInstancesWithFullNameAndClustersNotSubscribe() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          // when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.setGroupName(groupName);
          serviceInfo.addHost(mockInstance("cluster1", false));
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          when(proxy.queryInstancesOfService(serviceName, groupName, "cluster1,cluster2", false)).thenReturn(serviceInfo);
          List<Instance> result = client.getAllInstances(serviceName, groupName, clusterList, false);
          //then
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testGetAllInstanceFromFailover() throws NacosException {
          when(serviceInfoHolder.isFailoverSwitch()).thenReturn(true);
          ServiceInfo serviceInfo = new ServiceInfo("group1@@service1");
          serviceInfo.setHosts(Collections.singletonList(new Instance()));
          when(serviceInfoHolder.getFailoverServiceInfo("service1", "group1")).thenReturn(serviceInfo);
          List<Instance> actual = client.getAllInstances("service1", "group1", false);
          verify(proxy, never()).queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean());
          assertEquals(1, actual.size());
          assertEquals(new Instance(), actual.get(0));
      }
      
      @Test
      void testGetAllInstanceFromFailoverEmpty() throws NacosException {
          when(serviceInfoHolder.isFailoverSwitch()).thenReturn(true);
          ServiceInfo serviceInfo = new ServiceInfo("group1@@service1");
          when(serviceInfoHolder.getFailoverServiceInfo("service1", "group1")).thenReturn(serviceInfo);
          List<Instance> actual = client.getAllInstances("service1", "group1", false);
          verify(proxy).queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean());
          assertEquals(0, actual.size());
      }
      
      @Test
      void testGetAllInstanceWithCacheAndSubscribeException() throws NacosException {
          String serviceName = "service1";
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(new Instance());
          when(serviceInfoHolder.getServiceInfo(serviceName, Constants.DEFAULT_GROUP)).thenReturn(serviceInfo);
          when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenThrow(new NacosException(500, "test"));
          List<Instance> result = client.getAllInstances(serviceName);
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testGetAllInstanceWithoutCacheAndSubscribeException() throws NacosException {
          String serviceName = "service1";
          when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenThrow(new NacosException(500, "test"));
          assertThrows(NacosException.class, () -> client.getAllInstances(serviceName));
      }
      
      @Test
      void testGetAllInstanceWithCacheAndSubscribed() throws NacosException {
          String serviceName = "service1";
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(new Instance());
          when(serviceInfoHolder.getServiceInfo(serviceName, Constants.DEFAULT_GROUP)).thenReturn(serviceInfo);
          when(proxy.isSubscribed(serviceName, Constants.DEFAULT_GROUP, "")).thenReturn(true);
          List<Instance> result = client.getAllInstances(serviceName);
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testSelectInstancesOnlyService() throws NacosException {
          //given
          String serviceName = "service1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true));
          when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenReturn(serviceInfo);
          List<Instance> result = client.selectInstances(serviceName, true);
          //then
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testSelectInstancesFullName() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true));
          when(proxy.subscribe(serviceName, groupName, "")).thenReturn(serviceInfo);
          List<Instance> result = client.selectInstances(serviceName, groupName, true);
          //then
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testSelectInstancesOnlyServiceNotSubscribe() throws NacosException {
          //given
          String serviceName = "service1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true));
          when(proxy.queryInstancesOfService(serviceName, Constants.DEFAULT_GROUP, "", false)).thenReturn(serviceInfo);
          List<Instance> result = client.selectInstances(serviceName, true, false);
          //then
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testSelectInstancesFullNameNotSubscribe() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true));
          when(proxy.queryInstancesOfService(serviceName, groupName, "", false)).thenReturn(serviceInfo);
          List<Instance> result = client.selectInstances(serviceName, groupName, true, false);
          //then
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
          
      }
      
      @Test
      void testSelectInstancesWithServiceAndClusters() throws NacosException {
          //given
          String serviceName = "service1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(mockInstance("cluster1", true));
          serviceInfo.addHost(mockInstance("cluster1", false));
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenReturn(serviceInfo);
          List<Instance> result = client.selectInstances(serviceName, clusterList, true);
          //then
          assertEquals(1, result.size());
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testSelectInstancesWithFullNameAndClusters() throws NacosException {
          //given
          String serviceName = "service1";
          final String groupName = "group1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(mockInstance("cluster1", true));
          serviceInfo.addHost(mockInstance("cluster1", false));
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          when(proxy.subscribe(serviceName, groupName, "")).thenReturn(serviceInfo);
          List<Instance> result = client.selectInstances(serviceName, groupName, clusterList, true);
          //then
          assertEquals(1, result.size());
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
          
      }
      
      @Test
      void testSelectInstancesWithServiceAndClustersNotSubscribe() throws NacosException {
          //given
          String serviceName = "service1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(mockInstance("cluster1", true));
          serviceInfo.addHost(mockInstance("cluster1", false));
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          when(proxy.queryInstancesOfService(serviceName, Constants.DEFAULT_GROUP, "cluster1,cluster2",
                  false)).thenReturn(serviceInfo);
          List<Instance> result = client.selectInstances(serviceName, clusterList, true, false);
          //then
          assertEquals(1, result.size());
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testSelectInstancesWithFullNameAndClustersNotSubscribe() throws NacosException {
          //given
          String serviceName = "service1";
          final String groupName = "group1";
          //when
          ServiceInfo serviceInfo = new ServiceInfo();
          serviceInfo.setName(serviceName);
          serviceInfo.addHost(mockInstance("cluster1", true));
          serviceInfo.addHost(mockInstance("cluster1", false));
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          when(proxy.queryInstancesOfService(serviceName, groupName, "cluster1,cluster2", false)).thenReturn(serviceInfo);
          List<Instance> result = client.selectInstances(serviceName, groupName, clusterList, true, false);
          //then
          assertEquals(1, result.size());
          assertEquals(serviceInfo.getHosts().get(0), result.get(0));
      }
      
      @Test
      void testSelectInstancesWithHealthyFlag() throws NacosException {
          //given
          Instance healthyInstance = new Instance();
          healthyInstance.setHealthy(true);
          
          Instance instance1 = new Instance();
          instance1.setHealthy(false);
          
          Instance instance2 = new Instance();
          instance2.setHealthy(true);
          instance2.setEnabled(false);
          Instance instance3 = new Instance();
          instance3.setHealthy(true);
          instance3.setWeight(0.0);
          
          List<Instance> hosts = new ArrayList<>();
          hosts.add(healthyInstance);
          hosts.add(instance1);
          hosts.add(instance2);
          hosts.add(instance3);
          
          ServiceInfo info = new ServiceInfo();
          info.setHosts(hosts);
          String serviceName = "service1";
          String groupName = "group1";
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          when(proxy.queryInstancesOfService(serviceName, groupName, "cluster1,cluster2", false)).thenReturn(info);
          
          //when
          List<Instance> instances = client.selectInstances(serviceName, groupName, clusterList, true, false);
          //then
          assertEquals(1, instances.size());
          assertSame(healthyInstance, instances.get(0));
      }
      
      @Test
      void testSelectOneHealthyInstanceOnlyService() throws NacosException {
          //given
          ServiceInfo infoWithHealthyInstance = new ServiceInfo();
          infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true));
          when(proxy.subscribe(anyString(), anyString(), anyString())).thenReturn(infoWithHealthyInstance);
          
          String serviceName = "service1";
          //when
          Instance instance = client.selectOneHealthyInstance(serviceName);
          //then
          assertNotNull(instance);
      }
      
      @Test
      void testSelectOneHealthyInstanceFullName() throws NacosException {
          //given
          ServiceInfo infoWithHealthyInstance = new ServiceInfo();
          infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true));
          when(proxy.subscribe(anyString(), anyString(), anyString())).thenReturn(infoWithHealthyInstance);
          
          String serviceName = "service1";
          String groupName = "group1";
          //when
          Instance instance = client.selectOneHealthyInstance(serviceName, groupName);
          //then
          assertNotNull(instance);
      }
      
      @Test
      void testSelectOneHealthyInstanceOnlyServiceNotSubscribe() throws NacosException {
          //given
          ServiceInfo infoWithHealthyInstance = new ServiceInfo();
          infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true));
          when(proxy.queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean())).thenReturn(
                  infoWithHealthyInstance);
          
          String serviceName = "service1";
          //when
          Instance instance = client.selectOneHealthyInstance(serviceName, false);
          //then
          assertNotNull(instance);
      }
      
      @Test
      void testSelectOneHealthyInstanceFullNameNotSubscribe() throws NacosException {
          //given
          ServiceInfo infoWithHealthyInstance = new ServiceInfo();
          infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true));
          when(proxy.queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean())).thenReturn(
                  infoWithHealthyInstance);
          
          String serviceName = "service1";
          String groupName = "group1";
          //when
          Instance instance = client.selectOneHealthyInstance(serviceName, groupName, false);
          //then
          assertNotNull(instance);
          
      }
      
      @Test
      void testSelectOneHealthyInstanceWithServiceAndClusters() throws NacosException {
          //given
          ServiceInfo infoWithHealthyInstance = new ServiceInfo();
          infoWithHealthyInstance.addHost(mockInstance("cluster1", true));
          infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true));
          String serviceName = "service1";
          when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, StringUtils.EMPTY)).thenReturn(
                  infoWithHealthyInstance);
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          //when
          Instance instance = client.selectOneHealthyInstance(serviceName, clusterList);
          //then
          assertNotNull(instance);
          assertEquals("cluster1", instance.getClusterName());
      }
      
      @Test
      void testSelectOneHealthyInstanceWithFullNameAndClusters() throws NacosException {
          //given
          ServiceInfo infoWithHealthyInstance = new ServiceInfo();
          infoWithHealthyInstance.addHost(mockInstance("cluster1", true));
          infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true));
          when(proxy.subscribe(anyString(), anyString(), anyString())).thenReturn(infoWithHealthyInstance);
          
          String serviceName = "service1";
          String groupName = "group1";
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          //when
          Instance instance = client.selectOneHealthyInstance(serviceName, groupName, clusterList);
          //then
          assertNotNull(instance);
          assertEquals("cluster1", instance.getClusterName());
      }
      
      @Test
      void testSelectOneHealthyInstanceWithServiceAndClustersNotSubscribe() throws NacosException {
          //given
          ServiceInfo infoWithHealthyInstance = new ServiceInfo();
          infoWithHealthyInstance.addHost(mockInstance("cluster1", true));
          when(proxy.queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean())).thenReturn(
                  infoWithHealthyInstance);
          
          String serviceName = "service1";
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          //when
          Instance instance = client.selectOneHealthyInstance(serviceName, clusterList, false);
          //then
          assertNotNull(instance);
          assertEquals("cluster1", instance.getClusterName());
      }
      
      @Test
      void testSelectOneHealthyInstanceWithFullNameAndClustersNotSubscribe() throws NacosException {
          //given
          ServiceInfo infoWithHealthyInstance = new ServiceInfo();
          infoWithHealthyInstance.addHost(mockInstance("cluster1", true));
          when(proxy.queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean())).thenReturn(
                  infoWithHealthyInstance);
          
          String serviceName = "service1";
          String groupName = "group1";
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          //when
          Instance instance = client.selectOneHealthyInstance(serviceName, groupName, clusterList, false);
          //then
          assertNotNull(instance);
          assertEquals("cluster1", instance.getClusterName());
      }
      
      @Test
      void testSubscribeOnlyService() throws NacosException {
          //given
          String serviceName = "service1";
          EventListener listener = event -> {
          
          };
          //when
          client.subscribe(serviceName, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, Constants.DEFAULT_GROUP, Constants.NULL,
                  NamingSelectorFactory.newClusterSelector(Collections.emptyList()), listener);
          //then
          verify(changeNotifier, times(1)).registerListener(Constants.DEFAULT_GROUP, serviceName, wrapper);
          verify(proxy, times(1)).subscribe(serviceName, Constants.DEFAULT_GROUP, "");
      }
      
      @Test
      void testSubscribeFullName() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          EventListener listener = event -> {
          
          };
          //when
          client.subscribe(serviceName, groupName, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, groupName, Constants.NULL,
                  NamingSelectorFactory.newClusterSelector(Collections.emptyList()), listener);
          //then
          verify(changeNotifier, times(1)).registerListener(groupName, serviceName, wrapper);
          verify(proxy, times(1)).subscribe(serviceName, groupName, "");
      }
      
      @Test
      void testSubscribeWithServiceAndClusters() throws NacosException {
          //given
          String serviceName = "service1";
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          EventListener listener = event -> {
          
          };
          //when
          client.subscribe(serviceName, clusterList, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, Constants.DEFAULT_GROUP, Constants.NULL,
                  NamingSelectorFactory.newClusterSelector(clusterList), listener);
          //then
          verify(changeNotifier, times(1)).registerListener(Constants.DEFAULT_GROUP, serviceName, wrapper);
          verify(proxy, times(1)).subscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL);
      }
      
      @Test
      void testSubscribeWithFullNameAndClusters() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          EventListener listener = event -> {
          
          };
          //when
          client.subscribe(serviceName, groupName, clusterList, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, groupName,
                  getUniqueClusterString(clusterList), NamingSelectorFactory.newClusterSelector(clusterList), listener);
          //then
          verify(changeNotifier, times(1)).registerListener(groupName, serviceName, wrapper);
          verify(proxy, times(1)).subscribe(serviceName, groupName, Constants.NULL);
      }
      
      @Test
      public void testSubscribeWithServiceAndCustomSelector() throws NacosException {
          String serviceName = "service1";
          EventListener listener = event -> {
          
          };
          //when
          client.subscribe(serviceName, NamingSelectorFactory.HEALTHY_SELECTOR, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, Constants.DEFAULT_GROUP, Constants.NULL,
                  NamingSelectorFactory.HEALTHY_SELECTOR, listener);
          //then
          verify(changeNotifier, times(1)).registerListener(Constants.DEFAULT_GROUP, serviceName, wrapper);
          verify(proxy, times(1)).subscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL);
      }
      
      @Test
      public void testSubscribeWithFullNameAndCustomSelector() throws NacosException {
          String serviceName = "service1";
          String groupName = "group1";
          EventListener listener = event -> {
          
          };
          //when
          client.subscribe(serviceName, groupName, NamingSelectorFactory.HEALTHY_SELECTOR, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, groupName, Constants.NULL,
                  NamingSelectorFactory.HEALTHY_SELECTOR, listener);
          //then
          verify(changeNotifier, times(1)).registerListener(groupName, serviceName, wrapper);
          verify(proxy, times(1)).subscribe(serviceName, groupName, Constants.NULL);
      }
      
      @Test
      void testSubscribeWithNullListener() throws NacosException {
          String serviceName = "service1";
          String groupName = "group1";
          //when
          client.subscribe(serviceName, groupName, null);
          //then
          verify(changeNotifier, never()).registerListener(groupName, serviceName,
                  new NamingSelectorWrapper(NamingSelectorFactory.newIpSelector(""), null));
          verify(proxy, never()).subscribe(serviceName, groupName, "");
          
      }
      
      @Test
      void testSubscribeDuplicate() throws NacosException {
          String serviceName = "service1";
          when(proxy.isSubscribed(serviceName, Constants.DEFAULT_GROUP, StringUtils.EMPTY)).thenReturn(true);
          ServiceInfo serviceInfo = new ServiceInfo(Constants.DEFAULT_GROUP + "@@" + serviceName);
          serviceInfo.addHost(new Instance());
          when(serviceInfoHolder.getServiceInfo(serviceName, Constants.DEFAULT_GROUP)).thenReturn(serviceInfo);
          final AtomicBoolean flag = new AtomicBoolean(false);
          client.subscribe(serviceName, event -> flag.set(true));
          assertTrue(flag.get());
      }
      
      @Test
      void testUnSubscribeOnlyService() throws NacosException {
          //given
          String serviceName = "service1";
          EventListener listener = event -> {
          
          };
          when(changeNotifier.isSubscribed(Constants.DEFAULT_GROUP, serviceName)).thenReturn(false);
          //when
          client.unsubscribe(serviceName, listener);
          //then
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(
                  NamingSelectorFactory.newClusterSelector(Collections.emptyList()), listener);
          verify(changeNotifier, times(1)).deregisterListener(Constants.DEFAULT_GROUP, serviceName, wrapper);
          verify(proxy, times(1)).unsubscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL);
      }
      
      @Test
      void testUnSubscribeFullName() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          EventListener listener = event -> {
          
          };
          when(changeNotifier.isSubscribed(groupName, serviceName)).thenReturn(false);
          
          //when
          client.unsubscribe(serviceName, groupName, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(
                  NamingSelectorFactory.newClusterSelector(Collections.emptyList()), listener);
          //then
          verify(changeNotifier, times(1)).deregisterListener(groupName, serviceName, wrapper);
          verify(proxy, times(1)).unsubscribe(serviceName, groupName, Constants.NULL);
      }
      
      @Test
      void testUnSubscribeWithServiceAndClusters() throws NacosException {
          //given
          String serviceName = "service1";
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          EventListener listener = event -> {
          
          };
          when(changeNotifier.isSubscribed(Constants.DEFAULT_GROUP, serviceName)).thenReturn(false);
          
          //when
          client.unsubscribe(serviceName, clusterList, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(NamingSelectorFactory.newClusterSelector(clusterList),
                  listener);
          //then
          verify(changeNotifier, times(1)).deregisterListener(Constants.DEFAULT_GROUP, serviceName, wrapper);
          verify(proxy, times(1)).unsubscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL);
      }
      
      @Test
      void testUnSubscribeWithFullNameAndClusters() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          List<String> clusterList = Arrays.asList("cluster1", "cluster2");
          EventListener listener = event -> {
          
          };
          when(changeNotifier.isSubscribed(groupName, serviceName)).thenReturn(false);
          
          //when
          client.unsubscribe(serviceName, groupName, clusterList, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(NamingSelectorFactory.newClusterSelector(clusterList),
                  listener);
          //then
          verify(changeNotifier, times(1)).deregisterListener(groupName, serviceName, wrapper);
          verify(proxy, times(1)).unsubscribe(serviceName, groupName, Constants.NULL);
      }
      
      @Test
      public void testUnSubscribeWithServiceAndCustomSelector() throws NacosException {
          //given
          String serviceName = "service1";
          EventListener listener = event -> {
          
          };
          when(changeNotifier.isSubscribed(Constants.DEFAULT_GROUP, serviceName)).thenReturn(false);
          
          //when
          client.unsubscribe(serviceName, NamingSelectorFactory.HEALTHY_SELECTOR, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(NamingSelectorFactory.HEALTHY_SELECTOR, listener);
          //then
          verify(changeNotifier, times(1)).deregisterListener(Constants.DEFAULT_GROUP, serviceName, wrapper);
          verify(proxy, times(1)).unsubscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL);
      }
      
      @Test
      public void testUnSubscribeWithFullNameAndCustomSelector() throws NacosException {
          //given
          String serviceName = "service1";
          String groupName = "group1";
          EventListener listener = event -> {
          
          };
          when(changeNotifier.isSubscribed(groupName, serviceName)).thenReturn(false);
          
          //when
          client.unsubscribe(serviceName, groupName, NamingSelectorFactory.HEALTHY_SELECTOR, listener);
          NamingSelectorWrapper wrapper = new NamingSelectorWrapper(NamingSelectorFactory.HEALTHY_SELECTOR, listener);
          //then
          verify(changeNotifier, times(1)).deregisterListener(groupName, serviceName, wrapper);
          verify(proxy, times(1)).unsubscribe(serviceName, groupName, Constants.NULL);
      }
      
      @Test
      void testUnSubscribeWithNullListener() throws NacosException {
          String serviceName = "service1";
          String groupName = "group1";
          //when
          client.unsubscribe(serviceName, groupName, null);
          //then
          verify(changeNotifier, never()).deregisterListener(groupName, serviceName,
                  new NamingSelectorWrapper(NamingSelectorFactory.newIpSelector(""), null));
          verify(proxy, never()).unsubscribe(serviceName, groupName, "");
          
      }
      
      @Test
      void testGetServicesOfServer1() throws NacosException {
          //given
          int pageNo = 1;
          int pageSize = 10;
          //when
          client.getServicesOfServer(pageNo, pageSize);
          //then
          verify(proxy, times(1)).getServiceList(pageNo, pageSize, Constants.DEFAULT_GROUP, null);
      }
      
      @Test
      void testGetServicesOfServer2() throws NacosException {
          //given
          int pageNo = 1;
          int pageSize = 10;
          String groupName = "group1";
          //when
          client.getServicesOfServer(pageNo, pageSize, groupName);
          //then
          verify(proxy, times(1)).getServiceList(pageNo, pageSize, groupName, null);
      }
      
      @Test
      void testGetServicesOfServer3() throws NacosException {
          //given
          int pageNo = 1;
          int pageSize = 10;
          AbstractSelector selector = new AbstractSelector("aaa") {
              @Override
              public String getType() {
                  return super.getType();
              }
          };
          //when
          client.getServicesOfServer(pageNo, pageSize, selector);
          //then
          verify(proxy, times(1)).getServiceList(pageNo, pageSize, Constants.DEFAULT_GROUP, selector);
      }
      
      @Test
      void testGetServicesOfServer4() throws NacosException {
          //given
          int pageNo = 1;
          int pageSize = 10;
          String groupName = "group1";
          
          AbstractSelector selector = new AbstractSelector("aaa") {
              @Override
              public String getType() {
                  return super.getType();
              }
          };
          //when
          client.getServicesOfServer(pageNo, pageSize, groupName, selector);
          //then
          verify(proxy, times(1)).getServiceList(pageNo, pageSize, groupName, selector);
      }
      
      @Test
      void testGetSubscribeServices() {
          //when
          client.getSubscribeServices();
          //then
          verify(changeNotifier, times(1)).getSubscribeServices();
      }
      
      @Test
      void testGetServerStatus() {
          //given
          when(proxy.serverHealthy()).thenReturn(true);
          //when
          String serverStatus = client.getServerStatus();
          //then
          assertEquals("UP", serverStatus);
      }
      
      @Test
      void testGetServerStatusFail() {
          //given
          when(proxy.serverHealthy()).thenReturn(false);
          //when
          String serverStatus = client.getServerStatus();
          //then
          assertEquals("DOWN", serverStatus);
      }
      
      @Test
      void testShutDown() throws NacosException {
          //when
          client.shutDown();
          //then
          verify(proxy, times(1)).shutdown();
      }
      
      @Test
      void testConstructorWithServerList() throws NacosException, NoSuchFieldException, IllegalAccessException {
          NacosNamingService namingService = new NacosNamingService("localhost");
          try {
              Field namespaceField = NacosNamingService.class.getDeclaredField("namespace");
              namespaceField.setAccessible(true);
              String namespace = (String) namespaceField.get(namingService);
              assertEquals(UtilAndComs.DEFAULT_NAMESPACE_ID, namespace);
          } finally {
              namingService.shutDown();
          }
      }
      
      private Instance mockInstance(String clusterName, boolean healthy) {
          Instance instance = new Instance();
          instance.setClusterName(clusterName);
          instance.setHealthy(healthy);
          return instance;
      }
  }