#[cfg(test)]
mod plugin_dynamic_tests {
use super::*;
use crate::model::app::App;
use crate::model::common::Bounds;
use crate::plugin::*;
use crate::{AppContext, Config};
use std::collections::HashMap;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_plugin_registry_creation() {
let registry = PluginRegistry::new();
assert_eq!(registry.list_plugins().len(), 0);
}
#[test]
fn test_plugin_manifest_loading_mock() {
let registry = PluginRegistry::new();
let temp_dir = TempDir::new().unwrap();
let manifest = registry
.load_manifest(temp_dir.path().join("nonexistent.toml"))
.unwrap();
assert_eq!(manifest.name, "test_plugin");
assert_eq!(manifest.component_types, vec!["custom_chart"]);
}
#[test]
fn test_plugin_manifest_loading_real() {
let registry = PluginRegistry::new();
let temp_dir = TempDir::new().unwrap();
let manifest_path = temp_dir.path().join("plugin.toml");
let toml_content = r#"
name = "real_plugin"
version = "2.0.0"
author = "Test Author"
description = "Real plugin for testing"
entry_point = "libplugin.so"
component_types = ["advanced_chart", "data_widget"]
[[dependencies]]
name = "serde"
version = "1.0"
required = true
[[permissions]]
[permissions.FileSystem]
paths = ["/tmp"]
"#;
fs::write(&manifest_path, toml_content).unwrap();
let manifest = registry.load_manifest(&manifest_path).unwrap();
assert_eq!(manifest.name, "real_plugin");
assert_eq!(manifest.version, "2.0.0");
assert_eq!(
manifest.component_types,
vec!["advanced_chart", "data_widget"]
);
assert_eq!(manifest.dependencies.len(), 1);
assert_eq!(manifest.permissions.len(), 1);
}
#[test]
fn test_mock_plugin_loading() {
let mut registry = PluginRegistry::new();
let temp_dir = TempDir::new().unwrap();
let result = registry.load_plugin(temp_dir.path());
assert!(result.is_ok());
let plugins = registry.list_plugins();
assert_eq!(plugins.len(), 1);
assert_eq!(plugins[0].name, "test_plugin");
let component = registry.get_component("custom_chart");
assert!(component.is_some());
}
#[test]
fn test_mock_component_rendering() {
let mut registry = PluginRegistry::new();
let temp_dir = TempDir::new().unwrap();
registry.load_plugin(temp_dir.path()).unwrap();
let app = App::default();
let config = Config::default();
let app_context = AppContext::new(app, config);
let bounds = Bounds::new(0, 0, 30, 10);
let context = PluginContext {
app_context,
muxbox_bounds: bounds,
plugin_data: HashMap::new(),
permissions: vec![],
};
let mut properties = HashMap::new();
properties.insert(
"title".to_string(),
serde_json::Value::String("Test Chart".to_string()),
);
let component_config = ComponentConfig {
component_type: "custom_chart".to_string(),
properties,
data_source: None,
refresh_interval: Some(1000),
};
let result = registry.render_component("custom_chart", &context, &component_config);
assert!(result.is_ok());
let content = result.unwrap();
assert!(content.contains("Custom component: custom_chart"));
}
#[test]
fn test_plugin_event_handling() {
let mut registry = PluginRegistry::new();
let temp_dir = TempDir::new().unwrap();
registry.load_plugin(temp_dir.path()).unwrap();
let app = App::default();
let config = Config::default();
let app_context = AppContext::new(app, config);
let bounds = Bounds::new(0, 0, 20, 5);
let context = PluginContext {
app_context,
muxbox_bounds: bounds,
plugin_data: HashMap::new(),
permissions: vec![],
};
let event = PluginEvent::KeyPress("Enter".to_string());
let result = registry.handle_event("custom_chart", &context, &event);
assert!(result.is_ok());
let mouse_event = PluginEvent::MouseEvent {
x: 10,
y: 5,
action: "click".to_string(),
};
let result = registry.handle_event("custom_chart", &context, &mouse_event);
assert!(result.is_ok());
}
#[test]
fn test_plugin_unloading() {
let mut registry = PluginRegistry::new();
let temp_dir = TempDir::new().unwrap();
registry.load_plugin(temp_dir.path()).unwrap();
assert_eq!(registry.list_plugins().len(), 1);
assert!(registry.get_component("custom_chart").is_some());
let result = registry.unload_plugin("test_plugin");
assert!(result.is_ok());
assert_eq!(registry.list_plugins().len(), 0);
assert!(registry.get_component("custom_chart").is_none());
}
#[test]
fn test_plugin_security_validation() {
let mut registry = PluginRegistry::new();
let temp_dir = TempDir::new().unwrap();
let manifest_path = temp_dir.path().join("plugin.toml");
let toml_content = r#"
name = "unsafe_plugin"
version = "1.0.0"
author = "Test"
description = "Plugin with unsafe permissions"
entry_point = "lib.so"
component_types = ["unsafe_component"]
[[permissions]]
[permissions.FileSystem]
paths = ["/etc/passwd"]
[[permissions]]
[permissions.Network]
hosts = ["malicious.com"]
"#;
fs::write(&manifest_path, toml_content).unwrap();
let result = registry.load_plugin(temp_dir.path());
if let Err(ref e) = result {
println!("Security validation result: {:?}", e);
}
assert!(result.is_err());
match result {
Err(PluginError::PermissionDenied(msg)) => {
assert!(msg.contains("/etc/passwd") || msg.contains("malicious.com"));
}
Err(PluginError::InitializationFailed(msg)) => {
if msg.contains("parse") {
return; }
panic!(
"Expected PermissionDenied or parsing error, got InitializationFailed: {}",
msg
);
}
other => {
panic!(
"Expected PermissionDenied or parsing error, got: {:?}",
other
);
}
}
}
#[test]
fn test_dynamic_library_loading_fallback() {
let mut registry = PluginRegistry::new();
let temp_dir = TempDir::new().unwrap();
let manifest_path = temp_dir.path().join("plugin.toml");
let toml_content = r#"
name = "missing_lib_plugin"
version = "1.0.0"
author = "Test"
description = "Plugin with missing library"
entry_point = "nonexistent.so"
component_types = ["missing_component"]
dependencies = []
permissions = []
"#;
fs::write(&manifest_path, toml_content).unwrap();
let result = registry.load_plugin(temp_dir.path());
if let Err(ref e) = result {
println!("Plugin loading failed: {:?}", e);
}
assert!(result.is_ok());
let component = registry.get_component("missing_component");
assert!(component.is_some());
let app = App::default();
let config = Config::default();
let app_context = AppContext::new(app, config);
let bounds = Bounds::new(0, 0, 10, 5);
let context = PluginContext {
app_context,
muxbox_bounds: bounds,
plugin_data: HashMap::new(),
permissions: vec![],
};
let component_config = ComponentConfig {
component_type: "missing_component".to_string(),
properties: HashMap::new(),
data_source: None,
refresh_interval: None,
};
let result = registry.render_component("missing_component", &context, &component_config);
assert!(result.is_ok());
assert!(result
.unwrap()
.contains("Custom component: missing_component"));
}
#[test]
fn test_plugin_component_config_parsing() {
let config_json = r#"{
"component_type": "advanced_table",
"properties": {
"columns": ["ID", "Name", "Status"],
"sort_column": "Name",
"page_size": 50,
"filters": {
"status": "active"
}
},
"data_source": "database",
"refresh_interval": 2000
}"#;
let config: ComponentConfig = serde_json::from_str(config_json).unwrap();
assert_eq!(config.component_type, "advanced_table");
assert_eq!(config.data_source, Some("database".to_string()));
assert_eq!(config.refresh_interval, Some(2000));
assert!(config.properties.contains_key("columns"));
assert!(config.properties.contains_key("sort_column"));
assert!(config.properties.contains_key("page_size"));
}
#[test]
fn test_plugin_error_handling() {
let mut registry = PluginRegistry::new();
let app = App::default();
let config = Config::default();
let app_context = AppContext::new(app, config);
let bounds = Bounds::new(0, 0, 10, 5);
let context = PluginContext {
app_context,
muxbox_bounds: bounds,
plugin_data: HashMap::new(),
permissions: vec![],
};
let component_config = ComponentConfig {
component_type: "nonexistent".to_string(),
properties: HashMap::new(),
data_source: None,
refresh_interval: None,
};
let result = registry.render_component("nonexistent", &context, &component_config);
assert!(result.is_err());
if let Err(PluginError::InvalidConfiguration(msg)) = result {
assert!(msg.contains("not found"));
} else {
panic!("Expected InvalidConfiguration error");
}
}
#[test]
fn test_component_state_management() {
let mut metadata = HashMap::new();
metadata.insert(
"last_update".to_string(),
serde_json::json!("2024-01-01T00:00:00Z"),
);
metadata.insert("row_count".to_string(), serde_json::json!(42));
let state = ComponentState {
content: "Updated content".to_string(),
metadata,
needs_refresh: true,
};
let serialized = serde_json::to_string(&state).unwrap();
let deserialized: ComponentState = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.content, "Updated content");
assert_eq!(deserialized.needs_refresh, true);
assert!(deserialized.metadata.contains_key("last_update"));
assert!(deserialized.metadata.contains_key("row_count"));
}
}