test(derive): close final mutation shard

Fail closed on malformed symbol lines, prove sorted deduplicated component discovery and component filtering, exclude ordinary functions from handler registration, and emit diagnostics through quoted tokens without string reparsing.

req: diagnostics/003

req: component/003

req: derive_handler/003

req: test/020

req: test/021

req: test/022

req: test/023
This commit is contained in:
slhx agent
2026-07-17 14:52:09 +02:00
parent 5d6486c22c
commit df9f8f16fb
2 changed files with 20 additions and 6 deletions
+19 -5
View File
@@ -1007,9 +1007,7 @@ fn symbol_component(symbol: &str) -> Option<&str> {
}
fn compile_error(message: &str) -> TokenStream {
format!("compile_error!({message:?});")
.parse()
.expect("compile_error expansion is valid")
quote!(compile_error!(#message);).into()
}
#[cfg(test)]
@@ -1023,7 +1021,8 @@ mod tests {
handler_syms_path, has_form_param, has_non_unit_return, is_type_named,
join_contract_errors, missing_component_handlers, missing_form_generated_files_message,
missing_handle_params, missing_handler_generated_files_message, parser_type,
returns_result, rust_ident, syms_contains_handle, ComponentHandler, HandlerPlacement,
returns_result, rust_ident, symbol_component, syms_components, syms_contains_handle,
syms_handles, ComponentHandler, HandlerPlacement,
};
use quote::{quote, ToTokens};
use syn::{parse_quote, ItemFn, Type};
@@ -1345,7 +1344,7 @@ mod tests {
let path = std::env::temp_dir().join("hemx-derive-syms-test.syms");
std::fs::write(
&path,
"hemx-syms-v1\nslot\ttemplates/a.heml::count\tcount\t1\nhandle\ttemplates/a.heml::create\tcreate\t2\nhandle_form\tcreate\tnew_todo\nhandle_param\tcreate\ttodo_id\n",
"hemx-syms-v1\nslot\ttemplates/a.heml::count\tcount\t1\nhandle\ttemplates/z.heml::archive\tarchive\t3\nhandle\ttemplates/a.heml::create\tcreate\t2\nhandle\ttemplates/a.heml::delete\tdelete\t4\nhandle\ttemplates/a.heml::create\tcreate-again\t5\nhandle\thas-no-component\tignored\t6\nhandle\nhandle_form\tcreate\tnew_todo\nhandle_param\tcreate\ttodo_id\n",
)
.unwrap();
@@ -1355,6 +1354,19 @@ mod tests {
assert!(!handle_requires_form(&path, "missing"));
assert_eq!(handle_params(&path, "create"), vec!["todo_id"]);
assert!(handle_params(&path, "missing").is_empty());
assert_eq!(
syms_handles(&path, Some("a")),
vec!["create", "delete", "create-again"]
);
assert_eq!(
syms_handles(&path, None),
vec!["archive", "create", "delete", "create-again", "ignored"]
);
assert_eq!(syms_components(&path), vec!["a", "z"]);
assert_eq!(symbol_component("templates/a.heml::create"), Some("a"));
assert_eq!(symbol_component("a.heml::create"), Some("a"));
assert_eq!(symbol_component("a.html::create"), None);
assert_eq!(symbol_component("a.heml"), None);
let _ = std::fs::remove_file(&path);
assert!(
@@ -1531,6 +1543,8 @@ mod tests {
mod component {
#[hemx::handler]
fn create() -> impl hemx::IntoEffect { hemx::advanced::EffectBatch::default() }
fn helper() {}
}
};
let (_, items) = module.content.expect("inline module");