Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class NginxCollectImpl extends AbstractCollect {
@Override
public void preCheck(Metrics metrics) throws IllegalArgumentException {
final NginxProtocol nginxProtocol;
if (metrics == null || (nginxProtocol = metrics.getNginx()) == null || nginxProtocol.isInValid()) {
if (metrics == null || (nginxProtocol = metrics.getNginx()) == null || nginxProtocol.isInvalid()) {
throw new IllegalArgumentException("Nginx collect must has nginx params");
}
}
Expand Down Expand Up @@ -141,7 +141,7 @@ private HttpUriRequest createHttpRequest(NginxProtocol nginxProtocol) {
RequestBuilder requestBuilder = RequestBuilder.get();
String portWithUri = nginxProtocol.getPort() + CollectUtil.replaceUriSpecialChar(nginxProtocol.getUrl());
String host = nginxProtocol.getHost();

if (IpDomainUtil.isHasSchema(host)) {
requestBuilder.setUri(host + ":" + portWithUri);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ public class RegistryImpl extends AbstractCollect {

@Override
public void preCheck(Metrics metrics) throws IllegalArgumentException {

RegistryProtocol registryProtocol = metrics.getRegistry();

if (Objects.isNull(registryProtocol) || registryProtocol.isInvalid()){
throw new IllegalArgumentException("registry collect must have a valid registry protocol param! ");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,17 @@ public ConnectConfig buildConnectConfig(RegistryProtocol registryProtocol) {
@Override
public void initClient(ConnectConfig connectConfig) {
try {

localConnectConfig = connectConfig;
namingService = NamingFactory.createNamingService(connectConfig.getHost() + ":" + connectConfig.getPort());

// Perform a synchronous probe to verify connectivity eagerly,
// because NamingFactory.createNamingService() establishes the TCP
// connection in a background thread and getServerStatus() returns
// "UP" by default before that thread finishes.
namingService.getServicesOfServer(0, 1);
} catch (NacosException exception) {
throw new RuntimeException("Failed to init namingService");
throw new RuntimeException("Failed to connect to Nacos server: " + exception.getErrMsg());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@
*/
@Slf4j
public class NacosSdCollectImpl extends AbstractCollect {

/**
* Client management to interact with discovery services
*/
private final DiscoveryClientManagement discoveryClientManagement = new DiscoveryClientManagement();

@Override
public void preCheck(Metrics metrics) throws IllegalArgumentException {
// Validate the required configuration is present
Expand All @@ -55,7 +55,7 @@ public void preCheck(Metrics metrics) throws IllegalArgumentException {
throw new IllegalArgumentException("Nacos service discovery monitoring, the config is invalid");
}
}

@Override
public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
// Create Registry protocol from NacosSd protocol
Expand All @@ -74,13 +74,13 @@ public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
builder.setMsg("Failed to get Nacos discovery client");
return;
}

// Get all services registered in Nacos
List<ServiceInstance> services = discoveryClient.getServices();
if (CollectionUtils.isEmpty(services)) {
return;
}

// Populate the response data with service information
services.forEach(service -> {
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
Expand All @@ -106,7 +106,7 @@ public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
}
}
}

@Override
public String supportProtocol() {
return DispatchConstants.PROTOCOL_NACOS_SD;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
/*
* 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.hertzbeat.collector.collect.registry.discovery.impl;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hertzbeat.collector.collect.registry.constant.DiscoveryClientHealthStatus;
import org.apache.hertzbeat.collector.collect.registry.discovery.entity.ConnectConfig;
import org.apache.hertzbeat.collector.collect.registry.discovery.entity.ServerInfo;
import org.apache.hertzbeat.collector.collect.registry.discovery.entity.ServiceInstance;
import org.apache.hertzbeat.common.entity.job.protocol.RegistryProtocol;
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.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;

/**
* Test case for {@link NacosDiscoveryClient}
*/
@ExtendWith(MockitoExtension.class)
class NacosDiscoveryClientTest {

private NacosDiscoveryClient nacosDiscoveryClient;

@Mock
private NamingService namingService;

private static final String HOST = "127.0.0.1";
private static final int PORT = 8848;

@BeforeEach
void setUp() {
nacosDiscoveryClient = new NacosDiscoveryClient();
}

@Test
void testBuildConnectConfig() {
RegistryProtocol protocol = RegistryProtocol.builder()
.host(HOST)
.port(String.valueOf(PORT))
.build();

ConnectConfig config = nacosDiscoveryClient.buildConnectConfig(protocol);

assertNotNull(config);
assertEquals(HOST, config.getHost());
assertEquals(PORT, config.getPort());
}

@Test
void testInitClientSuccess() throws NacosException {
ConnectConfig config = ConnectConfig.builder().host(HOST).port(PORT).build();

try (MockedStatic<NamingFactory> mockedFactory = Mockito.mockStatic(NamingFactory.class)) {
mockedFactory.when(() -> NamingFactory.createNamingService(HOST + ":" + PORT))
.thenReturn(namingService);
ListView<String> emptyView = new ListView<>();
emptyView.setData(Collections.emptyList());
when(namingService.getServicesOfServer(0, 1)).thenReturn(emptyView);

nacosDiscoveryClient.initClient(config);

mockedFactory.verify(() -> NamingFactory.createNamingService(HOST + ":" + PORT));
verify(namingService).getServicesOfServer(0, 1);
}
}

@Test
void testInitClientFailedOnCreate() throws NacosException {
ConnectConfig config = ConnectConfig.builder().host(HOST).port(PORT).build();

try (MockedStatic<NamingFactory> mockedFactory = Mockito.mockStatic(NamingFactory.class)) {
mockedFactory.when(() -> NamingFactory.createNamingService(anyString()))
.thenThrow(new NacosException(500, "connection refused"));

assertThrows(RuntimeException.class, () -> nacosDiscoveryClient.initClient(config));
}
}

@Test
void testInitClientFailedOnProbe() throws NacosException {
ConnectConfig config = ConnectConfig.builder().host(HOST).port(PORT).build();

try (MockedStatic<NamingFactory> mockedFactory = Mockito.mockStatic(NamingFactory.class)) {
mockedFactory.when(() -> NamingFactory.createNamingService(anyString()))
.thenReturn(namingService);
when(namingService.getServicesOfServer(0, 1))
.thenThrow(new NacosException(500, "host unreachable"));

RuntimeException ex = assertThrows(RuntimeException.class, () -> nacosDiscoveryClient.initClient(config));
assertTrue(ex.getMessage().contains("Failed to connect"));
}
}

@Test
void testGetServerInfoWithNullNamingService() {
assertThrows(NullPointerException.class, () -> nacosDiscoveryClient.getServerInfo());
}

@Test
void testGetServerInfoWhenHealthy() {
injectNamingServiceAndConfig();
when(namingService.getServerStatus()).thenReturn(DiscoveryClientHealthStatus.UP);

ServerInfo serverInfo = nacosDiscoveryClient.getServerInfo();

assertNotNull(serverInfo);
assertEquals(HOST, serverInfo.getAddress());
assertEquals(String.valueOf(PORT), serverInfo.getPort());
}

@Test
void testGetServerInfoWhenNotHealthy() {
injectNamingServiceAndConfig();
when(namingService.getServerStatus()).thenReturn(DiscoveryClientHealthStatus.DOWN);

assertThrows(RuntimeException.class, () -> nacosDiscoveryClient.getServerInfo());
}

@Test
void testGetServicesWithNullNamingService() {
List<ServiceInstance> result = nacosDiscoveryClient.getServices();

assertNotNull(result);
assertTrue(result.isEmpty());
}

@Test
void testGetServicesWhenNotHealthy() {
injectNamingServiceAndConfig();
when(namingService.getServerStatus()).thenReturn(DiscoveryClientHealthStatus.DOWN);

List<ServiceInstance> result = nacosDiscoveryClient.getServices();

assertNotNull(result);
assertTrue(result.isEmpty());
}

@Test
void testGetServicesSuccess() throws NacosException {
injectNamingServiceAndConfig();
when(namingService.getServerStatus()).thenReturn(DiscoveryClientHealthStatus.UP);

ListView<String> serviceNames = new ListView<>();
serviceNames.setData(Collections.singletonList("test-service"));
when(namingService.getServicesOfServer(0, 9999)).thenReturn(serviceNames);

Instance instance = new Instance();
instance.setInstanceId("inst-1");
instance.setServiceName("test-service");
instance.setIp("192.168.1.1");
instance.setPort(9090);
instance.setWeight(1.0);
Map<String, String> metadata = new HashMap<>();
metadata.put("version", "1.0");
instance.setMetadata(metadata);
instance.setHealthy(true);

when(namingService.getAllInstances("test-service")).thenReturn(Collections.singletonList(instance));

List<ServiceInstance> result = nacosDiscoveryClient.getServices();

assertNotNull(result);
assertEquals(1, result.size());
ServiceInstance serviceInstance = result.get(0);
assertEquals("inst-1", serviceInstance.getServiceId());
assertEquals("test-service", serviceInstance.getServiceName());
assertEquals("192.168.1.1", serviceInstance.getAddress());
assertEquals(9090, serviceInstance.getPort());
assertEquals(DiscoveryClientHealthStatus.UP, serviceInstance.getHealthStatus());
}

@Test
void testGetServicesWithUnhealthyInstance() throws NacosException {
injectNamingServiceAndConfig();
when(namingService.getServerStatus()).thenReturn(DiscoveryClientHealthStatus.UP);

ListView<String> serviceNames = new ListView<>();
serviceNames.setData(Collections.singletonList("test-service"));
when(namingService.getServicesOfServer(0, 9999)).thenReturn(serviceNames);

Instance instance = new Instance();
instance.setInstanceId("inst-down");
instance.setServiceName("test-service");
instance.setIp("10.0.0.1");
instance.setPort(8080);
instance.setWeight(1.0);
instance.setHealthy(false);

when(namingService.getAllInstances("test-service")).thenReturn(Collections.singletonList(instance));

List<ServiceInstance> result = nacosDiscoveryClient.getServices();

assertEquals(1, result.size());
assertEquals(DiscoveryClientHealthStatus.DOWN, result.get(0).getHealthStatus());
}

@Test
void testGetServicesThrowsNacosException() throws NacosException {
injectNamingServiceAndConfig();
when(namingService.getServerStatus()).thenReturn(DiscoveryClientHealthStatus.UP);
when(namingService.getServicesOfServer(anyInt(), anyInt()))
.thenThrow(new NacosException(500, "server error"));

assertThrows(RuntimeException.class, () -> nacosDiscoveryClient.getServices());
}

@Test
void testHealthCheckReturnsTrue() {
injectNamingServiceAndConfig();
when(namingService.getServerStatus()).thenReturn(DiscoveryClientHealthStatus.UP);

assertTrue(nacosDiscoveryClient.healthCheck());
}

@Test
void testHealthCheckReturnsFalse() {
injectNamingServiceAndConfig();
when(namingService.getServerStatus()).thenReturn(DiscoveryClientHealthStatus.DOWN);

assertFalse(nacosDiscoveryClient.healthCheck());
}

@Test
void testCloseWithNullNamingService() {
nacosDiscoveryClient.close();
}

@Test
void testCloseSuccess() throws NacosException {
injectNamingServiceAndConfig();

nacosDiscoveryClient.close();

verify(namingService).shutDown();
}

@Test
void testCloseThrowsNacosException() throws NacosException {
injectNamingServiceAndConfig();
doThrow(new NacosException(500, "shutdown error")).when(namingService).shutDown();

nacosDiscoveryClient.close();

verify(namingService).shutDown();
}

private void injectNamingServiceAndConfig() {
ReflectionTestUtils.setField(nacosDiscoveryClient, "namingService", namingService);
ConnectConfig config = ConnectConfig.builder().host(HOST).port(PORT).build();
ReflectionTestUtils.setField(nacosDiscoveryClient, "localConnectConfig", config);
}
}
Loading
Loading