e26672c98c
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
99 lines
4.1 KiB
Rust
99 lines
4.1 KiB
Rust
//! 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<u8> {
|
|
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<u8> {
|
|
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<u8> {
|
|
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/<id>/`.
|
|
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<Session> {
|
|
let mut registry = Registry::new();
|
|
register_core(&mut registry).expect("core commands");
|
|
registry
|
|
}
|