Test and Iterate
Two recipes for the part of plugin development that isn't writing the handler: proving it works without a running host, and getting new bytes into a running Mydia without restarting it.
Test without a host
Goal: unit-test your handler logic with cargo test, no Wasm build and no
running Mydia.
Because the handler is plain Rust, you call it directly:
#[cfg(test)]
mod tests {
use super::*;
use mydia_plugin_sdk::types::Event;
fn event(kind: &str, metadata_json: &str) -> Event {
Event {
event: kind.into(),
category: None,
severity: None,
actor_type: None,
actor_id: None,
resource_type: None,
resource_id: None,
metadata_json: metadata_json.into(),
}
}
#[test]
fn handles_added() {
let evt = event("media_item.added", r#"{"config":{"webhook_url":"https://example.com/h"}}"#);
assert!(on_event(evt).is_ok());
}
}
The host functions (http_request, data_read) only exist in the Wasm
component, so a test that builds for the native target cannot link them
directly. Keep your testable logic in plain functions, and wrap the host calls
behind a thin shim that is compiled out off-Wasm:
#[cfg(target_arch = "wasm32")]
fn send(req: &OutboundRequest) -> Option<mydia_plugin_sdk::types::OutboundResponse> {
mydia_plugin_sdk::host::http_request(req).ok()
}
#[cfg(not(target_arch = "wasm32"))]
fn send(_req: &OutboundRequest) -> Option<mydia_plugin_sdk::types::OutboundResponse> {
None // tests exercise the request-building logic, not the wire call
}
This is exactly how the bundled notifier stays fully unit-tested. Its src/lib.rs
is worth reading for the pattern at scale.
The dev loop
Goal: rebuild a plugin and load the new bytes into a running Mydia without a restart.
Mydia reads an override directory (PLUGINS_OVERRIDE_DIR) as the
highest-precedence source of plugin bytes. Drop a <slug>.wasm there and it
shadows the installed copy; re-activating the plugin picks it up live, no host
restart. The loop:
# 1. Build the component. See the tutorial's "Build the component" step for
# the wasm32-wasip2 toolchain requirement (the nix/devenv shell provides
# it; a plain Docker setup does not).
cargo build --release --target wasm32-wasip2
# 2. Copy it into the override dir, named by your manifest slug.
# (hyphen or underscore both resolve)
cp target/wasm32-wasip2/release/my_plugin.wasm "$PLUGINS_OVERRIDE_DIR/my-plugin.wasm"
# 3. Re-activate the plugin (toggle it in the admin UI, or call
# Mydia.Plugins.reload/0 from an IEx session), then trigger an event.
So the cycle is edit -> build -> copy -> re-activate -> test. The plugin must
already be installed (its manifest seeded) so the host knows its capabilities;
the copy only refreshes the Wasm bytes.
Shortcut for repo contributors
If you have the Mydia repo checked out, native/mydia_plugin_sdk/sideload.sh
wraps steps 1 and 2 into one command:
export PLUGINS_OVERRIDE_DIR=/path/mydia/reads
native/mydia_plugin_sdk/sideload.sh path/to/my_plugin --name my-plugin
External plugin authors who pull the SDK as a dependency will not have this script, so the manual build-and-copy above is the canonical path.