test(build): close resource extraction mutants
Prove fail-closed slot and handler-parameter diagnostics through AppBuilder, consolidate resource insertion around the stored entry, and lower validated runtime events without redundant fallible identifier conversion. req: build/004 req: diagnostics/004 req: surface/008 req: test/021
This commit is contained in:
+62
-52
@@ -335,7 +335,7 @@ impl Resources {
|
||||
if let Some(on_attr) = static_attr(&node.attrs, "data-hemx-on") {
|
||||
for event in event_tokens(&on_attr) {
|
||||
let canonical = canonical_symbol(root, path, event);
|
||||
self.insert_event(canonical, event.to_owned(), component.clone())?;
|
||||
self.insert_event(canonical, event.to_owned(), component.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,15 +417,17 @@ impl Resources {
|
||||
component: String,
|
||||
keyed: bool,
|
||||
) -> io::Result<()> {
|
||||
insert_resource(&mut self.slots, "slot", symbol, name, component, keyed)
|
||||
let slot = insert_resource(&mut self.slots, "slot", symbol, name, component)?;
|
||||
slot.keyed |= keyed;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_handle(&mut self, symbol: String, name: String, component: String) -> io::Result<()> {
|
||||
insert_resource(&mut self.handles, "handle", symbol, name, component, false)
|
||||
insert_resource(&mut self.handles, "handle", symbol, name, component).map(|_| ())
|
||||
}
|
||||
|
||||
fn insert_atom(&mut self, symbol: String, name: String, component: String) -> io::Result<()> {
|
||||
insert_resource(&mut self.atoms, "atom", symbol, name, component, false)
|
||||
insert_resource(&mut self.atoms, "atom", symbol, name, component).map(|_| ())
|
||||
}
|
||||
|
||||
fn insert_class(&mut self, symbol: String, token: String, component: String) -> io::Result<()> {
|
||||
@@ -482,32 +484,13 @@ impl Resources {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_event(&mut self, symbol: String, name: String, component: String) -> io::Result<()> {
|
||||
let ident = event_ident(&name).ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"invalid hemx event `{name}`; expected an ASCII event name usable from Rust"
|
||||
),
|
||||
)
|
||||
})?;
|
||||
let event = EventToken {
|
||||
fn insert_event(&mut self, symbol: String, name: String, component: String) {
|
||||
self.events.entry(symbol.clone()).or_insert(EventToken {
|
||||
symbol,
|
||||
ident,
|
||||
ident: name.clone(),
|
||||
component,
|
||||
name,
|
||||
};
|
||||
match self.events.get(&event.symbol) {
|
||||
Some(existing) if existing.name != event.name => Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("conflicting event name for `{}`", existing.symbol),
|
||||
)),
|
||||
Some(_) => Ok(()),
|
||||
None => {
|
||||
self.events.insert(event.symbol.clone(), event);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn insert_form(
|
||||
@@ -1323,32 +1306,29 @@ fn __hemx_attr(tag: &str, attr: &str) -> Option<::std::string::String> {
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_resource(
|
||||
map: &mut BTreeMap<String, Resource>,
|
||||
fn insert_resource<'a>(
|
||||
map: &'a mut BTreeMap<String, Resource>,
|
||||
kind: &str,
|
||||
symbol: String,
|
||||
name: String,
|
||||
component: String,
|
||||
keyed: bool,
|
||||
) -> io::Result<()> {
|
||||
let mut resource = make_resource(kind, symbol, name, component)?;
|
||||
resource.keyed = keyed;
|
||||
match map.get_mut(&resource.ident) {
|
||||
Some(existing) if existing.symbol != resource.symbol => Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"duplicate generated identifier `{}` for `{}` and `{}`",
|
||||
resource.ident, existing.symbol, resource.symbol
|
||||
),
|
||||
)),
|
||||
Some(existing) => {
|
||||
existing.keyed |= keyed;
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
map.insert(resource.ident.clone(), resource);
|
||||
Ok(())
|
||||
) -> io::Result<&'a mut Resource> {
|
||||
let resource = make_resource(kind, symbol, name, component)?;
|
||||
match map.entry(resource.ident.clone()) {
|
||||
std::collections::btree_map::Entry::Occupied(entry)
|
||||
if entry.get().symbol != resource.symbol =>
|
||||
{
|
||||
let existing = entry.get();
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!(
|
||||
"duplicate generated identifier `{}` for `{}` and `{}`",
|
||||
resource.ident, existing.symbol, resource.symbol
|
||||
),
|
||||
))
|
||||
}
|
||||
std::collections::btree_map::Entry::Occupied(entry) => Ok(entry.into_mut()),
|
||||
std::collections::btree_map::Entry::Vacant(entry) => Ok(entry.insert(resource)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1570,10 +1550,6 @@ fn data_param_ident(name: &str) -> Option<String> {
|
||||
rust_ident(&data_name.replace('-', "_"))
|
||||
}
|
||||
|
||||
fn event_ident(name: &str) -> Option<String> {
|
||||
rust_ident(&name.replace(['-', ':'], "_"))
|
||||
}
|
||||
|
||||
fn can_host_keyed_collection(tag: &str) -> bool {
|
||||
matches!(
|
||||
tag,
|
||||
@@ -2564,6 +2540,40 @@ mod tests {
|
||||
invalid_handler.display()
|
||||
)
|
||||
);
|
||||
|
||||
std::fs::remove_file(invalid_handler).unwrap();
|
||||
std::fs::write(
|
||||
templates.join("invalid_param.heml"),
|
||||
r#"<button data-hemx-handle="save" data-123="value">Save</button>"#,
|
||||
)
|
||||
.unwrap();
|
||||
let error = app()
|
||||
.template_dir(&templates)
|
||||
.out_dir(&invalid_out)
|
||||
.run()
|
||||
.unwrap_err();
|
||||
assert_eq!(error.kind(), io::ErrorKind::InvalidData);
|
||||
assert_eq!(
|
||||
error.to_string(),
|
||||
"invalid handler param attribute `data-123`; expected data-* name usable from Rust"
|
||||
);
|
||||
|
||||
std::fs::remove_file(templates.join("invalid_param.heml")).unwrap();
|
||||
std::fs::write(
|
||||
templates.join("invalid_slot.heml"),
|
||||
r#"<section data-hemx-slot="123">Invalid</section>"#,
|
||||
)
|
||||
.unwrap();
|
||||
let error = app()
|
||||
.template_dir(&templates)
|
||||
.out_dir(&invalid_out)
|
||||
.run()
|
||||
.unwrap_err();
|
||||
assert_eq!(error.kind(), io::ErrorKind::InvalidData);
|
||||
assert_eq!(
|
||||
error.to_string(),
|
||||
"invalid hemx slot name `123`; expected a Rust identifier"
|
||||
);
|
||||
let _ = std::fs::remove_dir_all(root);
|
||||
// test req: build/004 req: client_local/011 req: diagnostics/004
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user