Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: If4372ea33b93306486170353f9edf4a76a6a6964
188 lines
5.1 KiB
Rust
188 lines
5.1 KiB
Rust
//! Integration tests for the plugin pipeline with real WASM execution.
|
|
//!
|
|
//! These tests use the test-plugin fixture at
|
|
//! `tests/fixtures/test-plugin/` which is a compiled WASM binary
|
|
//! exercising all extension points.
|
|
|
|
// FIXME: add a Justfile and make sure the fixture is compiled for tests...
|
|
|
|
#![allow(clippy::print_stderr, reason = "Fine for tests")]
|
|
use std::{path::Path, sync::Arc};
|
|
|
|
use pinakes_core::{
|
|
config::PluginTimeoutConfig,
|
|
plugin::{PluginManager, PluginManagerConfig, PluginPipeline},
|
|
};
|
|
use tempfile::TempDir;
|
|
|
|
/// Path to the compiled test plugin fixture.
|
|
fn fixture_dir() -> std::path::PathBuf {
|
|
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/test-plugin")
|
|
}
|
|
|
|
/// Check the WASM binary exists (skip tests if not built).
|
|
fn wasm_binary_exists() -> bool {
|
|
fixture_dir().join("test_plugin.wasm").exists()
|
|
}
|
|
|
|
/// Set up a `PluginManager` pointing at the fixture directory as a
|
|
/// `plugin_dir`. The loader expects `plugin_dirs/test-plugin/plugin.toml`.
|
|
/// So we point at the fixtures directory (parent of test-plugin/).
|
|
fn setup_manager(temp: &TempDir) -> PluginManager {
|
|
let fixtures_dir =
|
|
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures");
|
|
|
|
let config = PluginManagerConfig {
|
|
plugin_dirs: vec![fixtures_dir],
|
|
allow_unsigned: true,
|
|
max_concurrent_ops: 2,
|
|
plugin_timeout_secs: 30,
|
|
timeouts: PluginTimeoutConfig::default(),
|
|
max_consecutive_failures: 5,
|
|
..Default::default()
|
|
};
|
|
|
|
PluginManager::new(
|
|
temp.path().join("data"),
|
|
temp.path().join("cache"),
|
|
config,
|
|
)
|
|
.expect("create plugin manager")
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_plugin_discovery_loads_fixture() {
|
|
if !wasm_binary_exists() {
|
|
eprintln!("WASM binary not found, skipping test");
|
|
return;
|
|
}
|
|
|
|
let temp = TempDir::new().unwrap();
|
|
let manager = setup_manager(&temp);
|
|
|
|
let loaded = manager.discover_and_load_all().await.unwrap();
|
|
assert!(
|
|
!loaded.is_empty(),
|
|
"should discover at least the test-plugin"
|
|
);
|
|
assert!(
|
|
loaded.contains(&"test-plugin".to_string()),
|
|
"should load test-plugin, got: {loaded:?}"
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_plugin_capability_discovery() {
|
|
if !wasm_binary_exists() {
|
|
return;
|
|
}
|
|
|
|
let temp = TempDir::new().unwrap();
|
|
let manager = Arc::new(setup_manager(&temp));
|
|
manager.discover_and_load_all().await.unwrap();
|
|
|
|
let pipeline = Arc::new(PluginPipeline::new(
|
|
manager,
|
|
PluginTimeoutConfig::default(),
|
|
5,
|
|
));
|
|
|
|
pipeline.discover_capabilities().await.unwrap();
|
|
|
|
// The test plugin registers a custom .testfile media type, so
|
|
// resolve_media_type should recognise it (even if no physical file exists,
|
|
// can_handle checks the extension).
|
|
let resolved = pipeline
|
|
.resolve_media_type(Path::new("/tmp/example.testfile"))
|
|
.await;
|
|
assert!(
|
|
resolved.is_some(),
|
|
"pipeline should resolve .testfile via test-plugin"
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_pipeline_builtin_fallback_still_works() {
|
|
if !wasm_binary_exists() {
|
|
return;
|
|
}
|
|
|
|
let temp = TempDir::new().unwrap();
|
|
let manager = Arc::new(setup_manager(&temp));
|
|
manager.discover_and_load_all().await.unwrap();
|
|
|
|
let pipeline = Arc::new(PluginPipeline::new(
|
|
manager,
|
|
PluginTimeoutConfig::default(),
|
|
5,
|
|
));
|
|
pipeline.discover_capabilities().await.unwrap();
|
|
|
|
// Built-in types should still resolve (test-plugin at priority 50 runs
|
|
// first but won't claim .mp3).
|
|
let mp3 = pipeline
|
|
.resolve_media_type(Path::new("/tmp/song.mp3"))
|
|
.await;
|
|
assert!(mp3.is_some(), "built-in .mp3 resolution should still work");
|
|
|
|
// Unknown types should still resolve to None
|
|
let unknown = pipeline
|
|
.resolve_media_type(Path::new("/tmp/data.xyz999"))
|
|
.await;
|
|
assert!(
|
|
unknown.is_none(),
|
|
"unknown extension should resolve to None"
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_emit_event_with_loaded_plugin() {
|
|
if !wasm_binary_exists() {
|
|
return;
|
|
}
|
|
|
|
let temp = TempDir::new().unwrap();
|
|
let manager = Arc::new(setup_manager(&temp));
|
|
manager.discover_and_load_all().await.unwrap();
|
|
|
|
let pipeline = Arc::new(PluginPipeline::new(
|
|
manager,
|
|
PluginTimeoutConfig::default(),
|
|
5,
|
|
));
|
|
pipeline.discover_capabilities().await.unwrap();
|
|
|
|
// Emit an event the test-plugin is interested in (MediaImported).
|
|
// Should not panic; the handler runs in a spawned task.
|
|
pipeline.emit_event(
|
|
"MediaImported",
|
|
&serde_json::json!({"media_id": "test-123", "path": "/tmp/test.mp3"}),
|
|
);
|
|
|
|
// Give the spawned task a moment to run
|
|
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_event_not_interested_is_ignored() {
|
|
if !wasm_binary_exists() {
|
|
return;
|
|
}
|
|
|
|
let temp = TempDir::new().unwrap();
|
|
let manager = Arc::new(setup_manager(&temp));
|
|
manager.discover_and_load_all().await.unwrap();
|
|
|
|
let pipeline = Arc::new(PluginPipeline::new(
|
|
manager,
|
|
PluginTimeoutConfig::default(),
|
|
5,
|
|
));
|
|
pipeline.discover_capabilities().await.unwrap();
|
|
|
|
// Emit an event the test-plugin is NOT interested in.
|
|
// Should complete silently.
|
|
pipeline.emit_event("ScanStarted", &serde_json::json!({"roots": ["/tmp"]}));
|
|
|
|
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
|
}
|