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

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


The following commit(s) were added to refs/heads/master by this push:
     new c8021dc8d7 feat: add common protocol entity check (#4097)
c8021dc8d7 is described below

commit c8021dc8d75d5dc06b2331b4555a48edc087857b
Author: shown <[email protected]>
AuthorDate: Wed Apr 1 08:15:57 2026 +0800

    feat: add common protocol entity check (#4097)
    
    Signed-off-by: yuluo-yx <[email protected]>
---
 .../entity/job/protocol/EurekaSdProtocol.java      |  15 ++-
 .../common/entity/job/protocol/FtpProtocol.java    |  20 ++-
 .../common/entity/job/protocol/ImapProtocol.java   |  20 ++-
 .../common/entity/job/protocol/NgqlProtocol.java   |  23 +++-
 .../common/entity/job/protocol/NtpProtocol.java    |  15 ++-
 .../common/entity/job/protocol/ScriptProtocol.java |  25 +++-
 .../common/entity/job/protocol/SmtpProtocol.java   |  12 +-
 .../common/entity/job/protocol/SnmpProtocol.java   |  54 +++++++-
 .../entity/job/protocol/ZookeeperSdProtocol.java   |  10 +-
 .../entity/job/protocol/EurekaSdProtocolTest.java  |  66 ++++++++++
 .../entity/job/protocol/FtpProtocolTest.java       |  98 ++++++++++++++
 .../entity/job/protocol/ImapProtocolTest.java      |  93 +++++++++++++
 .../entity/job/protocol/NgqlProtocolTest.java      |  97 ++++++++++++++
 .../entity/job/protocol/NtpProtocolTest.java       |  80 ++++++++++++
 .../entity/job/protocol/ScriptProtocolTest.java    |  91 +++++++++++++
 .../entity/job/protocol/SmtpProtocolTest.java      |  88 +++++++++++++
 .../entity/job/protocol/SnmpProtocolTest.java      | 144 +++++++++++++++++++++
 .../job/protocol/ZookeeperSdProtocolTest.java      |  71 ++++++++++
 18 files changed, 997 insertions(+), 25 deletions(-)

diff --git 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/EurekaSdProtocol.java
 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/EurekaSdProtocol.java
index 0c8cf93f76..e75533021c 100644
--- 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/EurekaSdProtocol.java
+++ 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/EurekaSdProtocol.java
@@ -17,10 +17,12 @@
 
 package org.apache.hertzbeat.common.entity.job.protocol;
 
+import java.net.URI;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
 
 /**
  * eureka sd protocol
@@ -35,8 +37,15 @@ public class EurekaSdProtocol implements Protocol{
 
     @Override
     public boolean isInvalid() {
-
-        // todo: add
-        return true;
+        if (StringUtils.isBlank(url)) {
+            return true;
+        }
+        try {
+            URI uri = URI.create(url.trim());
+            return StringUtils.isBlank(uri.getHost())
+                    || (!"http".equalsIgnoreCase(uri.getScheme()) && 
!"https".equalsIgnoreCase(uri.getScheme()));
+        } catch (Exception e) {
+            return true;
+        }
     }
 }
diff --git 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/FtpProtocol.java
 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/FtpProtocol.java
index a3d2b525ab..3767620056 100644
--- 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/FtpProtocol.java
+++ 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/FtpProtocol.java
@@ -17,10 +17,15 @@
 
 package org.apache.hertzbeat.common.entity.job.protocol;
 
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validPort;
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validateIpDomain;
+
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hertzbeat.common.util.CommonUtil;
 
 /**
  * ftp protocol
@@ -68,8 +73,17 @@ public class FtpProtocol implements CommonRequestProtocol, 
Protocol {
 
     @Override
     public boolean isInvalid() {
-
-        // todo: add
-        return true;
+        if (!validateIpDomain(host) || !validPort(port) || 
StringUtils.isBlank(direction) || StringUtils.isBlank(timeout)) {
+            return true;
+        }
+        if (!CommonUtil.isNumeric(timeout)) {
+            return true;
+        }
+        if (StringUtils.isNotBlank(ssl)
+                && !"true".equalsIgnoreCase(ssl)
+                && !"false".equalsIgnoreCase(ssl)) {
+            return true;
+        }
+        return "true".equalsIgnoreCase(ssl) && 
StringUtils.isAnyBlank(username, password);
     }
 }
diff --git 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ImapProtocol.java
 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ImapProtocol.java
index 35e4a32854..f08103e41c 100644
--- 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ImapProtocol.java
+++ 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ImapProtocol.java
@@ -17,10 +17,15 @@
 
 package org.apache.hertzbeat.common.entity.job.protocol;
 
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validPort;
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validateIpDomain;
+
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hertzbeat.common.util.CommonUtil;
 
 /**
  * imap protocol
@@ -66,8 +71,17 @@ public class ImapProtocol implements CommonRequestProtocol, 
Protocol {
 
     @Override
     public boolean isInvalid() {
-
-        // todo: add
-        return true;
+        if (!validateIpDomain(host) || !validPort(port)) {
+            return true;
+        }
+        if (StringUtils.isAnyBlank(timeout, email, authorize, folderName)) {
+            return true;
+        }
+        if (!CommonUtil.isNumeric(timeout)) {
+            return true;
+        }
+        return StringUtils.isNotBlank(ssl)
+                && !"true".equalsIgnoreCase(ssl)
+                && !"false".equalsIgnoreCase(ssl);
     }
 }
diff --git 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/NgqlProtocol.java
 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/NgqlProtocol.java
index a5b775aac0..1072366bce 100644
--- 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/NgqlProtocol.java
+++ 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/NgqlProtocol.java
@@ -17,11 +17,17 @@
 
 package org.apache.hertzbeat.common.entity.job.protocol;
 
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validPort;
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validateIpDomain;
+
 import java.util.List;
+import java.util.Set;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hertzbeat.common.util.CommonUtil;
 
 /**
  * NGQL protocol
@@ -31,6 +37,7 @@ import lombok.NoArgsConstructor;
 @AllArgsConstructor
 @NoArgsConstructor
 public class NgqlProtocol implements CommonRequestProtocol, Protocol {
+    private static final Set<String> VALID_PARSE_TYPES = Set.of("oneRow", 
"multiRow", "filterCount", "columns");
 
     /**
      * IP ADDRESS OR DOMAIN NAME OF THE PEER HOST
@@ -74,8 +81,18 @@ public class NgqlProtocol implements CommonRequestProtocol, 
Protocol {
 
     @Override
     public boolean isInvalid() {
-
-        // todo: add
-        return true;
+        if (!validateIpDomain(host) || !validPort(port)) {
+            return true;
+        }
+        if (StringUtils.isAnyBlank(username, password, timeout, parseType)) {
+            return true;
+        }
+        if (!VALID_PARSE_TYPES.contains(parseType) || 
!CommonUtil.isNumeric(timeout)) {
+            return true;
+        }
+        if (commands == null || commands.isEmpty()) {
+            return true;
+        }
+        return commands.stream().anyMatch(StringUtils::isBlank);
     }
 }
diff --git 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/NtpProtocol.java
 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/NtpProtocol.java
index ee9178b02b..fd05ec0cef 100644
--- 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/NtpProtocol.java
+++ 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/NtpProtocol.java
@@ -17,10 +17,15 @@
 
 package org.apache.hertzbeat.common.entity.job.protocol;
 
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validPort;
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validateIpDomain;
+
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hertzbeat.common.util.CommonUtil;
 
 /**
  * Ntp Protocol
@@ -47,8 +52,12 @@ public class NtpProtocol implements CommonRequestProtocol, 
Protocol {
 
     @Override
     public boolean isInvalid() {
-
-        // todo: add
-        return true;
+        if (!validateIpDomain(host)) {
+            return true;
+        }
+        if (StringUtils.isNotBlank(port) && !validPort(port)) {
+            return true;
+        }
+        return StringUtils.isNotBlank(timeout) && 
!CommonUtil.isNumeric(timeout);
     }
 }
diff --git 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocol.java
 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocol.java
index 9876a751de..fb1877e7d4 100644
--- 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocol.java
+++ 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocol.java
@@ -17,10 +17,13 @@
 
 package org.apache.hertzbeat.common.entity.job.protocol;
 
+import java.nio.charset.Charset;
+import java.util.Set;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
 
 /**
  * script protocol
@@ -30,6 +33,9 @@ import lombok.NoArgsConstructor;
 @AllArgsConstructor
 @NoArgsConstructor
 public class ScriptProtocol implements CommonRequestProtocol, Protocol {
+    private static final Set<String> VALID_PARSE_TYPES = Set.of("oneRow", 
"multiRow", "netcat", "log");
+    private static final Set<String> VALID_SCRIPT_TOOLS = Set.of("bash", 
"cmd", "powershell");
+
     /**
      * OS charset
      */
@@ -72,8 +78,23 @@ public class ScriptProtocol implements 
CommonRequestProtocol, Protocol {
 
     @Override
     public boolean isInvalid() {
+        if (StringUtils.isBlank(charset) || !isSupportedCharset(charset)) {
+            return true;
+        }
+        if (StringUtils.isBlank(parseType) || 
!VALID_PARSE_TYPES.contains(parseType)) {
+            return true;
+        }
+        if (StringUtils.isBlank(scriptTool) || 
!VALID_SCRIPT_TOOLS.contains(scriptTool)) {
+            return true;
+        }
+        return StringUtils.isAllBlank(scriptCommand, scriptPath);
+    }
 
-        // todo: add
-        return true;
+    private boolean isSupportedCharset(String charsetName) {
+        try {
+            return Charset.isSupported(charsetName);
+        } catch (Exception e) {
+            return false;
+        }
     }
 }
diff --git 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/SmtpProtocol.java
 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/SmtpProtocol.java
index fa8d8a120b..a8236b2250 100644
--- 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/SmtpProtocol.java
+++ 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/SmtpProtocol.java
@@ -17,10 +17,15 @@
 
 package org.apache.hertzbeat.common.entity.job.protocol;
 
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validPort;
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validateIpDomain;
+
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hertzbeat.common.util.CommonUtil;
 
 /**
  * Smtp Protocol
@@ -57,8 +62,9 @@ public class SmtpProtocol implements CommonRequestProtocol, 
Protocol {
 
     @Override
     public boolean isInvalid() {
-
-        // todo: add
-        return true;
+        if (!validateIpDomain(host) || !validPort(port) || 
StringUtils.isBlank(email)) {
+            return true;
+        }
+        return StringUtils.isNotBlank(timeout) && 
!CommonUtil.isNumeric(timeout);
     }
 }
diff --git 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/SnmpProtocol.java
 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/SnmpProtocol.java
index 229cde701e..77428c9ba8 100644
--- 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/SnmpProtocol.java
+++ 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/SnmpProtocol.java
@@ -17,11 +17,16 @@
 
 package org.apache.hertzbeat.common.entity.job.protocol;
 
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validPort;
+import static org.apache.hertzbeat.common.util.IpDomainUtil.validateIpDomain;
+
 import java.util.Map;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hertzbeat.common.util.CommonUtil;
 
 /**
  * snmp Protocol configuration
@@ -31,6 +36,9 @@ import lombok.NoArgsConstructor;
 @AllArgsConstructor
 @NoArgsConstructor
 public class SnmpProtocol implements CommonRequestProtocol, Protocol {
+    private static final String OPERATION_GET = "get";
+    private static final String OPERATION_WALK = "walk";
+
     /**
      * IP ADDRESS OR DOMAIN NAME OF THE PEER HOST
      */
@@ -94,8 +102,50 @@ public class SnmpProtocol implements CommonRequestProtocol, 
Protocol {
 
     @Override
     public boolean isInvalid() {
+        if (!validateIpDomain(host) || !validPort(port) || 
StringUtils.isBlank(version)) {
+            return true;
+        }
+        if (StringUtils.isNotBlank(timeout) && !CommonUtil.isNumeric(timeout)) 
{
+            return true;
+        }
+        if (StringUtils.isNotBlank(operation)
+                && !OPERATION_GET.equalsIgnoreCase(operation)
+                && !OPERATION_WALK.equalsIgnoreCase(operation)) {
+            return true;
+        }
+        if (oids == null || oids.isEmpty()) {
+            return true;
+        }
+        for (Map.Entry<String, String> entry : oids.entrySet()) {
+            if (StringUtils.isAnyBlank(entry.getKey(), entry.getValue())) {
+                return true;
+            }
+        }
+        if (isVersion3()) {
+            return StringUtils.isAnyBlank(username, authPassphrase, 
privPassphrase);
+        }
+        if (!isVersion1Or2c()) {
+            return true;
+        }
+        return StringUtils.isBlank(community);
+    }
+
+    private boolean isVersion1Or2c() {
+        return isVersion1() || isVersion2c();
+    }
+
+    private boolean isVersion1() {
+        return "0".equalsIgnoreCase(version) || "v1".equalsIgnoreCase(version);
+    }
+
+    private boolean isVersion2c() {
+        return "1".equalsIgnoreCase(version)
+                || "2".equalsIgnoreCase(version)
+                || "2c".equalsIgnoreCase(version)
+                || "v2c".equalsIgnoreCase(version);
+    }
 
-        // todo: add
-        return true;
+    private boolean isVersion3() {
+        return "3".equalsIgnoreCase(version) || "v3".equalsIgnoreCase(version);
     }
 }
diff --git 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ZookeeperSdProtocol.java
 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ZookeeperSdProtocol.java
index 7b53f354b5..1799c878cb 100644
--- 
a/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ZookeeperSdProtocol.java
+++ 
b/hertzbeat-common-core/src/main/java/org/apache/hertzbeat/common/entity/job/protocol/ZookeeperSdProtocol.java
@@ -21,6 +21,7 @@ import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
 
 /**
  * Zookeeper service discovery protocol
@@ -37,8 +38,11 @@ public class ZookeeperSdProtocol implements Protocol{
 
     @Override
     public boolean isInvalid() {
-
-        // todo: add
-        return true;
+        if (StringUtils.isAnyBlank(url, pathPrefix)) {
+            return true;
+        }
+        return StringUtils.containsWhitespace(url)
+                || StringUtils.containsWhitespace(pathPrefix)
+                || !pathPrefix.startsWith("/");
     }
 }
diff --git 
a/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/EurekaSdProtocolTest.java
 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/EurekaSdProtocolTest.java
new file mode 100644
index 0000000000..634eaada61
--- /dev/null
+++ 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/EurekaSdProtocolTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.common.entity.job.protocol;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class EurekaSdProtocolTest {
+
+    @Test
+    void isInvalidValidHttpUrl() {
+        EurekaSdProtocol protocol = EurekaSdProtocol.builder()
+                .url("http://127.0.0.1:8761/eureka";)
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidValidHttpsUrl() {
+        EurekaSdProtocol protocol = EurekaSdProtocol.builder()
+                .url("https://eureka.example.com/eureka";)
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidBlankUrl() {
+        EurekaSdProtocol protocol = EurekaSdProtocol.builder()
+                .url("")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidUnsupportedScheme() {
+        EurekaSdProtocol protocol = EurekaSdProtocol.builder()
+                .url("ftp://eureka.example.com/eureka";)
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidMalformedUrl() {
+        EurekaSdProtocol protocol = EurekaSdProtocol.builder()
+                .url("http://";)
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+}
diff --git 
a/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/FtpProtocolTest.java
 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/FtpProtocolTest.java
new file mode 100644
index 0000000000..b17795b246
--- /dev/null
+++ 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/FtpProtocolTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.common.entity.job.protocol;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class FtpProtocolTest {
+
+    @Test
+    void isInvalidValidAnonymousFtp() {
+        FtpProtocol protocol = FtpProtocol.builder()
+                .host("ftp.example.com")
+                .port("21")
+                .direction("/")
+                .timeout("3000")
+                .ssl("false")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidValidSftp() {
+        FtpProtocol protocol = FtpProtocol.builder()
+                .host("sftp.example.com")
+                .port("22")
+                .direction("/data")
+                .timeout("3000")
+                .ssl("true")
+                .username("admin")
+                .password("secret")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidMissingDirection() {
+        FtpProtocol protocol = FtpProtocol.builder()
+                .host("ftp.example.com")
+                .port("21")
+                .timeout("3000")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidUnsupportedSslValue() {
+        FtpProtocol protocol = FtpProtocol.builder()
+                .host("ftp.example.com")
+                .port("21")
+                .direction("/")
+                .timeout("3000")
+                .ssl("yes")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidSftpWithoutPassword() {
+        FtpProtocol protocol = FtpProtocol.builder()
+                .host("sftp.example.com")
+                .port("22")
+                .direction("/data")
+                .timeout("3000")
+                .ssl("true")
+                .username("admin")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidInvalidTimeout() {
+        FtpProtocol protocol = FtpProtocol.builder()
+                .host("ftp.example.com")
+                .port("21")
+                .direction("/")
+                .timeout("abc")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+}
diff --git 
a/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/ImapProtocolTest.java
 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/ImapProtocolTest.java
new file mode 100644
index 0000000000..d567a6c875
--- /dev/null
+++ 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/ImapProtocolTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.common.entity.job.protocol;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class ImapProtocolTest {
+
+    @Test
+    void isInvalidValidProtocol() {
+        ImapProtocol protocol = ImapProtocol.builder()
+                .host("imap.example.com")
+                .port("993")
+                .timeout("6000")
+                .ssl("true")
+                .email("[email protected]")
+                .authorize("auth-code")
+                .folderName("INBOX")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidMissingFolderName() {
+        ImapProtocol protocol = ImapProtocol.builder()
+                .host("imap.example.com")
+                .port("993")
+                .timeout("6000")
+                .email("[email protected]")
+                .authorize("auth-code")
+                .folderName("")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidUnsupportedSslValue() {
+        ImapProtocol protocol = ImapProtocol.builder()
+                .host("imap.example.com")
+                .port("993")
+                .timeout("6000")
+                .ssl("yes")
+                .email("[email protected]")
+                .authorize("auth-code")
+                .folderName("INBOX")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidInvalidTimeout() {
+        ImapProtocol protocol = ImapProtocol.builder()
+                .host("imap.example.com")
+                .port("993")
+                .timeout("abc")
+                .email("[email protected]")
+                .authorize("auth-code")
+                .folderName("INBOX")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidInvalidHost() {
+        ImapProtocol protocol = ImapProtocol.builder()
+                .host("")
+                .port("993")
+                .timeout("6000")
+                .email("[email protected]")
+                .authorize("auth-code")
+                .folderName("INBOX")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+}
diff --git 
a/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/NgqlProtocolTest.java
 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/NgqlProtocolTest.java
new file mode 100644
index 0000000000..98e89eaf1e
--- /dev/null
+++ 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/NgqlProtocolTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.common.entity.job.protocol;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class NgqlProtocolTest {
+
+    @Test
+    void isInvalidValidProtocol() {
+        NgqlProtocol protocol = NgqlProtocol.builder()
+                .host("127.0.0.1")
+                .port("9669")
+                .username("root")
+                .password("nebula")
+                .timeout("6000")
+                .parseType("oneRow")
+                .commands(List.of("SHOW HOSTS;"))
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidUnsupportedParseType() {
+        NgqlProtocol protocol = NgqlProtocol.builder()
+                .host("127.0.0.1")
+                .port("9669")
+                .username("root")
+                .password("nebula")
+                .timeout("6000")
+                .parseType("json")
+                .commands(List.of("SHOW HOSTS;"))
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidEmptyCommands() {
+        NgqlProtocol protocol = NgqlProtocol.builder()
+                .host("127.0.0.1")
+                .port("9669")
+                .username("root")
+                .password("nebula")
+                .timeout("6000")
+                .parseType("oneRow")
+                .commands(List.of())
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidBlankCommandItem() {
+        NgqlProtocol protocol = NgqlProtocol.builder()
+                .host("127.0.0.1")
+                .port("9669")
+                .username("root")
+                .password("nebula")
+                .timeout("6000")
+                .parseType("oneRow")
+                .commands(List.of(" "))
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidInvalidTimeout() {
+        NgqlProtocol protocol = NgqlProtocol.builder()
+                .host("127.0.0.1")
+                .port("9669")
+                .username("root")
+                .password("nebula")
+                .timeout("abc")
+                .parseType("oneRow")
+                .commands(List.of("SHOW HOSTS;"))
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+}
diff --git 
a/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/NtpProtocolTest.java
 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/NtpProtocolTest.java
new file mode 100644
index 0000000000..a000b79c2a
--- /dev/null
+++ 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/NtpProtocolTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.common.entity.job.protocol;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class NtpProtocolTest {
+
+    @Test
+    void isInvalidValidProtocolWithoutPort() {
+        NtpProtocol protocol = NtpProtocol.builder()
+                .host("pool.ntp.org")
+                .timeout("3000")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidValidProtocolWithPort() {
+        NtpProtocol protocol = NtpProtocol.builder()
+                .host("192.168.1.1")
+                .port("123")
+                .timeout("3000")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidValidProtocolWithoutTimeout() {
+        NtpProtocol protocol = NtpProtocol.builder()
+                .host("pool.ntp.org")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidInvalidHost() {
+        NtpProtocol protocol = NtpProtocol.builder()
+                .host("")
+                .timeout("3000")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidInvalidPort() {
+        NtpProtocol protocol = NtpProtocol.builder()
+                .host("pool.ntp.org")
+                .port("70000")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidInvalidTimeout() {
+        NtpProtocol protocol = NtpProtocol.builder()
+                .host("pool.ntp.org")
+                .timeout("abc")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+}
diff --git 
a/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocolTest.java
 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocolTest.java
new file mode 100644
index 0000000000..3bf8dd3c29
--- /dev/null
+++ 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/ScriptProtocolTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.common.entity.job.protocol;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class ScriptProtocolTest {
+
+    @Test
+    void isInvalidValidScriptCommand() {
+        ScriptProtocol protocol = ScriptProtocol.builder()
+                .charset("UTF-8")
+                .parseType("oneRow")
+                .scriptTool("bash")
+                .scriptCommand("echo test")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidValidScriptPath() {
+        ScriptProtocol protocol = ScriptProtocol.builder()
+                .charset("UTF-8")
+                .parseType("multiRow")
+                .scriptTool("powershell")
+                .scriptPath("/tmp/test.ps1")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidUnsupportedCharset() {
+        ScriptProtocol protocol = ScriptProtocol.builder()
+                .charset("not-a-charset")
+                .parseType("oneRow")
+                .scriptTool("bash")
+                .scriptCommand("echo test")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidUnsupportedParseType() {
+        ScriptProtocol protocol = ScriptProtocol.builder()
+                .charset("UTF-8")
+                .parseType("json")
+                .scriptTool("bash")
+                .scriptCommand("echo test")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidUnsupportedScriptTool() {
+        ScriptProtocol protocol = ScriptProtocol.builder()
+                .charset("UTF-8")
+                .parseType("oneRow")
+                .scriptTool("sh")
+                .scriptCommand("echo test")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidMissingCommandAndPath() {
+        ScriptProtocol protocol = ScriptProtocol.builder()
+                .charset("UTF-8")
+                .parseType("oneRow")
+                .scriptTool("bash")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+}
diff --git 
a/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/SmtpProtocolTest.java
 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/SmtpProtocolTest.java
new file mode 100644
index 0000000000..e47b3feae7
--- /dev/null
+++ 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/SmtpProtocolTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.common.entity.job.protocol;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class SmtpProtocolTest {
+
+    @Test
+    void isInvalidValidProtocol() {
+        SmtpProtocol protocol = SmtpProtocol.builder()
+                .host("smtp.example.com")
+                .port("25")
+                .email("[email protected]")
+                .timeout("3000")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidValidProtocolWithoutTimeout() {
+        SmtpProtocol protocol = SmtpProtocol.builder()
+                .host("smtp.example.com")
+                .port("25")
+                .email("[email protected]")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidBlankEmail() {
+        SmtpProtocol protocol = SmtpProtocol.builder()
+                .host("smtp.example.com")
+                .port("25")
+                .email("")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidInvalidHost() {
+        SmtpProtocol protocol = SmtpProtocol.builder()
+                .host("")
+                .port("25")
+                .email("[email protected]")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidInvalidPort() {
+        SmtpProtocol protocol = SmtpProtocol.builder()
+                .host("smtp.example.com")
+                .port("70000")
+                .email("[email protected]")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidInvalidTimeout() {
+        SmtpProtocol protocol = SmtpProtocol.builder()
+                .host("smtp.example.com")
+                .port("25")
+                .email("[email protected]")
+                .timeout("abc")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+}
diff --git 
a/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/SnmpProtocolTest.java
 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/SnmpProtocolTest.java
new file mode 100644
index 0000000000..34fc995332
--- /dev/null
+++ 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/SnmpProtocolTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.common.entity.job.protocol;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+class SnmpProtocolTest {
+
+    @Test
+    void isInvalidValidV2cProtocol() {
+        SnmpProtocol protocol = SnmpProtocol.builder()
+                .host("192.168.1.1")
+                .port("161")
+                .version("2c")
+                .community("public")
+                .oids(validOids())
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidValidNumericV1Protocol() {
+        SnmpProtocol protocol = SnmpProtocol.builder()
+                .host("192.168.1.1")
+                .port("161")
+                .version("0")
+                .community("public")
+                .oids(validOids())
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidValidNumericV2cProtocol() {
+        SnmpProtocol protocol = SnmpProtocol.builder()
+                .host("192.168.1.1")
+                .port("161")
+                .version("1")
+                .community("public")
+                .oids(validOids())
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidValidV3Protocol() {
+        SnmpProtocol protocol = SnmpProtocol.builder()
+                .host("192.168.1.1")
+                .port("161")
+                .version("3")
+                .username("user")
+                .authPassphrase("authPass")
+                .privPassphrase("privPass")
+                .oids(validOids())
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidMissingCommunityForV2c() {
+        SnmpProtocol protocol = SnmpProtocol.builder()
+                .host("192.168.1.1")
+                .port("161")
+                .version("2c")
+                .oids(validOids())
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidMissingCredentialsForV3() {
+        SnmpProtocol protocol = SnmpProtocol.builder()
+                .host("192.168.1.1")
+                .port("161")
+                .version("v3")
+                .username("user")
+                .oids(validOids())
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidUnsupportedOperation() {
+        SnmpProtocol protocol = SnmpProtocol.builder()
+                .host("192.168.1.1")
+                .port("161")
+                .version("2c")
+                .community("public")
+                .operation("bulk")
+                .oids(validOids())
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidEmptyOids() {
+        SnmpProtocol protocol = SnmpProtocol.builder()
+                .host("192.168.1.1")
+                .port("161")
+                .version("2c")
+                .community("public")
+                .oids(Map.of())
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidUnsupportedVersion() {
+        SnmpProtocol protocol = SnmpProtocol.builder()
+                .host("192.168.1.1")
+                .port("161")
+                .version("4")
+                .community("public")
+                .oids(validOids())
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    private Map<String, String> validOids() {
+        Map<String, String> oids = new HashMap<>();
+        oids.put("name", "1.3.6.1.2.1.1.5.0");
+        return oids;
+    }
+}
diff --git 
a/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/ZookeeperSdProtocolTest.java
 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/ZookeeperSdProtocolTest.java
new file mode 100644
index 0000000000..1c10ec7319
--- /dev/null
+++ 
b/hertzbeat-common-core/src/test/java/org/apache/hertzbeat/common/entity/job/protocol/ZookeeperSdProtocolTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.common.entity.job.protocol;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class ZookeeperSdProtocolTest {
+
+    @Test
+    void isInvalidValidProtocol() {
+        ZookeeperSdProtocol protocol = ZookeeperSdProtocol.builder()
+                .url("zk1:2181,zk2:2181")
+                .pathPrefix("/services")
+                .build();
+        assertFalse(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidBlankUrl() {
+        ZookeeperSdProtocol protocol = ZookeeperSdProtocol.builder()
+                .url("")
+                .pathPrefix("/services")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidBlankPathPrefix() {
+        ZookeeperSdProtocol protocol = ZookeeperSdProtocol.builder()
+                .url("localhost:2181")
+                .pathPrefix("")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidPathPrefixWithoutSlash() {
+        ZookeeperSdProtocol protocol = ZookeeperSdProtocol.builder()
+                .url("localhost:2181")
+                .pathPrefix("services")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+
+    @Test
+    void isInvalidPathPrefixWithWhitespace() {
+        ZookeeperSdProtocol protocol = ZookeeperSdProtocol.builder()
+                .url("localhost:2181")
+                .pathPrefix("/service path")
+                .build();
+        assertTrue(protocol.isInvalid());
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to