//! Small real WASM packages for the engine plugin integration tests. //! //! Every fixture is a full ABI v1 module built from WAT at test time, so the //! tests exercise the same loader a user plugin goes through. #![allow(dead_code)] // shared test helpers: each test file uses only some of them use std::path::Path; use notescraft_engine::{AppSettings, Registry, Session, register_core}; use notescraft_plugins::Permission; use serde_json::json; /// An empty JSON result packed the way ABI v1 does: `ptr << 32 | len`. pub const EMPTY: &str = "(i64.const 68719476738)"; /// Makes an ABI v1 module with caller-supplied imports and command body. /// /// The module carries the data blocks the fixtures point at: `{}` at 16 (the /// empty result), `source.md` at 128, `copied.md` at 160 and /// `# Copy\0a[[source]] #bridge` at 192. #[must_use] pub fn module(imports: &str, body: &str) -> Vec { build(imports, body, "") } /// [`module`], plus a data block at 512 holding `path`: a fixture that writes to /// a path of its own (a reserved one, a traversal) needs the exact text. #[must_use] pub fn module_with_path(imports: &str, body: &str, path: &str) -> Vec { let escaped = path.replace('\\', "\\\\").replace('"', "\\\""); build(imports, body, &format!("(data (i32.const 512) \"{escaped}\")")) } /// Compiles the module text around the caller's imports, body and extra data. fn build(imports: &str, body: &str, extra_data: &str) -> Vec { let text = format!( r##"(module {imports} (memory (export "memory") 32) (global $bump (mut i32) (i32.const 65536)) (global $count (mut i32) (i32.const 0)) (data (i32.const 16) "{{}}") (data (i32.const 128) "source.md") (data (i32.const 160) "copied.md") (data (i32.const 192) "# Copy\0a[[source]] #bridge") {extra_data} (func (export "ns_alloc") (param $len i32) (result i32) (local $ptr i32) (local.set $ptr (global.get $bump)) (global.set $bump (i32.add (local.get $ptr) (local.get $len))) (local.get $ptr)) (func (export "ns_free") (param i32 i32)) (func (export "ns_abi_version") (result i32) (i32.const 1)) (func (export "ns_on_load") (result i32) (i32.const 0)) (func (export "ns_on_unload") (result i32) (i32.const 0)) (func (export "ns_on_event") (param i32 i32) (result i32) (i32.const 0)) (func (export "ns_run_command") (param $cmd i32) (param $cmdlen i32) (param $args i32) (param $argslen i32) (result i64) {body}))"## ); wat::parse_str(text).expect("compile WAT") } /// Installs an actual plugin package under `.notescraft/plugins//`. pub fn install(root: &Path, id: &str, permissions: &[Permission], wasm: &[u8]) { let dir = root.join(".notescraft/plugins").join(id); std::fs::create_dir_all(&dir).expect("plugin folder"); let manifest = json!({ "id": id, "name": id, "version": "1.2.3", "min_app_version": "1", "permissions": permissions, "commands": [{"id": "run", "name": "Run"}, {"id": "other", "name": "Other"}] }); std::fs::write(dir.join("manifest.json"), manifest.to_string()).expect("manifest"); std::fs::write(dir.join("plugin.wasm"), wasm).expect("wasm"); } /// The settings document that enables and approves `id`. pub fn settings_for(id: &str, permissions: &[Permission]) -> AppSettings { let mut settings = AppSettings::default(); settings.plugins.enabled.insert(id.to_owned()); settings.plugins.approved.insert(id.to_owned(), permissions.to_vec()); settings } /// Applies enabled + approved for `id` and loads the plugins of the vault. pub fn approved(session: &mut Session, id: &str, permissions: &[Permission]) { session.apply_app_settings(&settings_for(id, permissions)).expect("apply plugin settings"); session.load_plugins().expect("load plugins"); } /// The core registry every transport uses. #[must_use] pub fn registry() -> Registry { let mut registry = Registry::new(); register_core(&mut registry).expect("core commands"); registry }