summaryrefslogtreecommitdiffstats
path: root/crates/core/plugin_sm/tests/plugin.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/core/plugin_sm/tests/plugin.rs')
-rw-r--r--crates/core/plugin_sm/tests/plugin.rs329
1 files changed, 329 insertions, 0 deletions
diff --git a/crates/core/plugin_sm/tests/plugin.rs b/crates/core/plugin_sm/tests/plugin.rs
new file mode 100644
index 00000000..26738d4f
--- /dev/null
+++ b/crates/core/plugin_sm/tests/plugin.rs
@@ -0,0 +1,329 @@
+#[cfg(test)]
+mod tests {
+
+ use assert_matches::assert_matches;
+ use json_sm::{SoftwareError, SoftwareModule, SoftwareModuleUpdate};
+ use plugin_sm::plugin::{ExternalPluginCommand, Plugin};
+ use std::{fs, io::Write, path::PathBuf, str::FromStr};
+ use tokio::fs::File;
+ use tokio::io::BufWriter;
+
+ #[tokio::test]
+ async fn plugin_get_command_prepare() {
+ // Prepare dummy plugin.
+ let (plugin, _plugin_path) = get_dummy_plugin("test");
+
+ // Call dummy plugin via plugin api.
+ let mut logger = dev_null().await;
+ let res = plugin.prepare(&mut logger).await;
+
+ // Expect to get Ok as plugin should exit with code 0.
+ assert_eq!(res, Ok(()));
+ }
+
+ #[tokio::test]
+ async fn plugin_get_command_finalize() {
+ // Prepare dummy plugin.
+ let (plugin, _plugin_path) = get_dummy_plugin("test");
+
+ // Call dummy plugin via plugin api.
+ let mut logger = dev_null().await;
+ let res = plugin.finalize(&mut logger).await;
+
+ // Expect Ok as plugin should exit with code 0. If Ok, there is no more checks to be done.
+ assert_eq!(res, Ok(()));
+ }
+
+ #[tokio::test]
+ async fn plugin_get_command_list() {
+ // Prepare dummy plugin with .0 which will give specific exit code ==0.
+ let (plugin, _plugin_path) = get_dummy_plugin("test");
+ let path = get_dummy_plugin_tmp_path();
+
+ let mut file = tempfile::Builder::new()
+ .suffix(".0")
+ .tempfile_in(path)
+ .unwrap();
+
+ // Add content of the expected stdout to the dummy plugin.
+ let content = "abc\t1.0";
+ let _a = file.write_all(content.as_bytes()).unwrap();
+
+ // Create expected response.
+ let module = SoftwareModule {
+ module_type: Some("test".into()),
+ name: "abc".into(),
+ version: Some("1.0".into()),
+ url: None,
+ file_path: None,
+ };
+ let expected_response = vec![module];
+
+ // Call plugin via API.
+ let mut logger = dev_null().await;
+ let res = plugin.list(&mut logger).await;
+
+ // Expect Ok as plugin should exit with code 0.
+ assert!(res.is_ok());
+ assert_eq!(res.unwrap(), expected_response);
+ }
+
+ #[tokio::test]
+ async fn plugin_get_command_install() {
+ // Prepare dummy plugin with .0 which will give specific exit code ==0.
+ let (plugin, _plugin_path) = get_dummy_plugin("test");
+ let path = get_dummy_plugin_tmp_path();
+
+ let mut file = tempfile::Builder::new()
+ .suffix(".0")
+ .tempfile_in(path)
+ .unwrap();
+
+ // Add content of the expected stdout to the dummy plugin.
+ let content = "abc\t1.0";
+ let _a = file.write_all(content.as_bytes()).unwrap();
+
+ // Create module to perform plugin install API call containing valid input.
+ let module = SoftwareModule {
+ module_type: Some("test".into()),
+ name: "test".into(),
+ version: None,
+ url: None,
+ file_path: None,
+ };
+
+ // Call plugin install via API.
+ let mut logger = dev_null().await;
+ let res = plugin.install(&module, &mut logger).await;
+
+ // Expect Ok as plugin should exit with code 0. If Ok, there is no response to assert.
+ assert!(res.is_ok());
+ }
+
+ #[tokio::test]
+ async fn plugin_get_command_remove() {
+ // Prepare dummy plugin with .0 which will give specific exit code ==0.
+ let (plugin, _plugin_path) = get_dummy_plugin("test");
+ let path = get_dummy_plugin_tmp_path();
+
+ let mut file = tempfile::Builder::new()
+ .suffix(".0")
+ .tempfile_in(path)
+ .unwrap();
+
+ // Add content of the expected stdout to the dummy plugin.
+ let content = "abc\t1.0";
+ let _a = file.write_all(content.as_bytes()).unwrap();
+
+ // Create module to perform plugin install API call containing valid input.
+ let module = SoftwareModule {
+ module_type: Some("test".into()),
+ name: "test".into(),
+ version: None,
+ url: None,
+ file_path: None,
+ };
+
+ // Call plugin remove API .
+ let mut logger = dev_null().await;
+ let res = plugin.remove(&module, &mut logger).await;
+
+ // Expect Ok as plugin should exit with code 0. If Ok, no more output to be validated.
+ assert!(res.is_ok());
+ }
+
+ #[test]
+ fn plugin_call_name_and_path() {
+ let dummy_plugin_path = get_dummy_plugin_path();
+ let plugin = ExternalPluginCommand::new("test", &dummy_plugin_path);
+ assert_eq!(plugin.name, "test");
+ assert_eq!(plugin.path, dummy_plugin_path);
+ }
+
+ #[test]
+ fn plugin_check_module_type_both_same() {
+ let dummy_plugin_path = get_dummy_plugin_path();
+ let plugin = ExternalPluginCommand::new("test", &dummy_plugin_path);
+ let module = SoftwareModule {
+ module_type: Some("test".into()),
+ name: "test".into(),
+ version: None,
+ url: None,
+ file_path: None,
+ };
+
+ // Call plugin check_module_type API to validate if plugin exists.
+ let res = plugin.check_module_type(&module);
+
+ // Expect Ok as plugin registry shall return no error. If Ok, no more output to be validated.
+ assert_eq!(res, Ok(()));
+ }
+
+ #[test]
+ fn plugin_check_module_type_both_different() {
+ // Create dummy plugin.
+ let dummy_plugin_path = get_dummy_plugin_path();
+
+ // Create new plugin in the registry with name `test`.
+ let plugin = ExternalPluginCommand::new("test", &dummy_plugin_path);
+
+ // Create test module with name `test2`.
+ let module = SoftwareModule {
+ module_type: Some("test2".into()),
+ name: "test2".into(),
+ version: None,
+ url: None,
+ file_path: None,
+ };
+
+ // Call plugin API to check if the plugin with name `test2` is registered.
+ let res = plugin.check_module_type(&module);
+
+ // Plugin is with name `test2` is not registered.
+ assert_eq!(
+ res,
+ Err(SoftwareError::WrongModuleType {
+ actual: "test".into(),
+ expected: "test2".into()
+ })
+ );
+ }
+
+ #[test]
+ fn plugin_check_module_type_default() {
+ // Create dummy plugin.
+ let dummy_plugin_path = get_dummy_plugin_path();
+
+ let plugin = ExternalPluginCommand::new("test", &dummy_plugin_path);
+
+ // Create software module without an explicit type.
+ let module = SoftwareModule {
+ module_type: None,
+ name: "test".into(),
+ version: None,
+ url: None,
+ file_path: None,
+ };
+ let res = plugin.check_module_type(&module);
+
+ // A software module without an explicit type can be handled by any plugin, which in practice is the default plugin.
+ assert_eq!(res, Ok(()));
+ }
+
+ #[tokio::test]
+ async fn plugin_get_command_update_list() {
+ // Prepare dummy plugin with .0 which will give specific exit code ==0.
+ let (plugin, _plugin_path) = get_dummy_plugin("test");
+
+ // Create list of modules to perform plugin update-list API call containing valid input.
+ let module1 = SoftwareModule {
+ module_type: Some("test".into()),
+ name: "test1".into(),
+ version: None,
+ url: None,
+ file_path: None,
+ };
+ let module2 = SoftwareModule {
+ module_type: Some("test".into()),
+ name: "test2".into(),
+ version: None,
+ url: None,
+ file_path: None,
+ };
+
+ let mut logger = dev_null().await;
+ // Call plugin update-list via API.
+ let res = plugin
+ .update_list(
+ &vec![
+ SoftwareModuleUpdate::Install { module: module1 },
+ SoftwareModuleUpdate::Remove { module: module2 },
+ ],
+ &mut logger,
+ )
+ .await;
+
+ // Expect Ok as plugin should exit with code 0. If Ok, there is no response to assert.
+ assert_matches!(res, Err(SoftwareError::UpdateListNotSupported(_)));
+ }
+
+ // Test validating if the plugin will fall back to `install` and `remove` options if the `update-list` option is not supported
+ #[tokio::test]
+ async fn plugin_command_update_list_fallback() {
+ // Prepare dummy plugin with .0 which will give specific exit code ==0.
+ let (plugin, _plugin_path) = get_dummy_plugin("test");
+
+ // Create list of modules to perform plugin update-list API call containing valid input.
+ let module1 = SoftwareModule {
+ module_type: Some("test".into()),
+ name: "test1".into(),
+ version: None,
+ url: None,
+ file_path: None,
+ };
+ let module2 = SoftwareModule {
+ module_type: Some("test".into()),
+ name: "test2".into(),
+ version: None,
+ url: None,
+ file_path: None,
+ };
+
+ let mut logger = dev_null().await;
+ // Call plugin update-list via API.
+ let errors = plugin
+ .apply_all(
+ vec![
+ SoftwareModuleUpdate::Install { module: module1 },
+ SoftwareModuleUpdate::Remove { module: module2 },
+ ],
+ &mut logger,
+ )
+ .await;
+
+ // Expect Ok as plugin should exit with code 0. If Ok, there is no response to assert.
+ assert!(errors.is_empty());
+ }
+
+ fn get_dummy_plugin_path() -> PathBuf {
+ // Return a path to a dummy plugin in target directory.
+ let package_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
+
+ // To get the plugin binary path we need to find the `target` directory which is 3 levels above the `Cargo.toml` file of the package
+ // CARGO_MANIFEST_DIR == ./thin-edge.io/crates/core/plugin_sm
+ let dummy_plugin_path = PathBuf::from_str(package_dir.as_str())
+ .unwrap()
+ .parent() //./thin-edge.io/crates/core/
+ .unwrap()
+ .parent() // ./thin-edge.io/crates/
+ .unwrap()
+ .parent() // ./thin-edge.io/
+ .unwrap()
+ .join("target/debug/tedge_dummy_plugin");
+
+ dummy_plugin_path
+ }
+
+ fn get_dummy_plugin(name: &str) -> (ExternalPluginCommand, PathBuf) {
+ let dummy_plugin_path = get_dummy_plugin_path();
+ let plugin = ExternalPluginCommand {
+ name: name.into(),
+ path: dummy_plugin_path.clone(),
+ sudo: None,
+ };
+ (plugin, dummy_plugin_path)
+ }
+
+ fn get_dummy_plugin_tmp_path() -> PathBuf {
+ let path = PathBuf::from_str("/tmp/.tedge_dummy_plugin").unwrap();
+ if !&path.exists() {
+ let () = fs::create_dir(&path).unwrap();
+ }
+ path
+ }
+
+ async fn dev_null() -> BufWriter<File> {
+ let log_file = File::create("/dev/null").await.unwrap();
+ BufWriter::new(log_file)
+ }
+}