test(derive): close fourth mutation shard

Prove generated form decoding for required, optional, and repeated controls plus bare, qualified, mismatched, and empty generic container boundaries.

req: form/004

req: diagnostics/003

req: test/022

req: test/023
This commit is contained in:
slhx agent
2026-07-17 14:27:35 +02:00
parent dd230b470e
commit 78a0d91223
2 changed files with 53 additions and 8 deletions
+52 -7
View File
@@ -1023,13 +1023,13 @@ fn compile_error(message: &str) -> TokenStream {
mod tests {
use super::{
add_app_registry_helper, add_component_register_helper, client_handler_has_inputs,
component_handler_names, expand_handler_function, form_contract_errors, form_fields,
form_impl_generics, form_model_type, form_resource_id, handle_params, handle_requires_form,
handler_form_model_type, handler_placement, 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, syms_contains_handle,
HandlerPlacement,
component_handler_names, expand_handler_function, form_contract_errors, form_decode_fields,
form_fields, form_impl_generics, form_model_type, form_resource_id, generic_inner_type,
handle_params, handle_requires_form, handler_form_model_type, handler_placement,
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, syms_contains_handle, HandlerPlacement,
};
use quote::{quote, ToTokens};
use syn::{parse_quote, ItemFn, Type};
@@ -1297,6 +1297,51 @@ mod tests {
"hemx form `profile` field `required_name` is required in HTML and must not be Option<_>",
]
);
let decode_struct: syn::ItemStruct = parse_quote!(
struct Profile {
first: Option<String>,
tags: Vec<String>,
required_name: String,
}
);
let decoded = form_decode_fields(&path, "profile", &decode_struct)
.into_iter()
.map(|tokens| tokens.to_string())
.collect::<Vec<_>>();
assert_eq!(decoded.len(), 3);
assert!(decoded[0].contains("Some (__hemx_value) => Some"));
assert!(decoded[0].contains("None => None"));
assert!(decoded[1].contains("collect :: < Result < Vec < _ > , _ >> () ?"));
assert!(decoded[2].contains("missing form field"));
let option: Type = parse_quote!(Option<String>);
let qualified_vec: Type = parse_quote!(std::vec::Vec<u64>);
let plain: Type = parse_quote!(String);
assert_eq!(
generic_inner_type(&option, "Option")
.unwrap()
.to_token_stream()
.to_string(),
"String"
);
assert_eq!(
generic_inner_type(&qualified_vec, "Vec")
.unwrap()
.to_token_stream()
.to_string(),
"u64"
);
assert!(generic_inner_type(&option, "Vec").is_none());
assert!(generic_inner_type(&plain, "String").is_none());
let empty_path = Type::Path(syn::TypePath {
qself: None,
path: syn::Path {
leading_colon: None,
segments: Default::default(),
},
});
assert!(generic_inner_type(&empty_path, "Vec").is_none());
std::fs::remove_file(path).unwrap();
// test req: form/004 req: diagnostics/003
}