Skip to content

Commit fcac251

Browse files
committed
test(stack-guest-abi): GuestError's codes are checked without a hand-kept list
The same change as #1103 makes for the stack crates: ERROR_CODES was read only by its own test. The test now builds every GuestError through a match with no wildcard, so a new variant fails to compile until it has a row, and checks each code is in this crate's namespace and snake_case. Refs #1100 Claude-Session: https://claude.ai/code/session_01URtfKsTToFUCRwq3g7gCUf
1 parent 8071a9b commit fcac251

1 file changed

Lines changed: 41 additions & 25 deletions

File tree

‎packages/stack-guest-abi/src/last_error.rs‎

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,6 @@ impl GuestError {
124124
}
125125
}
126126

127-
/// Every miette code this crate's errors carry. A test builds every
128-
/// [`GuestError`] and checks its code is here.
129-
pub const ERROR_CODES: &[&str] = &[
130-
"stack_guest_abi::malformed_input",
131-
"stack_guest_abi::out_of_order",
132-
"stack_guest_abi::internal",
133-
"stack_guest_abi::status",
134-
];
135-
136127
/// A [`GuestError::Malformed`], recorded; returns `STATUS_ENCODING`.
137128
pub fn malformed(detail: impl Into<Cow<'static, str>>) -> u32 {
138129
GuestError::Malformed(detail.into()).fail()
@@ -534,23 +525,48 @@ mod tests {
534525
assert!(take().is_none());
535526
}
536527

528+
/// One row per variant of an enum, written `pattern => value`. The
529+
/// patterns are the arms of a match with no wildcard, so a variant with
530+
/// no row fails to compile, and each value must match its own pattern.
531+
macro_rules! variants {
532+
($($pattern:pat => $value:expr),+ $(,)?) => {{
533+
let rows = vec![$({
534+
let value = $value;
535+
assert!(matches!(value, $pattern), "{value:?} is not {}", stringify!($pattern));
536+
value
537+
}),+];
538+
for row in &rows {
539+
match row {
540+
$($pattern => {})+
541+
}
542+
}
543+
rows
544+
}};
545+
}
546+
547+
/// Every [`GuestError`] has a code in this crate's namespace and
548+
/// `snake_case`.
537549
#[test]
538-
fn every_guest_error_has_a_listed_code() {
539-
let errors = [
540-
GuestError::Malformed("x".into()),
541-
GuestError::OutOfOrder("x".into()),
542-
GuestError::Internal("x".into()),
543-
GuestError::Status(9),
550+
fn every_guest_error_has_a_code_of_this_crate() {
551+
let errors = variants![
552+
GuestError::Malformed(_) => GuestError::Malformed("x".into()),
553+
GuestError::OutOfOrder(_) => GuestError::OutOfOrder("x".into()),
554+
GuestError::Internal(_) => GuestError::Internal("x".into()),
555+
GuestError::Status(_) => GuestError::Status(9),
544556
];
545-
let codes: Vec<String> = errors
546-
.iter()
547-
.map(|error| {
548-
error
549-
.code()
550-
.map(|code| code.to_string())
551-
.unwrap_or_default()
552-
})
553-
.collect();
554-
assert_eq!(codes, ERROR_CODES);
557+
for error in &errors {
558+
let code = error
559+
.code()
560+
.unwrap_or_else(|| panic!("{error:?} has no code"))
561+
.to_string();
562+
let name = code.strip_prefix("stack_guest_abi::").unwrap_or_default();
563+
assert!(
564+
name.starts_with(|c: char| c.is_ascii_lowercase())
565+
&& name
566+
.chars()
567+
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_'),
568+
"{code}"
569+
);
570+
}
555571
}
556572
}

0 commit comments

Comments
 (0)