Repository: robustmq/robustmq (language: rust)

Issue:
MINOR: Add validation to the get_resource_config method in GrpcPlacementService
### What type of enhancement is this?

Refactor

### What does the enhancement do?

Add validation to the get_resource_configmethod in GrpcPlacementService and fill in the test case. 

Tips:
you can implement the `ValidateExt` trait on `GetResourceConfigRequest`, then invoke ` validate_ext ` function to achieve the validation functionality.

- you can locate the ValidateExt trait in the file: src\placement-center\src\server\grpc\validate.rs
- you need to add validation to the node_list method in this file:  src\placement-center\src\server\grpc\service_placement.rs
- you need to fill in the test case in this file: src\grpc-clients\tests\placement_test.rs

reference:
- https://github.com/robustmq/robustmq/pull/548)https://github.com/robustmq/robustmq/pull/548

### Implementation challenges

_No response_


Discussion / hints:
mark

Fix patch:
diff --git a/src/placement-center/src/server/grpc/service_placement.rs b/src/placement-center/src/server/grpc/service_placement.rs
index bace2f5f7..b9cddea91 100644
--- a/src/placement-center/src/server/grpc/service_placement.rs
+++ b/src/placement-center/src/server/grpc/service_placement.rs
@@ -238,6 +238,8 @@ impl PlacementCenterService for GrpcPlacementService {
         request: Request<GetResourceConfigRequest>,
     ) -> Result<Response<GetResourceConfigReply>, Status> {
         let req = request.into_inner();
+        let _ = req.validate_ext()?;
+
         let storage = ResourceConfigStorage::new(self.rocksdb_engine_handler.clone());
         match storage.get(req.cluster_name, req.resources) {
             Ok(data) => {
diff --git a/src/placement-center/src/server/grpc/validate.rs b/src/placement-center/src/server/grpc/validate.rs
index a0f5327ff..a8c777b13 100644
--- a/src/placement-center/src/server/grpc/validate.rs
+++ b/src/placement-center/src/server/grpc/validate.rs
@@ -16,8 +16,8 @@ use std::net::{IpAddr, SocketAddr};
 
 use common_base::error::common::CommonError;
 use protocol::placement_center::placement_center_inner::{
-    ClusterType, DeleteIdempotentDataRequest, RegisterNodeRequest, SetIdempotentDataRequest,
-    SetResourceConfigRequest, UnRegisterNodeRequest,
+    ClusterType, DeleteIdempotentDataRequest, GetResourceConfigRequest, RegisterNodeRequest,
+    SetIdempotentDataRequest, SetResourceConfigRequest, UnRegisterNodeRequest,
 };
 use tonic::Status;
 
@@ -127,6 +127,18 @@ impl ValidateExt for SetResourceConfigRequest {
     }
 }
 
+impl ValidateExt for GetResourceConfigRequest {
+    fn validate_ext(&self) -> Result<(), Status> {
+        if self.cluster_name.is_empty() {
+            return Err(Status::cancelled(
+                CommonError::ParameterCannotBeNull("cluster name".to_string()).to_string(),
+            ));
+        }
+
+        Ok(())
+    }
+}
+
 impl ValidateExt for SetIdempotentDataRequest {
     fn validate_ext(&self) -> Result<(), Status> {
         ensure_param_not_empty("cluster_name", &self.cluster_name)?;


Test patch:
diff --git a/scripts/unit-test.sh b/scripts/unit-test.sh
index 2c1bc108c..ac1fdfcff 100644
--- a/scripts/unit-test.sh
+++ b/scripts/unit-test.sh
@@ -12,8 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Run Cargo Test
-cargo nextest run --workspace --exclude robustmq-test
+# Run Cargo Test （exclude integration test modules）
+cargo nextest run --workspace --exclude robustmq-test --exclude grpc-clients --exclude cmd --exclude mqtt-bridge  --exclude mqtt-broker
 
  # Modules that are not suitable for unit testing
  # cmd
diff --git a/src/grpc-clients/tests/placement/mqtt/cluster_test.rs b/src/grpc-clients/tests/placement/mqtt/cluster_test.rs
index 72f8842dc..8b47f8a49 100644
--- a/src/grpc-clients/tests/placement/mqtt/cluster_test.rs
+++ b/src/grpc-clients/tests/placement/mqtt/cluster_test.rs
@@ -17,18 +17,18 @@ mod tests {
     use std::sync::Arc;
 
     use grpc_clients::placement::placement::call::{
-        cluster_status, delete_idempotent_data, register_node, set_resource_config, unregister_node,
+        cluster_status, delete_idempotent_data, get_resource_config, register_node,
+        set_resource_config, unregister_node,
     };
     use grpc_clients::pool::ClientPool;
     use protocol::placement_center::placement_center_inner::{
-        ClusterStatusRequest, ClusterType, DeleteIdempotentDataRequest, RegisterNodeRequest,
-        SetResourceConfigRequest, UnRegisterNodeRequest,
+        ClusterStatusRequest, ClusterType, DeleteIdempotentDataRequest, GetResourceConfigRequest,
+        RegisterNodeRequest, SetResourceConfigRequest, UnRegisterNodeRequest,
     };
 
     use crate::common::get_placement_addr;
 
     #[tokio::test]
-    #[ignore]
     async fn register_node_test() {
         let client_pool: Arc<ClientPool> = Arc::new(ClientPool::new(1));
         let addrs = vec![get_placement_addr()];
@@ -102,7 +102,6 @@ mod tests {
     }
 
     #[tokio::test]
-    #[ignore]
     async fn un_register_node_test() {
         let client_pool: Arc<ClientPool> = Arc::new(ClientPool::new(1));
         let addrs = vec![get_placement_addr()];
@@ -140,7 +139,6 @@ mod tests {
     }
 
     #[tokio::test]
-    #[ignore]
     async fn delete_idempotent_data_test() {
         let client_pool: Arc<ClientPool> = Arc::new(ClientPool::new(1));
         let addrs = vec![get_placement_addr()];
@@ -174,7 +172,6 @@ mod tests {
     }
 
     #[tokio::test]
-    #[ignore]
     async fn set_resource_config_test() {
         let client_pool: Arc<ClientPool> = Arc::new(ClientPool::new(1));
         let addrs = vec![get_placement_addr()];
@@ -212,4 +209,57 @@ mod tests {
         .await
         .is_err());
     }
+
+    #[tokio::test]
+    async fn get_resource_config_test() {
+        let client_pool: Arc<ClientPool> = Arc::new(ClientPool::new(1));
+        let addrs = vec![get_placement_addr()];
+
+        // Check if the cluster is available
+        let request = ClusterStatusRequest::default();
+        assert!(cluster_status(client_pool.clone(), addrs.clone(), request)
+            .await
+            .is_ok());
+
+        // Test data
+        let cluster_name = "test-cluster-name".to_string();
+        let config = vec![1, 2, 3];
+        let resources = vec!["1".to_string(), "2".to_string(), "3".to_string()];
+
+        // Set the resource config first
+        let set_request = SetResourceConfigRequest {
+            cluster_name: cluster_name.clone(),
+            resources: resources.clone(),
+            config: config.clone(),
+        };
+        assert!(
+            set_resource_config(client_pool.clone(), addrs.clone(), set_request)
+                .await
+                .is_ok()
+        );
+
+        // Test: Get the resource config
+        let valid_get_request = GetResourceConfigRequest {
+            cluster_name: cluster_name.clone(),
+            resources: resources.clone(),
+        };
+        assert!(
+            get_resource_config(client_pool.clone(), addrs.clone(), valid_get_request)
+                .await
+                .is_ok()
+        );
+
+        // Test: Get the resource config with empty cluster name
+        let get_request_with_empty_cluster_name = GetResourceConfigRequest {
+            cluster_name: "".to_string(),
+            resources,
+        };
+        assert!(get_resource_config(
+            client_pool.clone(),
+            addrs.clone(),
+            get_request_with_empty_cluster_name
+        )
+        .await
+        .is_err());
+    }
 }