From 2e5a2327f009432ac9bd247567f2b6d953ec63c6 Mon Sep 17 00:00:00 2001
From: licunlong <licunlong1@huawei.com>
Date: Thu, 13 Jul 2023 14:50:28 +0800
Subject: [PATCH] test: add UT testcase for command dispatch

---
 core/sysmaster/manager/commands.rs | 171 +++++++++++++++++++++++++++++
 1 file changed, 171 insertions(+)

diff --git a/core/sysmaster/manager/commands.rs b/core/sysmaster/manager/commands.rs
index ad3e97f..a05d949 100644
--- a/core/sysmaster/manager/commands.rs
+++ b/core/sysmaster/manager/commands.rs
@@ -132,3 +132,174 @@ where
         0i8
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use std::sync::{Arc, Mutex};
+    use std::{os::unix::net::UnixStream, rc::Rc};
+
+    use cmdproto::error::Result;
+    use cmdproto::proto::{execute::ExecuterAction, unit_comm};
+    use cmdproto::proto::{CommandRequest, ProstClientStream};
+    use constants::SCTL_SOCKET;
+    use event::{EventState, Events};
+    use sysmaster::rel::{ReliConf, Reliability};
+
+    use crate::manager::RELI_HISTORY_MAX_DBS;
+
+    use super::Commands;
+
+    struct TestExecAction {}
+
+    impl ExecuterAction for TestExecAction {
+        type Error = nix::Error;
+        type Status = nix::Error;
+
+        fn start(&self, _unit_name: &str) -> Result<(), Self::Error> {
+            Err(nix::Error::ENOENT)
+        }
+
+        fn stop(&self, _unit_name: &str) -> Result<(), Self::Error> {
+            Ok(())
+        }
+
+        fn restart(&self, _unit_name: &str) -> Result<(), Self::Error> {
+            Ok(())
+        }
+
+        fn reload(&self, _unit_name: &str) -> Result<(), Self::Error> {
+            Ok(())
+        }
+
+        fn isolate(&self, _unit_name: &str) -> Result<(), Self::Error> {
+            Ok(())
+        }
+
+        fn reset_failed(&self, _unit_name: &str) -> Result<(), Self::Error> {
+            Ok(())
+        }
+
+        fn status(&self, _unit_name: &str) -> Result<Self::Status, Self::Error> {
+            Ok(nix::Error::EINVAL)
+        }
+
+        fn list_units(&self) -> Result<String, Self::Error> {
+            Ok(String::new())
+        }
+
+        fn suspend(&self) -> Result<i32, Self::Error> {
+            Ok(0)
+        }
+
+        fn poweroff(&self) -> Result<i32, Self::Error> {
+            Ok(0)
+        }
+
+        fn reboot(&self) -> Result<i32, Self::Error> {
+            Ok(0)
+        }
+
+        fn halt(&self) -> Result<i32, Self::Error> {
+            Ok(0)
+        }
+
+        fn disable(&self, _unit_name: &str) -> Result<(), Self::Error> {
+            Ok(())
+        }
+
+        fn enable(&self, _unit_name: &str) -> Result<(), Self::Error> {
+            Ok(())
+        }
+
+        fn mask(&self, _unit_name: &str) -> Result<(), Self::Error> {
+            Ok(())
+        }
+
+        fn unmask(&self, _unit_name: &str) -> Result<(), Self::Error> {
+            Ok(())
+        }
+
+        fn daemon_reload(&self) {}
+
+        fn daemon_reexec(&self) {}
+
+        fn switch_root(&self, _init: &[String]) -> Result<(), Self::Error> {
+            Ok(())
+        }
+    }
+
+    #[test]
+    fn test_sctl_socket() {
+        if !nix::unistd::getuid().is_root() {
+            println!(
+                "We will create a socket under '/run', so we must be root to run this testcase."
+            );
+            return;
+        }
+
+        let mutex_main = Arc::new(Mutex::new(0));
+        let mutex_child = Arc::clone(&mutex_main);
+        let temp = mutex_main.lock().unwrap();
+
+        std::thread::spawn(move || {
+            /* Wait until the main thread are ready to process our request. */
+            let mut test_ok = mutex_child.lock().unwrap();
+            /* 1. Start a service as root. */
+            let stream = UnixStream::connect(SCTL_SOCKET).unwrap();
+            let mut client = ProstClientStream::new(stream);
+
+            let req = CommandRequest::new_unitcomm(
+                unit_comm::Action::Start,
+                vec!["foo.service".to_string()],
+            );
+            let data = client.execute(req).unwrap();
+            if !data
+                .message
+                .eq("Failed to start foo.service: ENOENT: No such file or directory")
+            {
+                return;
+            }
+            /* 2. Start a service as an unprivileged user. */
+            if nix::unistd::setuid(nix::unistd::Uid::from_raw(1000)).is_err() {
+                println!("Failed to set ourself to unprivileged user");
+                return;
+            }
+            let stream = UnixStream::connect(SCTL_SOCKET).unwrap();
+            let mut client = ProstClientStream::new(stream);
+
+            let req = CommandRequest::new_unitcomm(
+                unit_comm::Action::Start,
+                vec!["foo.service".to_string()],
+            );
+            let data = client.execute(req).unwrap();
+            if !data
+                .message
+                .eq("Failed to execute your command: Operation not permitted.")
+            {
+                return;
+            }
+            *test_ok = 1;
+        });
+
+        let exec_action = TestExecAction {};
+        let reli = Rc::new(Reliability::new(
+            ReliConf::new().set_max_dbs(RELI_HISTORY_MAX_DBS),
+        ));
+        /* This will remove the /run/sysmaster/sctl on the compiling environment. */
+        let command = Rc::new(Commands::new(&reli, exec_action));
+        let e = Events::new().unwrap();
+        e.add_source(command.clone()).unwrap();
+        e.set_enabled(command.clone(), EventState::On).unwrap();
+        /* Drop temp, let the child thread go. */
+        drop(temp);
+        /* We have 2 events to dispatch, but are not sure if we can get these events after waiting
+         * 300ms, 400ms. So we wait one more time to make sure all events are dispatched. */
+        e.run(300).unwrap();
+        e.run(400).unwrap();
+        e.run(300).unwrap();
+        let test_ok = mutex_main.lock().unwrap();
+        /* The value should be set to 1 in the child process, if everything works well. */
+        assert_eq!(*test_ok, 1);
+        e.del_source(command).unwrap();
+    }
+}
-- 
2.33.0

