From 62cc1f3eae58562cd4ef8469e4f6d1bfc3418777 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:11:33 -0600 Subject: [PATCH 01/15] test(core-notify-stakeholders): backfill coverage to 100%/98%/97% Removed unreachable helper functions (never called from evaluate() or anywhere else: extract_i32, parse_i32, parse_number_millis, write_i32, ascii_lower, eq_ignore_case, normalize_email, trim_ascii, parse_ymd_days, format_score_millis) and added tests for the remaining branches, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../core-notify-stakeholders/src/main.rs | 342 ++++++++---------- 1 file changed, 155 insertions(+), 187 deletions(-) diff --git a/capability-src/core-notify-stakeholders/src/main.rs b/capability-src/core-notify-stakeholders/src/main.rs index 823d670..093c272 100644 --- a/capability-src/core-notify-stakeholders/src/main.rs +++ b/capability-src/core-notify-stakeholders/src/main.rs @@ -148,7 +148,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { continue; } let channel = extract_string(obj, b"\"channel\""); - let chan: &[u8] = if channel.is_empty() { b"in_app" } else { channel }; + let chan: &[u8] = if channel.is_empty() { + b"in_app" + } else { + channel + }; if wrote > 0 { i = copy(out, i, b","); @@ -177,7 +181,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { } i = copy(out, i, b"],\"intent_count\":"); i = write_u32(out, i, wrote); - i = copy(out, i, b",\"reason_code\":\"ok\",\"evaluation_trace\":[\"event_type="); + i = copy( + out, + i, + b",\"reason_code\":\"ok\",\"evaluation_trace\":[\"event_type=", + ); i = copy(out, i, event_type); i = copy(out, i, b"\",\"prepared "); i = write_u32(out, i, wrote); @@ -187,7 +195,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { fn fail(out: &mut [u8], code: &[u8]) -> usize { let mut i = 0usize; - i = copy(out, i, b"{\"intents\":[],\"intent_count\":0,\"reason_code\":\""); + i = copy( + out, + i, + b"{\"intents\":[],\"intent_count\":0,\"reason_code\":\"", + ); i = copy(out, i, code); i = copy(out, i, b"\",\"evaluation_trace\":[]}"); i @@ -352,79 +364,6 @@ fn extract_bool(hay: &[u8], key: &[u8]) -> Option { } } -fn extract_i32(hay: &[u8], key: &[u8]) -> Option { - let pos = find(hay, key)?; - let after = &hay[pos + key.len()..]; - let colon = after.iter().position(|b| *b == b':')?; - let rest = skip_ws(&after[colon + 1..]); - parse_i32(rest) -} - -fn parse_i32(rest: &[u8]) -> Option { - if rest.is_empty() { - return None; - } - let mut neg = false; - let mut j = 0usize; - if rest[0] == b'-' { - neg = true; - j = 1; - } - if j >= rest.len() || rest[j] < b'0' || rest[j] > b'9' { - return None; - } - let mut n: i32 = 0; - while j < rest.len() && rest[j] >= b'0' && rest[j] <= b'9' { - n = n * 10 + (rest[j] - b'0') as i32; - j += 1; - } - Some(if neg { -n } else { n }) -} - -fn parse_number_millis(hay: &[u8], key: &[u8]) -> Option { - let pos = find(hay, key)?; - let after = &hay[pos + key.len()..]; - let colon = after.iter().position(|b| *b == b':')?; - let rest = skip_ws(&after[colon + 1..]); - if rest.is_empty() { - return None; - } - let mut whole: u32 = 0; - let mut frac: u32 = 0; - let mut frac_digits = 0u32; - let mut seen_dot = false; - let mut j = 0usize; - while j < rest.len() { - let b = rest[j]; - if b == b',' || b == b'}' || b == b']' || b == b' ' || b == b'\n' { - break; - } - if b == b'.' { - seen_dot = true; - j += 1; - continue; - } - if b < b'0' || b > b'9' { - break; - } - let digit = (b - b'0') as u32; - if seen_dot { - if frac_digits < 3 { - frac = frac * 10 + digit; - frac_digits += 1; - } - } else { - whole = whole * 10 + digit; - } - j += 1; - } - while frac_digits < 3 { - frac *= 10; - frac_digits += 1; - } - Some(whole * 1000 + frac) -} - fn copy(out: &mut [u8], at: usize, bytes: &[u8]) -> usize { let end = at + bytes.len(); if end > out.len() { @@ -475,112 +414,6 @@ fn write_u32(out: &mut [u8], mut i: usize, mut n: u32) -> usize { i } -fn write_i32(out: &mut [u8], mut i: usize, n: i32) -> usize { - if n < 0 { - i = copy(out, i, b"-"); - write_u32(out, i, (-n) as u32) - } else { - write_u32(out, i, n as u32) - } -} - -fn ascii_lower(b: u8) -> u8 { - if b >= b'A' && b <= b'Z' { - b + 32 - } else { - b - } -} - -fn eq_ignore_case(a: &[u8], b: &[u8]) -> bool { - if a.len() != b.len() { - return false; - } - for i in 0..a.len() { - if ascii_lower(a[i]) != ascii_lower(b[i]) { - return false; - } - } - true -} - -fn normalize_email(src: &[u8], dst: &mut [u8]) -> usize { - let mut i = 0usize; - let mut j = 0usize; - while i < src.len() && (src[i] == b' ' || src[i] == b'\t') { - i += 1; - } - let mut end = src.len(); - while end > i && (src[end - 1] == b' ' || src[end - 1] == b'\t') { - end -= 1; - } - while i < end && j < dst.len() { - dst[j] = ascii_lower(src[i]); - i += 1; - j += 1; - } - j -} - -fn trim_ascii(s: &[u8]) -> &[u8] { - let mut start = 0usize; - let mut end = s.len(); - while start < end && matches!(s[start], b' ' | b'\t' | b'\n' | b'\r') { - start += 1; - } - while end > start && matches!(s[end - 1], b' ' | b'\t' | b'\n' | b'\r') { - end -= 1; - } - &s[start..end] -} - -/// Days since 1970-01-01 for YYYY-MM-DD (Howard Hinnant civil_from_days inverse). -fn parse_ymd_days(s: &[u8]) -> Option { - if s.len() < 10 || s[4] != b'-' || s[7] != b'-' { - return None; - } - let y = parse_i32(&s[0..4])?; - let m = parse_i32(&s[5..7])?; - let d = parse_i32(&s[8..10])?; - if m < 1 || m > 12 || d < 1 || d > 31 { - return None; - } - let y = y as i32 - if m <= 2 { 1 } else { 0 }; - let era = if y >= 0 { y } else { y - 399 } / 400; - let yoe = (y - era * 400) as u32; - let mp = if m > 2 { (m - 3) as u32 } else { (m + 9) as u32 }; - let doy = (153 * mp + 2) / 5 + d as u32 - 1; - let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; - Some((era * 146097 + doe as i32) - 719468) -} - -fn format_score_millis(out: &mut [u8], millis: u32) -> usize { - let whole = millis / 1000; - let frac = millis % 1000; - let mut i = write_u32(out, 0, whole); - i = copy(out, i, b"."); - // always 3 digits for determinism in contract examples we may trim; write without trailing zeros carefully - // Use up to 3 digits, trim trailing zeros but keep at least one if frac!=0? Contract examples use 0.785 / 1.0 - if frac == 0 { - i = copy(out, i, b"0"); - return i; - } - let d0 = (frac / 100) as u8; - let d1 = ((frac / 10) % 10) as u8; - let d2 = (frac % 10) as u8; - out[i] = b'0' + d0; - i += 1; - if d1 != 0 || d2 != 0 { - out[i] = b'0' + d1; - i += 1; - if d2 != 0 { - out[i] = b'0' + d2; - i += 1; - } - } - i -} - #[cfg(not(test))] #[panic_handler] fn panic(_: &core::panic::PanicInfo<'_>) -> ! { @@ -600,25 +433,160 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"Send the revised proposal\",\"owner_id\":\"user-ada\",\"owner_name\":\"Ada\",\"status\":\"done\"},\"stakeholders\":[{\"user_id\":\"user-carol\",\"role\":\"requester\",\"channel\":\"in_app\"},{\"user_id\":\"user-mgr\",\"role\":\"manager\",\"channel\":\"email\"}],\"event_type\":\"completed\",\"notify_config\":{\"version\":\"1.0\",\"include_manager_on_complete\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_sad() { let out = run("{\"item\":{\"id\":\"ai-3\",\"title\":\"Draft agenda\",\"owner_id\":\"user-bob\",\"owner_name\":\"Bob\",\"status\":\"done\"},\"stakeholders\":[],\"event_type\":\"completed\",\"notify_config\":{\"version\":\"1.0\",\"include_manager_on_complete\":true}}"); - assert!(out.contains("\"reason_code\":\"nothing_to_notify\""), "expected nothing_to_notify in {out}"); + assert!( + out.contains("\"reason_code\":\"nothing_to_notify\""), + "expected nothing_to_notify in {out}" + ); } #[test] fn use_case_03_happy() { let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"Send the revised proposal\",\"owner_id\":\"user-ada\",\"owner_name\":\"Ada\",\"status\":\"in_progress\"},\"stakeholders\":[{\"user_id\":\"user-carol\",\"role\":\"requester\",\"channel\":\"in_app\"}],\"event_type\":\"status_changed\",\"notify_config\":{\"version\":\"1.0\",\"include_manager_on_complete\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_04_happy() { let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"Send the revised proposal\",\"owner_id\":\"user-ada\",\"owner_name\":\"Ada\",\"status\":\"blocked\"},\"stakeholders\":[{\"user_id\":\"user-carol\",\"role\":\"requester\",\"channel\":\"in_app\"}],\"event_type\":\"blocked\",\"notify_config\":{\"version\":\"1.0\",\"include_manager_on_complete\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); + } + + #[test] + fn missing_required_fields_yields_invalid_input() { + let out = run("{}"); + assert!( + out.contains("\"reason_code\":\"invalid_input\""), + "expected invalid_input in {out}" + ); + } + + #[test] + fn unknown_event_type_is_rejected() { + let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"T\",\"owner_id\":\"u\",\"owner_name\":\"Ann\",\"status\":\"done\"},\"stakeholders\":[{\"user_id\":\"u1\",\"role\":\"requester\",\"channel\":\"in_app\"}],\"event_type\":\"archived\",\"notify_config\":{\"version\":\"1.0\",\"include_manager_on_complete\":true}}"); + assert!( + out.contains("\"reason_code\":\"invalid_event_type\""), + "expected invalid_event_type in {out}" + ); + } + + #[test] + fn missing_owner_name_defaults_to_someone() { + let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"T\",\"owner_id\":\"u\",\"owner_name\":\"\",\"status\":\"done\"},\"stakeholders\":[{\"user_id\":\"u1\",\"role\":\"requester\",\"channel\":\"in_app\"}],\"event_type\":\"completed\",\"notify_config\":{\"version\":\"1.0\",\"include_manager_on_complete\":true}}"); + assert!(out.contains("Someone"), "expected default owner in {out}"); + } + + #[test] + fn manager_excluded_when_include_manager_false_with_whitespace_and_backslash() { + let out = run("{\"item\": {\"id\":\"ai-1\",\"title\":\"Report\\\\Q1\",\"owner_id\":\"u\",\"owner_name\":\"Ann\",\"status\":\"done\"}, \"stakeholders\": [ {\"user_id\":\"u1\",\"role\":\"manager\",\"channel\":\"email\"},{\"user_id\":\"u2\",\"role\":\"requester\",\"channel\":\"in_app\"} ], \"event_type\":\"completed\", \"notify_config\": { \"version\":\"1.0\",\"include_manager_on_complete\": false } }"); + assert!( + out.contains("\"intent_count\":1"), + "expected only the non-manager intent in {out}" + ); + assert!( + !out.contains("\"recipient_id\":\"u1\""), + "manager should be excluded from {out}" + ); + } + + #[test] + fn stakeholder_missing_user_id_is_skipped() { + let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"T\",\"owner_id\":\"u\",\"owner_name\":\"Ann\",\"status\":\"done\"},\"stakeholders\":[{\"user_id\":\"\",\"role\":\"requester\",\"channel\":\"in_app\"},{\"user_id\":\"u2\",\"role\":\"requester\",\"channel\":\"in_app\"}],\"event_type\":\"completed\",\"notify_config\":{\"version\":\"1.0\",\"include_manager_on_complete\":true}}"); + assert!( + out.contains("\"intent_count\":1"), + "expected the empty user_id entry to be skipped in {out}" + ); + } + + #[test] + fn non_object_stakeholder_element_stops_scanning() { + let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"T\",\"owner_id\":\"u\",\"owner_name\":\"Ann\",\"status\":\"done\"},\"stakeholders\":[{\"user_id\":\"u1\",\"role\":\"requester\",\"channel\":\"in_app\"},42],\"event_type\":\"completed\",\"notify_config\":{\"version\":\"1.0\",\"include_manager_on_complete\":true}}"); + assert!( + out.contains("\"intent_count\":1"), + "expected scanning to stop at the non-object element in {out}" + ); } -} \ No newline at end of file + #[test] + fn unterminated_stakeholder_object_stops_scanning() { + let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"T\",\"owner_id\":\"u\",\"owner_name\":\"Ann\",\"status\":\"done\"},\"event_type\":\"completed\",\"notify_config\":{\"version\":\"1.0\",\"include_manager_on_complete\":true},\"stakeholders\":[{\"user_id\":\"u1\",\"role\":\"requester\",\"channel\":\"in_app\"},{\"user_id\":\"u2\"]}"); + assert!( + out.contains("\"intent_count\":1"), + "expected scanning to stop at the unterminated object in {out}" + ); + } + + #[test] + fn missing_channel_defaults_to_in_app() { + let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"T\",\"owner_id\":\"u\",\"owner_name\":\"Ann\",\"status\":\"done\"},\"stakeholders\":[{\"user_id\":\"u1\",\"role\":\"requester\"}],\"event_type\":\"completed\",\"notify_config\":{\"version\":\"1.0\",\"include_manager_on_complete\":true}}"); + assert!( + out.contains("\"channel\":\"in_app\""), + "expected default channel in {out}" + ); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon here"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + assert_eq!(string_value_after(b":\"ok\""), b"ok"); + } + + #[test] + fn extract_string_returns_empty_when_key_missing() { + assert_eq!(extract_string(b"{\"other\":\"x\"}", b"\"missing\""), b""); + } + + #[test] + fn extract_string_at_depth_returns_empty_when_key_missing() { + assert_eq!(extract_string_at_depth(b"{}", b"\"missing\"", 1), b""); + } + + #[test] + fn object_after_key_at_depth_returns_none_for_non_object_value() { + assert_eq!( + object_after_key_at_depth(b"\"item\":5", b"\"item\"", 0), + None + ); + } + + #[test] + fn array_after_key_at_depth_returns_none_for_non_array_value() { + assert_eq!( + array_after_key_at_depth(b"\"item\":5", b"\"item\"", 0), + None + ); + } + + #[test] + fn extract_bool_handles_false_and_neither() { + assert_eq!(extract_bool(b"\"k\":false", b"\"k\""), Some(false)); + assert_eq!(extract_bool(b"\"k\":maybe", b"\"k\""), None); + } + + #[test] + fn skip_ws_trims_leading_whitespace() { + assert_eq!(skip_ws(b" abc"), b"abc"); + assert_eq!(skip_ws(b"abc"), b"abc"); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } +} From 88a2fc3e92fee41b16f4395044614715acaea4c9 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:14:15 -0600 Subject: [PATCH 02/15] test(core-normalize-participants): backfill coverage to 100%/96%/95% Removed unreachable helper functions and added tests for the remaining branches (invalid input, id/name/email fallback matching, malformed array elements), per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../core-normalize-participants/src/main.rs | 309 +++++++++--------- 1 file changed, 153 insertions(+), 156 deletions(-) diff --git a/capability-src/core-normalize-participants/src/main.rs b/capability-src/core-normalize-participants/src/main.rs index b6bd9bc..486dddb 100644 --- a/capability-src/core-normalize-participants/src/main.rs +++ b/capability-src/core-normalize-participants/src/main.rs @@ -209,7 +209,15 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { matched += 1; i = copy(out, i, match_id); i = copy(out, i, b"\",\"display_name\":\""); - i = copy_json_escaped(out, i, if match_name.is_empty() { name } else { match_name }); + i = copy_json_escaped( + out, + i, + if match_name.is_empty() { + name + } else { + match_name + }, + ); i = copy(out, i, b"\",\"email\":"); if match_email.is_empty() { i = copy(out, i, b"null"); @@ -228,7 +236,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { i = write_u32(out, i, matched); i = copy(out, i, b",\"unmatched_count\":"); i = write_u32(out, i, unmatched); - i = copy(out, i, b",\"reason_code\":\"ok\",\"evaluation_trace\":[\"normalized "); + i = copy( + out, + i, + b",\"reason_code\":\"ok\",\"evaluation_trace\":[\"normalized ", + ); i = write_u32(out, i, total); i = copy(out, i, b" participants\",\"matched "); i = write_u32(out, i, matched); @@ -238,7 +250,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { fn fail(out: &mut [u8], code: &[u8]) -> usize { let mut i = 0usize; - i = copy(out, i, b"{\"participants\":[],\"matched_count\":0,\"unmatched_count\":0,\"reason_code\":\""); + i = copy( + out, + i, + b"{\"participants\":[],\"matched_count\":0,\"unmatched_count\":0,\"reason_code\":\"", + ); i = copy(out, i, code); i = copy(out, i, b"\",\"evaluation_trace\":[]}"); i @@ -358,13 +374,6 @@ fn extract_string<'a>(hay: &'a [u8], key: &[u8]) -> &'a [u8] { string_value_after(&hay[pos + key.len()..]) } -fn extract_string_at_depth<'a>(hay: &'a [u8], key: &[u8], depth: i32) -> &'a [u8] { - let Some(pos) = find_key_at_depth(hay, key, depth) else { - return b""; - }; - string_value_after(&hay[pos + key.len()..]) -} - fn object_after_key_at_depth<'a>(hay: &'a [u8], key: &[u8], depth: i32) -> Option<&'a [u8]> { let pos = find_key_at_depth(hay, key, depth)?; let after = &hay[pos + key.len()..]; @@ -389,93 +398,6 @@ fn array_after_key_at_depth<'a>(hay: &'a [u8], key: &[u8], depth: i32) -> Option Some(&rest[..=end]) } -fn extract_bool(hay: &[u8], key: &[u8]) -> Option { - let pos = find(hay, key)?; - let after = &hay[pos + key.len()..]; - let colon = after.iter().position(|b| *b == b':')?; - let rest = skip_ws(&after[colon + 1..]); - if rest.starts_with(b"true") { - Some(true) - } else if rest.starts_with(b"false") { - Some(false) - } else { - None - } -} - -fn extract_i32(hay: &[u8], key: &[u8]) -> Option { - let pos = find(hay, key)?; - let after = &hay[pos + key.len()..]; - let colon = after.iter().position(|b| *b == b':')?; - let rest = skip_ws(&after[colon + 1..]); - parse_i32(rest) -} - -fn parse_i32(rest: &[u8]) -> Option { - if rest.is_empty() { - return None; - } - let mut neg = false; - let mut j = 0usize; - if rest[0] == b'-' { - neg = true; - j = 1; - } - if j >= rest.len() || rest[j] < b'0' || rest[j] > b'9' { - return None; - } - let mut n: i32 = 0; - while j < rest.len() && rest[j] >= b'0' && rest[j] <= b'9' { - n = n * 10 + (rest[j] - b'0') as i32; - j += 1; - } - Some(if neg { -n } else { n }) -} - -fn parse_number_millis(hay: &[u8], key: &[u8]) -> Option { - let pos = find(hay, key)?; - let after = &hay[pos + key.len()..]; - let colon = after.iter().position(|b| *b == b':')?; - let rest = skip_ws(&after[colon + 1..]); - if rest.is_empty() { - return None; - } - let mut whole: u32 = 0; - let mut frac: u32 = 0; - let mut frac_digits = 0u32; - let mut seen_dot = false; - let mut j = 0usize; - while j < rest.len() { - let b = rest[j]; - if b == b',' || b == b'}' || b == b']' || b == b' ' || b == b'\n' { - break; - } - if b == b'.' { - seen_dot = true; - j += 1; - continue; - } - if b < b'0' || b > b'9' { - break; - } - let digit = (b - b'0') as u32; - if seen_dot { - if frac_digits < 3 { - frac = frac * 10 + digit; - frac_digits += 1; - } - } else { - whole = whole * 10 + digit; - } - j += 1; - } - while frac_digits < 3 { - frac *= 10; - frac_digits += 1; - } - Some(whole * 1000 + frac) -} - fn copy(out: &mut [u8], at: usize, bytes: &[u8]) -> usize { let end = at + bytes.len(); if end > out.len() { @@ -526,15 +448,6 @@ fn write_u32(out: &mut [u8], mut i: usize, mut n: u32) -> usize { i } -fn write_i32(out: &mut [u8], mut i: usize, n: i32) -> usize { - if n < 0 { - i = copy(out, i, b"-"); - write_u32(out, i, (-n) as u32) - } else { - write_u32(out, i, n as u32) - } -} - fn ascii_lower(b: u8) -> u8 { if b >= b'A' && b <= b'Z' { b + 32 @@ -585,53 +498,6 @@ fn trim_ascii(s: &[u8]) -> &[u8] { &s[start..end] } -/// Days since 1970-01-01 for YYYY-MM-DD (Howard Hinnant civil_from_days inverse). -fn parse_ymd_days(s: &[u8]) -> Option { - if s.len() < 10 || s[4] != b'-' || s[7] != b'-' { - return None; - } - let y = parse_i32(&s[0..4])?; - let m = parse_i32(&s[5..7])?; - let d = parse_i32(&s[8..10])?; - if m < 1 || m > 12 || d < 1 || d > 31 { - return None; - } - let y = y as i32 - if m <= 2 { 1 } else { 0 }; - let era = if y >= 0 { y } else { y - 399 } / 400; - let yoe = (y - era * 400) as u32; - let mp = if m > 2 { (m - 3) as u32 } else { (m + 9) as u32 }; - let doy = (153 * mp + 2) / 5 + d as u32 - 1; - let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; - Some((era * 146097 + doe as i32) - 719468) -} - -fn format_score_millis(out: &mut [u8], millis: u32) -> usize { - let whole = millis / 1000; - let frac = millis % 1000; - let mut i = write_u32(out, 0, whole); - i = copy(out, i, b"."); - // always 3 digits for determinism in contract examples we may trim; write without trailing zeros carefully - // Use up to 3 digits, trim trailing zeros but keep at least one if frac!=0? Contract examples use 0.785 / 1.0 - if frac == 0 { - i = copy(out, i, b"0"); - return i; - } - let d0 = (frac / 100) as u8; - let d1 = ((frac / 10) % 10) as u8; - let d2 = (frac % 10) as u8; - out[i] = b'0' + d0; - i += 1; - if d1 != 0 || d2 != 0 { - out[i] = b'0' + d1; - i += 1; - if d2 != 0 { - out[i] = b'0' + d2; - i += 1; - } - } - i -} - #[cfg(not(test))] #[panic_handler] fn panic(_: &core::panic::PanicInfo<'_>) -> ! { @@ -651,13 +517,144 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"raw_participants\":[{\"name\":\"Ada Lovelace\",\"email\":\"Ada@Loop.Dev\"},{\"name\":\"bob smith\",\"email\":\"bob@loop.dev\"},{\"name\":\"Unknown Person\",\"email\":\"stranger@example.com\"}],\"workspace_members\":[{\"id\":\"user-ada\",\"name\":\"Ada Lovelace\",\"email\":\"ada@loop.dev\"},{\"id\":\"user-bob\",\"name\":\"Bob Smith\",\"email\":\"bob@loop.dev\"}],\"normalize_config\":{\"version\":\"1.0\"}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_happy() { let out = run("{\"raw_participants\":[{\"name\":\"Ada Lovelace\",\"email\":null}],\"workspace_members\":[{\"id\":\"user-ada\",\"name\":\"Ada Lovelace\",\"email\":\"ada@loop.dev\"}],\"normalize_config\":{\"version\":\"1.0\"}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); + } + + #[test] + fn missing_required_fields_yields_invalid_input() { + let out = run("{\"workspace_members\":[],\"normalize_config\":{}}"); + assert!( + out.contains("\"reason_code\":\"invalid_input\""), + "expected invalid_input in {out}" + ); + } + + #[test] + fn member_with_missing_name_or_email_falls_back_on_match() { + let out = run("{\"raw_participants\": [ {\"name\":\"Dave X\",\"email\":\"dave@x.com\"},{\"name\":\"Carol\",\"email\":null} ],\"workspace_members\": [ {\"id\":\"user-dave\",\"name\":\"\",\"email\":\"dave@x.com\"},{\"id\":\"user-carol\",\"name\":\"Carol\",\"email\":\"\"} ],\"normalize_config\": {\"version\":\"1.0\"}}"); + assert!( + out.contains("\"display_name\":\"Dave X\""), + "expected name fallback to raw participant's own name in {out}" + ); + assert!( + out.contains("\"email\":null"), + "expected null email for member matched with no email on file in {out}" + ); + } + + #[test] + fn member_with_empty_or_too_long_id_is_skipped() { + let long_id = "x".repeat(200); + let input = format!( + "{{\"raw_participants\":[{{\"name\":\"Solo\",\"email\":\"solo@x.com\"}}],\"workspace_members\":[{{\"id\":\"\",\"name\":\"NoId\",\"email\":\"noid@x.com\"}},{{\"id\":\"{long_id}\",\"name\":\"TooLong\",\"email\":\"solo@x.com\"}}],\"normalize_config\":{{\"version\":\"1.0\"}}}}" + ); + let out = run(&input); + assert!( + out.contains("\"match_method\":\"none\""), + "expected no match since both members were skipped in {out}" + ); + } + + #[test] + fn non_object_member_element_stops_scanning() { + let out = run("{\"raw_participants\":[{\"name\":\"A\",\"email\":\"a@x.com\"}],\"workspace_members\":[{\"id\":\"user-a\",\"name\":\"A\",\"email\":\"a@x.com\"},42],\"normalize_config\":{\"version\":\"1.0\"}}"); + assert!( + out.contains("\"match_method\":\"email\""), + "expected the first member to still be usable in {out}" + ); + } + + #[test] + fn non_object_participant_element_stops_scanning() { + let out = run("{\"raw_participants\":[{\"name\":\"A\",\"email\":\"a@x.com\"},42],\"workspace_members\":[{\"id\":\"user-a\",\"name\":\"A\",\"email\":\"a@x.com\"}],\"normalize_config\":{\"version\":\"1.0\"}}"); + assert!( + out.contains("\"matched_count\":1"), + "expected scanning to stop at the non-object element in {out}" + ); + } + + #[test] + fn unterminated_member_object_stops_scanning() { + let out = run("{\"raw_participants\":[{\"name\":\"A\",\"email\":\"a@x.com\"}],\"normalize_config\":{\"version\":\"1.0\"},\"workspace_members\":[{\"id\":\"user-a\",\"name\":\"A\",\"email\":\"a@x.com\"},{\"id\":\"user-b\"]}"); + assert!( + out.contains("\"match_method\":\"email\""), + "expected the first member to still be usable in {out}" + ); + } + + #[test] + fn unterminated_participant_object_stops_scanning() { + let out = run("{\"workspace_members\":[{\"id\":\"user-a\",\"name\":\"A\",\"email\":\"a@x.com\"}],\"normalize_config\":{\"version\":\"1.0\"},\"raw_participants\":[{\"name\":\"A\",\"email\":\"a@x.com\"},{\"name\":\"B\"]}"); + assert!( + out.contains("\"matched_count\":1"), + "expected scanning to stop at the unterminated element in {out}" + ); + } + + #[test] + fn member_clone_is_a_bitwise_copy() { + let m = Member { + id: [1u8; STR_MAX], + id_len: 1, + name: [2u8; STR_MAX], + name_len: 1, + email: [3u8; STR_MAX], + email_len: 1, + }; + let cloned = m.clone(); + assert_eq!(cloned.id_len, m.id_len); + assert_eq!(cloned.email[0], 3u8); } -} \ No newline at end of file + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon here"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + assert_eq!(string_value_after(b":\"ok\""), b"ok"); + } + + #[test] + fn extract_string_returns_empty_when_key_missing() { + assert_eq!(extract_string(b"{\"other\":\"x\"}", b"\"missing\""), b""); + } + + #[test] + fn object_after_key_at_depth_returns_none_for_non_object_value() { + assert_eq!( + object_after_key_at_depth(b"\"item\":5", b"\"item\"", 0), + None + ); + } + + #[test] + fn array_after_key_at_depth_returns_none_for_non_array_value() { + assert_eq!( + array_after_key_at_depth(b"\"item\":5", b"\"item\"", 0), + None + ); + } + + #[test] + fn skip_ws_trims_leading_whitespace() { + assert_eq!(skip_ws(b" abc"), b"abc"); + assert_eq!(skip_ws(b"abc"), b"abc"); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } +} From 75a703ad7e1c2bd00e3407f59f32282f01bec639 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:18:11 -0600 Subject: [PATCH 03/15] test(core-select-items-for-followup): backfill coverage to 100%/98%/97% Added tests for quiet-hours (both wrap and non-wrap), snooze, budget, pressure, escalation, and array-scanning branches. Removed the last_activity/no-signal skip path and its approaching_due/days_between date-math dependents: pressure < min_pressure already returns above, so pressure >= min_pressure always holds by the time that code ran, making it genuinely unreachable, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../src/main.rs | 361 ++++++++++++------ 1 file changed, 254 insertions(+), 107 deletions(-) diff --git a/capability-src/core-select-items-for-followup/src/main.rs b/capability-src/core-select-items-for-followup/src/main.rs index 742e8cb..7aa2752 100644 --- a/capability-src/core-select-items-for-followup/src/main.rs +++ b/capability-src/core-select-items-for-followup/src/main.rs @@ -92,7 +92,6 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { let respect_quiet = extract_bool(config, b"\"respect_quiet_hours\"").unwrap_or(true); let escalate_after = extract_i32(config, b"\"escalate_after_nudges\"").unwrap_or(3); let min_pressure = extract_number_scaled(config, b"\"min_pressure_for_soft\"").unwrap_or(400); - let soft_days = extract_i32(config, b"\"soft_days_before_due\"").unwrap_or(2); let ref_date = &ref_dt[..ref_dt.len().min(10)]; @@ -132,7 +131,6 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { let snoozed = extract_optional_string(item, b"\"snoozed_until\""); let nudge_count = extract_i32(item, b"\"nudge_count\"").unwrap_or(0); let pressure = extract_number_scaled(item, b"\"pressure_score\"").unwrap_or(0); - let last_activity = extract_string(item, b"\"last_activity_at\""); if !snoozed.is_empty() && snoozed > ref_dt { push_skipped( @@ -172,8 +170,7 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { } let max_nudges = user_pref_i32(prefs, owner, b"\"max_nudges_per_day\"").unwrap_or(2); - let sent_today = - user_pref_i32(prefs, owner, b"\"nudges_sent_today\"").unwrap_or(0); + let sent_today = user_pref_i32(prefs, owner, b"\"nudges_sent_today\"").unwrap_or(0); if sent_today >= max_nudges { push_skipped( &mut skipped, @@ -223,38 +220,17 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { return; } - if approaching_due(due, ref_date, soft_days) || pressure >= min_pressure { - push_selected( - &mut selected, - &mut sel_count, - item_id, - b"soft", - b"approaching due date or sufficient pressure", - b"", - ); - return; - } - - if !last_activity.is_empty() { - let act_date = &last_activity[..last_activity.len().min(10)]; - if act_date >= ref_date { - push_skipped( - &mut skipped, - &mut skip_count, - item_id, - b"recently_active", - b"owner recently active on item", - ); - return; - } - } - - push_skipped( - &mut skipped, - &mut skip_count, + // `pressure < min_pressure` already returned above, so the + // `pressure >= min_pressure` disjunct always holds here: every + // remaining item is a "soft" selection, never a + // last_activity/no-signal skip. + push_selected( + &mut selected, + &mut sel_count, item_id, - b"low_pressure", - b"no follow-up signal", + b"soft", + b"approaching due date or sufficient pressure", + b"", ); }); @@ -275,15 +251,15 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { } t = copy(&mut trace, t, b"]"); - write_output(out, &selected[..sel_count], &skipped[..skip_count], &trace[..t]) + write_output( + out, + &selected[..sel_count], + &skipped[..skip_count], + &trace[..t], + ) } -fn write_output( - out: &mut [u8], - selected: &[Selected], - skipped: &[Skipped], - trace: &[u8], -) -> usize { +fn write_output(out: &mut [u8], selected: &[Selected], skipped: &[Skipped], trace: &[u8]) -> usize { let mut i = 0usize; i = copy(out, i, b"{\"selected\":["); for (idx, s) in selected.iter().enumerate() { @@ -323,7 +299,11 @@ fn write_output( fn fail(out: &mut [u8], code: &[u8], trace: &[u8]) -> usize { let mut i = 0usize; - i = copy(out, i, b"{\"selected\":[],\"skipped\":[],\"reason_code\":\""); + i = copy( + out, + i, + b"{\"selected\":[],\"skipped\":[],\"reason_code\":\"", + ); i = copy(out, i, code); i = copy(out, i, b"\",\"evaluation_trace\":"); i = copy(out, i, trace); @@ -480,63 +460,6 @@ fn parse_two_digit_hour(s: &[u8]) -> i32 { i32::from(h0 - b'0') * 10 + i32::from(h1 - b'0') } -fn approaching_due(due: &[u8], ref_date: &[u8], soft_days: i32) -> bool { - if due.is_empty() || ref_date.is_empty() { - return false; - } - if due == ref_date { - return true; - } - if due > ref_date { - return days_between(ref_date, due) <= soft_days; - } - false -} - -fn days_between(from: &[u8], to: &[u8]) -> i32 { - if from.len() < 10 || to.len() < 10 { - return 999; - } - let fy = parse_year(from); - let fm = parse_month(from); - let fd = parse_day(from); - let ty = parse_year(to); - let tm = parse_month(to); - let td = parse_day(to); - let from_days = fy * 372 + fm * 31 + fd; - let to_days = ty * 372 + tm * 31 + td; - to_days - from_days -} - -fn parse_year(d: &[u8]) -> i32 { - parse_digits(&d[..4.min(d.len())]) -} - -fn parse_month(d: &[u8]) -> i32 { - if d.len() < 7 { - return 0; - } - parse_digits(&d[5..7]) -} - -fn parse_day(d: &[u8]) -> i32 { - if d.len() < 10 { - return 0; - } - parse_digits(&d[8..10]) -} - -fn parse_digits(s: &[u8]) -> i32 { - let mut val = 0i32; - for &b in s { - if b < b'0' || b > b'9' { - break; - } - val = val.saturating_mul(10).saturating_add(i32::from(b - b'0')); - } - val -} - fn write_usize(out: &mut [u8], at: usize, n: usize) -> usize { if n == 0 { return copy(out, at, b"0"); @@ -612,9 +535,7 @@ fn extract_number_scaled(hay: &[u8], key: &[u8]) -> Option { frac_digits += 1; } } else { - whole = whole - .saturating_mul(10) - .saturating_add(i32::from(b - b'0')); + whole = whole.saturating_mul(10).saturating_add(i32::from(b - b'0')); } } if !any { @@ -845,19 +766,245 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-08\",\"status\":\"open\",\"last_activity_at\":\"2026-08-05T10:00:00Z\",\"nudge_count\":0,\"pressure_score\":0.85,\"snoozed_until\":null}],\"user_preferences\":{\"user-ada\":{\"quiet_hours\":{\"start\":\"20:00\",\"end\":\"08:00\",\"timezone\":\"America/Los_Angeles\"},\"max_nudges_per_day\":2,\"nudges_sent_today\":0}},\"reference_datetime\":\"2026-08-07T22:30:00Z\",\"followup_config\":{\"version\":\"1.1\",\"soft_days_before_due\":2,\"direct_days_overdue\":1,\"escalate_after_nudges\":3,\"min_pressure_for_soft\":0.4,\"respect_quiet_hours\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_happy() { let out = run("{\"open_items\":[{\"id\":\"ai-9\",\"owner_id\":\"user-bob\",\"due_date\":\"2026-08-01\",\"status\":\"open\",\"last_activity_at\":\"2026-07-28T09:00:00Z\",\"nudge_count\":3,\"pressure_score\":0.95,\"snoozed_until\":null}],\"user_preferences\":{\"user-bob\":{\"quiet_hours\":null,\"max_nudges_per_day\":3,\"nudges_sent_today\":0}},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"version\":\"1.1\",\"soft_days_before_due\":2,\"direct_days_overdue\":1,\"escalate_after_nudges\":3,\"min_pressure_for_soft\":0.4,\"respect_quiet_hours\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_03_sad() { let out = run("{\"open_items\":[],\"user_preferences\":{},\"reference_datetime\":\"\",\"followup_config\":{\"version\":\"1.1\",\"soft_days_before_due\":2,\"direct_days_overdue\":1,\"escalate_after_nudges\":3,\"min_pressure_for_soft\":0.4,\"respect_quiet_hours\":true}}"); - assert!(out.contains("\"reason_code\":\"config_error\""), "expected config_error in {out}"); + assert!( + out.contains("\"reason_code\":\"config_error\""), + "expected config_error in {out}" + ); + } + + #[test] + fn missing_open_items_yields_config_error() { + let out = run("{\"user_preferences\":{},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"version\":\"1.1\"}}"); + assert!( + out.contains("\"reason_code\":\"config_error\""), + "expected config_error in {out}" + ); + assert!( + out.contains("open_items missing"), + "expected reason detail in {out}" + ); + } + + #[test] + fn snoozed_item_is_skipped() { + let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-01\",\"nudge_count\":0,\"pressure_score\":0.9,\"snoozed_until\":\"2099-01-01T00:00:00Z\"}],\"user_preferences\":{},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":false,\"min_pressure_for_soft\":0.4}}"); + assert!( + out.contains("\"reason\":\"snoozed\""), + "expected snoozed skip in {out}" + ); + } + + #[test] + fn quiet_hours_wrap_around_midnight_skips_item() { + let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-01\",\"nudge_count\":0,\"pressure_score\":0.9,\"snoozed_until\":null}],\"user_preferences\":{\"user-ada\":{\"quiet_hours\":{\"start\":\"22:00\",\"end\":\"06:00\"},\"max_nudges_per_day\":2,\"nudges_sent_today\":0}},\"reference_datetime\":\"2026-08-07T23:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":true,\"min_pressure_for_soft\":0.4}}"); + assert!( + out.contains("\"reason\":\"quiet_hours\""), + "expected quiet_hours skip in {out}" + ); + } + + #[test] + fn quiet_hours_same_day_but_outside_window_proceeds() { + let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-01\",\"nudge_count\":0,\"pressure_score\":0.9,\"snoozed_until\":null}],\"user_preferences\":{\"user-ada\":{\"quiet_hours\":{\"start\":\"20:00\",\"end\":\"22:00\"},\"max_nudges_per_day\":2,\"nudges_sent_today\":0}},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":true,\"min_pressure_for_soft\":0.4}}"); + assert!( + !out.contains("\"reason\":\"quiet_hours\""), + "expected item not skipped for quiet hours in {out}" + ); + } + + #[test] + fn owner_without_preferences_skips_quiet_hours_check() { + let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-nobody\",\"due_date\":\"2026-08-01\",\"nudge_count\":0,\"pressure_score\":0.9,\"snoozed_until\":null}],\"user_preferences\":{},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":true,\"min_pressure_for_soft\":0.4}}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected evaluation to proceed in {out}" + ); + } + + #[test] + fn budget_exceeded_is_skipped() { + let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-01\",\"nudge_count\":0,\"pressure_score\":0.9,\"snoozed_until\":null}],\"user_preferences\":{\"user-ada\":{\"max_nudges_per_day\":1,\"nudges_sent_today\":2}},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":false,\"min_pressure_for_soft\":0.4}}"); + assert!( + out.contains("\"reason\":\"budget_exceeded\""), + "expected budget_exceeded skip in {out}" + ); + } + + #[test] + fn low_pressure_is_skipped() { + let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-01\",\"nudge_count\":0,\"pressure_score\":0.1,\"snoozed_until\":null}],\"user_preferences\":{},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":false,\"min_pressure_for_soft\":0.4}}"); + assert!( + out.contains("\"reason\":\"low_pressure\""), + "expected low_pressure skip in {out}" + ); + } + + #[test] + fn overdue_with_enough_nudges_escalates() { + let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-01\",\"nudge_count\":5,\"pressure_score\":0.9,\"snoozed_until\":null}],\"user_preferences\":{},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":false,\"min_pressure_for_soft\":0.4,\"escalate_after_nudges\":3}}"); + assert!( + out.contains("\"intensity\":\"escalate\""), + "expected escalate selection in {out}" + ); + assert!(out.contains("\"recommended_channel\":\"manager\"")); + } + + #[test] + fn overdue_without_enough_nudges_is_direct() { + let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-01\",\"nudge_count\":0,\"pressure_score\":0.9,\"snoozed_until\":null}],\"user_preferences\":{},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":false,\"min_pressure_for_soft\":0.4,\"escalate_after_nudges\":3}}"); + assert!( + out.contains("\"intensity\":\"direct\""), + "expected direct selection in {out}" + ); + } + + #[test] + fn due_today_is_soft_selection() { + let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-07\",\"nudge_count\":0,\"pressure_score\":0.9,\"snoozed_until\":null}],\"user_preferences\":{},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":false,\"min_pressure_for_soft\":0.4}}"); + assert!( + out.contains("\"intensity\":\"soft\""), + "expected soft selection in {out}" + ); + } + + #[test] + fn max_items_cutoff_stops_after_limit() { + let mut items = String::new(); + for n in 0..20 { + if n > 0 { + items.push(','); + } + items.push_str(&format!( + "{{\"id\":\"ai-{n}\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-01\",\"nudge_count\":0,\"pressure_score\":0.9,\"snoozed_until\":null}}" + )); + } + let input = format!( + "{{\"open_items\":[{items}],\"user_preferences\":{{}},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{{\"respect_quiet_hours\":false,\"min_pressure_for_soft\":0.4}}}}" + ); + let out = run(&input); + assert!( + out.contains("16 item evaluateds"), + "expected the 16-item cap in {out}" + ); + } + + #[test] + fn non_object_item_element_stops_scanning() { + let out = run("{\"open_items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-01\",\"nudge_count\":0,\"pressure_score\":0.9,\"snoozed_until\":null},42],\"user_preferences\":{},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":false,\"min_pressure_for_soft\":0.4}}"); + assert!( + out.contains("1 item evaluated"), + "expected scanning to stop at the non-object element in {out}" + ); } -} \ No newline at end of file + #[test] + fn empty_open_items_array_evaluates_to_zero_items() { + let out = run("{\"open_items\":[],\"user_preferences\":{},\"reference_datetime\":\"2026-08-07T10:00:00Z\",\"followup_config\":{\"respect_quiet_hours\":false}}"); + assert!( + out.contains("0 item evaluateds"), + "expected zero items in {out}" + ); + } + + #[test] + fn parse_two_digit_hour_rejects_non_digits() { + assert_eq!(parse_two_digit_hour(b"ab"), 0); + assert_eq!(parse_two_digit_hour(b"9"), 0); + assert_eq!(parse_two_digit_hour(b"23"), 23); + } + + #[test] + fn parse_ref_hour_defaults_when_no_t_separator() { + assert_eq!(parse_ref_hour(b"2026-08-07"), 0); + } + + #[test] + fn extract_number_scaled_handles_negative_and_missing() { + assert_eq!(extract_number_scaled(b"\"k\":-1.5", b"\"k\""), Some(-1500)); + assert_eq!(extract_number_scaled(b"\"k\":oops", b"\"k\""), None); + assert_eq!(extract_number_scaled(b"{}", b"\"missing\""), None); + } + + #[test] + fn extract_i32_handles_negative_and_missing() { + assert_eq!(extract_i32(b"\"k\":-7", b"\"k\""), Some(-7)); + assert_eq!(extract_i32(b"\"k\":oops", b"\"k\""), None); + assert_eq!(extract_i32(b"{}", b"\"missing\""), None); + } + + #[test] + fn extract_bool_handles_false_and_neither() { + assert_eq!(extract_bool(b"\"k\":false", b"\"k\""), Some(false)); + assert_eq!(extract_bool(b"\"k\":maybe", b"\"k\""), None); + } + + #[test] + fn extract_optional_string_handles_missing_null_and_malformed() { + assert_eq!(extract_optional_string(b"{}", b"\"k\""), b""); + assert_eq!(extract_optional_string(b"\"k\"no-colon", b"\"k\""), b""); + assert_eq!(extract_optional_string(b"\"k\":null", b"\"k\""), b""); + assert_eq!(extract_optional_string(b"\"k\":5", b"\"k\""), b""); + assert_eq!( + extract_optional_string(b"\"k\":\"unterminated", b"\"k\""), + b"" + ); + assert_eq!(extract_optional_string(b"\"k\":\"ok\"", b"\"k\""), b"ok"); + } + + #[test] + fn object_after_key_handles_missing_and_non_object() { + assert_eq!(object_after_key(b"{}", b"\"missing\""), None); + assert_eq!(object_after_key(b"\"k\":5", b"\"k\""), None); + } + + #[test] + fn array_after_key_at_depth_handles_non_array() { + assert_eq!(array_after_key_at_depth(b"\"k\":5", b"\"k\"", 0), None); + } + + #[test] + fn user_pref_object_handles_empty_prefs_and_owner() { + assert_eq!(user_pref_object(b"", b"user-a"), None); + assert_eq!(user_pref_object(b"{}", b""), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon here"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn write_usize_handles_zero_and_multiple_digits() { + let mut buf = [0u8; 8]; + let n = write_usize(&mut buf, 0, 0); + assert_eq!(&buf[..n], b"0"); + let mut buf2 = [0u8; 8]; + let n2 = write_usize(&mut buf2, 0, 123); + assert_eq!(&buf2[..n2], b"123"); + } +} From 712496de462cdab9043f1acc1c89527ac9c106be Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:20:30 -0600 Subject: [PATCH 04/15] test(core-evaluate-completion-quality): backfill coverage to 100%/95%/96% Removed unreachable helper functions and added tests for the verdict branches (fail vs needs_evidence under low/high pressure), multi-gap array output, and parser edge cases, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../src/main.rs | 226 ++++++++++-------- 1 file changed, 121 insertions(+), 105 deletions(-) diff --git a/capability-src/core-evaluate-completion-quality/src/main.rs b/capability-src/core-evaluate-completion-quality/src/main.rs index 0380fd6..97f61da 100644 --- a/capability-src/core-evaluate-completion-quality/src/main.rs +++ b/capability-src/core-evaluate-completion-quality/src/main.rs @@ -75,7 +75,8 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { let pressure_millis = parse_number_millis(item, b"\"pressure_score\"").unwrap_or(0); let high_thresh = parse_number_millis(config, b"\"high_pressure_threshold\"").unwrap_or(700); - let require_ev = extract_bool(config, b"\"require_evidence_when_high_pressure\"").unwrap_or(true); + let require_ev = + extract_bool(config, b"\"require_evidence_when_high_pressure\"").unwrap_or(true); let min_note = extract_i32(config, b"\"min_note_length\"").unwrap_or(8) as u32; let high_pressure = pressure_millis >= high_thresh; @@ -143,7 +144,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { i = copy(out, i, gaps[g]); i = copy(out, i, b"\""); } - i = copy(out, i, b"],\"reason_code\":\"ok\",\"evaluation_trace\":[\"high_pressure="); + i = copy( + out, + i, + b"],\"reason_code\":\"ok\",\"evaluation_trace\":[\"high_pressure=", + ); i = copy(out, i, if high_pressure { b"true" } else { b"false" }); i = copy(out, i, b"\",\"evidence="); i = write_u32(out, i, evidence_count); @@ -183,7 +188,11 @@ fn fail(out: &mut [u8], code: &[u8], item_id: &[u8]) -> usize { let mut i = 0usize; i = copy(out, i, b"{\"item_id\":\""); i = copy_json_escaped(out, i, item_id); - i = copy(out, i, b"\",\"quality_score\":0,\"verdict\":\"fail\",\"gaps\":[],\"reason_code\":\""); + i = copy( + out, + i, + b"\",\"quality_score\":0,\"verdict\":\"fail\",\"gaps\":[],\"reason_code\":\"", + ); i = copy(out, i, code); i = copy(out, i, b"\",\"evaluation_trace\":[]}"); i @@ -200,17 +209,6 @@ fn skip_ws(s: &[u8]) -> &[u8] { rest } -fn skip_ws_comma(s: &[u8]) -> usize { - let mut i = 0usize; - while i < s.len() { - match s[i] { - b' ' | b'\n' | b'\t' | b'\r' | b',' => i += 1, - _ => break, - } - } - i -} - fn balanced_end(s: &[u8], open: u8, close: u8) -> Option { let mut depth = 0i32; let mut in_str = false; @@ -303,13 +301,6 @@ fn extract_string<'a>(hay: &'a [u8], key: &[u8]) -> &'a [u8] { string_value_after(&hay[pos + key.len()..]) } -fn extract_string_at_depth<'a>(hay: &'a [u8], key: &[u8], depth: i32) -> &'a [u8] { - let Some(pos) = find_key_at_depth(hay, key, depth) else { - return b""; - }; - string_value_after(&hay[pos + key.len()..]) -} - fn object_after_key_at_depth<'a>(hay: &'a [u8], key: &[u8], depth: i32) -> Option<&'a [u8]> { let pos = find_key_at_depth(hay, key, depth)?; let after = &hay[pos + key.len()..]; @@ -471,85 +462,6 @@ fn write_u32(out: &mut [u8], mut i: usize, mut n: u32) -> usize { i } -fn write_i32(out: &mut [u8], mut i: usize, n: i32) -> usize { - if n < 0 { - i = copy(out, i, b"-"); - write_u32(out, i, (-n) as u32) - } else { - write_u32(out, i, n as u32) - } -} - -fn ascii_lower(b: u8) -> u8 { - if b >= b'A' && b <= b'Z' { - b + 32 - } else { - b - } -} - -fn eq_ignore_case(a: &[u8], b: &[u8]) -> bool { - if a.len() != b.len() { - return false; - } - for i in 0..a.len() { - if ascii_lower(a[i]) != ascii_lower(b[i]) { - return false; - } - } - true -} - -fn normalize_email(src: &[u8], dst: &mut [u8]) -> usize { - let mut i = 0usize; - let mut j = 0usize; - while i < src.len() && (src[i] == b' ' || src[i] == b'\t') { - i += 1; - } - let mut end = src.len(); - while end > i && (src[end - 1] == b' ' || src[end - 1] == b'\t') { - end -= 1; - } - while i < end && j < dst.len() { - dst[j] = ascii_lower(src[i]); - i += 1; - j += 1; - } - j -} - -fn trim_ascii(s: &[u8]) -> &[u8] { - let mut start = 0usize; - let mut end = s.len(); - while start < end && matches!(s[start], b' ' | b'\t' | b'\n' | b'\r') { - start += 1; - } - while end > start && matches!(s[end - 1], b' ' | b'\t' | b'\n' | b'\r') { - end -= 1; - } - &s[start..end] -} - -/// Days since 1970-01-01 for YYYY-MM-DD (Howard Hinnant civil_from_days inverse). -fn parse_ymd_days(s: &[u8]) -> Option { - if s.len() < 10 || s[4] != b'-' || s[7] != b'-' { - return None; - } - let y = parse_i32(&s[0..4])?; - let m = parse_i32(&s[5..7])?; - let d = parse_i32(&s[8..10])?; - if m < 1 || m > 12 || d < 1 || d > 31 { - return None; - } - let y = y as i32 - if m <= 2 { 1 } else { 0 }; - let era = if y >= 0 { y } else { y - 399 } / 400; - let yoe = (y - era * 400) as u32; - let mp = if m > 2 { (m - 3) as u32 } else { (m + 9) as u32 }; - let doy = (153 * mp + 2) / 5 + d as u32 - 1; - let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; - Some((era * 146097 + doe as i32) - 719468) -} - fn format_score_millis(out: &mut [u8], millis: u32) -> usize { let whole = millis / 1000; let frac = millis % 1000; @@ -596,25 +508,129 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"Send the revised proposal\",\"status\":\"done\",\"pressure_score\":0.9,\"completion_note\":\"Sent revised proposal to stakeholders\",\"evidence_refs\":[\"doc-123\"]},\"quality_config\":{\"version\":\"1.0\",\"high_pressure_threshold\":0.7,\"require_evidence_when_high_pressure\":true,\"min_note_length\":8}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_happy() { let out = run("{\"item\":{\"id\":\"ai-2\",\"title\":\"Fix production bug\",\"status\":\"done\",\"pressure_score\":0.95,\"completion_note\":\"done\",\"evidence_refs\":[]},\"quality_config\":{\"version\":\"1.0\",\"high_pressure_threshold\":0.7,\"require_evidence_when_high_pressure\":true,\"min_note_length\":8}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_03_sad() { let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"X\",\"status\":\"open\",\"pressure_score\":0.5,\"completion_note\":\"still working\",\"evidence_refs\":[]},\"quality_config\":{\"version\":\"1.0\",\"high_pressure_threshold\":0.7,\"require_evidence_when_high_pressure\":true,\"min_note_length\":8}}"); - assert!(out.contains("\"reason_code\":\"invalid_status\""), "expected invalid_status in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_status\""), + "expected invalid_status in {out}" + ); } #[test] fn use_case_04_sad() { let out = run("{\"item\":{\"id\":\"\",\"title\":\"X\",\"status\":\"done\",\"pressure_score\":0.5,\"completion_note\":\"done enough\",\"evidence_refs\":[]},\"quality_config\":{\"version\":\"1.0\",\"high_pressure_threshold\":0.7,\"require_evidence_when_high_pressure\":true,\"min_note_length\":8}}"); - assert!(out.contains("\"reason_code\":\"invalid_input\""), "expected invalid_input in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_input\""), + "expected invalid_input in {out}" + ); + } + + #[test] + fn missing_item_or_config_yields_invalid_input() { + let out = run("{}"); + assert!( + out.contains("\"reason_code\":\"invalid_input\""), + "expected invalid_input in {out}" + ); + } + + #[test] + fn short_note_and_missing_evidence_under_low_pressure_fails() { + let out = run("{\"item\":{\"id\":\"ai-1\",\"status\":\"done\",\"pressure_score\":0.1,\"completion_note\":\"hi\",\"evidence_refs\":[]},\"quality_config\":{\"high_pressure_threshold\":0.7,\"require_evidence_when_high_pressure\":true,\"min_note_length\":8}}"); + assert!( + out.contains("\"verdict\":\"fail\""), + "expected fail verdict in {out}" + ); + assert!(out.contains("\"gaps\":[\"short_note\"]")); + } + + #[test] + fn short_note_and_missing_evidence_under_high_pressure_needs_evidence_with_both_gaps() { + let out = run("{\"item\":{\"id\":\"ai-1\",\"status\":\"done\",\"pressure_score\":0.9,\"completion_note\":\"hi\",\"evidence_refs\":[]},\"quality_config\":{\"high_pressure_threshold\":0.7,\"require_evidence_when_high_pressure\":true,\"min_note_length\":8}}"); + assert!( + out.contains("\"verdict\":\"needs_evidence\""), + "expected needs_evidence verdict in {out}" + ); + assert!(out.contains("\"gaps\":[\"short_note\",\"missing_evidence\"]")); + } + + #[test] + fn short_note_with_evidence_present_needs_evidence() { + let out = run("{\"item\":{\"id\":\"ai-1\",\"status\":\"done\",\"pressure_score\":0.1,\"completion_note\":\"hi\",\"evidence_refs\":[\"doc-1\"]},\"quality_config\":{\"high_pressure_threshold\":0.7,\"require_evidence_when_high_pressure\":true,\"min_note_length\":8}}"); + assert!( + out.contains("\"verdict\":\"needs_evidence\""), + "expected needs_evidence verdict in {out}" + ); + assert!(out.contains("\"gaps\":[\"short_note\"]")); + } + + #[test] + fn extract_bool_handles_false_and_neither() { + assert_eq!(extract_bool(b"\"k\":false", b"\"k\""), Some(false)); + assert_eq!(extract_bool(b"\"k\":maybe", b"\"k\""), None); + } + + #[test] + fn extract_i32_handles_none() { + assert_eq!(extract_i32(b"{}", b"\"missing\""), None); + assert_eq!(extract_i32(b"\"k\":oops", b"\"k\""), None); } -} \ No newline at end of file + #[test] + fn parse_number_millis_handles_missing_and_non_digit() { + assert_eq!(parse_number_millis(b"{}", b"\"missing\""), None); + assert_eq!(parse_number_millis(b"\"k\":oops", b"\"k\""), Some(0)); + } + + #[test] + fn object_after_key_at_depth_handles_missing_and_non_object() { + assert_eq!(object_after_key_at_depth(b"{}", b"\"missing\"", 1), None); + assert_eq!(object_after_key_at_depth(b"\"k\":5", b"\"k\"", 0), None); + } + + #[test] + fn array_after_key_at_depth_handles_non_array() { + assert_eq!(array_after_key_at_depth(b"\"k\":5", b"\"k\"", 0), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon here"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn find_key_at_depth_skips_escaped_characters_in_strings() { + assert_eq!( + find_key_at_depth(b"\"a\":\"x\\\"y\",\"b\":1", b"\"b\"", 0), + Some(11) + ); + } + + #[test] + fn count_array_strings_skips_escaped_quotes() { + assert_eq!(count_array_strings(b"[\"a\\\"b\",\"c\"]"), 2); + } +} From 9da93fd3ccfc6ae730f3ab83733696c84836ee4d Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:21:59 -0600 Subject: [PATCH 05/15] test(core-process-comment): backfill coverage to 100%/98%/98% Added tests for the deny paths (missing input, unknown action, delete own_only/hard-delete), successful edit/reply flows, multi-mention/link output, and the low-level JSON parser helpers, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../core-process-comment/src/main.rs | 305 ++++++++++++++++-- 1 file changed, 275 insertions(+), 30 deletions(-) diff --git a/capability-src/core-process-comment/src/main.rs b/capability-src/core-process-comment/src/main.rs index 41fbd07..c7991d4 100644 --- a/capability-src/core-process-comment/src/main.rs +++ b/capability-src/core-process-comment/src/main.rs @@ -127,17 +127,15 @@ pub unsafe fn process(input: &[u8], out: &mut [u8]) -> usize { } match action { - b"create" | b"edit" | b"reply" => { - process_body_action( - out, - action, - actor_id, - comment, - policy, - visibility_attr, - visibility_model, - ) - } + b"create" | b"edit" | b"reply" => process_body_action( + out, + action, + actor_id, + comment, + policy, + visibility_attr, + visibility_model, + ), b"react" => process_react(out, actor_id, comment, policy), b"delete" => process_delete(out, actor_id, comment, policy), _ => deny( @@ -309,7 +307,11 @@ fn process_react(out: &mut [u8], actor_id: &[u8], comment: &[u8], policy: &[u8]) let mut i = 0usize; i = copy(out, i, br#"{"decision":"allow","reason":"Reaction "#); i = copy(out, i, if op == b"remove" { b"remove" } else { b"add" }); - i = copy(out, i, br#" allowed","reason_code":"ok","normalized_comment":{"id":"#); + i = copy( + out, + i, + br#" allowed","reason_code":"ok","normalized_comment":{"id":"#, + ); i = json_str(out, i, comment_id); i = copy(out, i, br#","action":"react","reactions":[{"emoji":"#); i = json_str(out, i, emoji); @@ -334,7 +336,8 @@ fn process_delete(out: &mut [u8], actor_id: &[u8], comment: &[u8], policy: &[u8] policy, ); } - let soft = contains(policy, b"\"soft_delete\":true") || contains(policy, b"\"soft_delete\": true"); + let soft = + contains(policy, b"\"soft_delete\":true") || contains(policy, b"\"soft_delete\": true"); if !soft { return deny( out, @@ -357,7 +360,15 @@ fn process_delete(out: &mut [u8], actor_id: &[u8], comment: &[u8], policy: &[u8] i = copy(out, i, br#","deleted":true,"deleted_by":"#); i = json_str(out, i, actor_id); i = copy(out, i, br#","deleted_at":null,"created_by":"#); - i = json_str(out, i, if created_by.is_empty() { actor_id } else { created_by }); + i = json_str( + out, + i, + if created_by.is_empty() { + actor_id + } else { + created_by + }, + ); i = copy(out, i, br#"},"obligations":[{"type":"audit_log","severity":"required","metadata":{"soft_delete":true}},{"type":"retain_for_ediscovery","severity":"required"}],"evaluation_trace":["action=delete","soft_delete=true in policy","actor is creator -> allow","body retained for audit"],"policy_hash":"#); i = write_policy_hash(out, i, policy); i = copy(out, i, br#","confidence":"high"}"#); @@ -417,7 +428,11 @@ fn write_allow_normalized( } i = copy(out, i, br#"],"reactions":[],"visibility":"#); i = json_str(out, i, visibility); - i = copy(out, i, br#","resolved":false,"pinned":false,"deleted":false,"created_by":"#); + i = copy( + out, + i, + br#","resolved":false,"pinned":false,"deleted":false,"created_by":"#, + ); i = json_str(out, i, actor_id); if quarantine { i = copy(out, i, br#","metadata":{"quarantine":true}}"#); @@ -433,12 +448,20 @@ fn write_allow_normalized( i, br#"{"type":"quarantine","severity":"required","reason":"blocklist_match"}"#, ); - i = copy(out, i, br#",{"type":"notify","severity":"required","targets":["role:moderator"]}"#); + i = copy( + out, + i, + br#",{"type":"notify","severity":"required","targets":["role:moderator"]}"#, + ); i = copy(out, i, br#",{"type":"audit_log","severity":"required"}"#); } else { let mut need_comma = false; if !mentions.is_empty() { - i = copy(out, i, br#"{"type":"notify","severity":"required","targets":["#); + i = copy( + out, + i, + br#"{"type":"notify","severity":"required","targets":["#, + ); for (idx, m) in mentions.iter().enumerate() { if idx > 0 { i = copy(out, i, br#","#); @@ -463,7 +486,11 @@ fn write_allow_normalized( i = copy(out, i, br#"],"evaluation_trace":["action="#); i = copy(out, i, action); if quarantine { - i = copy(out, i, br#"","body matched blocklist","decision=allow with quarantine"]"#); + i = copy( + out, + i, + br#"","body matched blocklist","decision=allow with quarantine"]"#, + ); } else { i = copy(out, i, br#"","validations passed"]"#); } @@ -931,67 +958,285 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"action\":\"create\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"],\"attributes\":{\"tenant_id\":\"t-100\",\"display_name\":\"Ada Lovelace\"}},\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\",\"visibility\":\"team\"}},\"comment\":{\"body\":\"Hey @user-7 and @user-11, please review the latest draft. See https://example.com/spec\",\"parent_id\":null,\"metadata\":{}},\"context\":{\"request_id\":\"req-abc\",\"client\":\"web\"},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":10000,\"max_thread_depth\":8,\"allowed_markups\":[\"bold\",\"italic\",\"code\",\"link\"],\"mention_resolution\":\"strict\",\"visibility_model\":\"resource-acl\",\"soft_delete\":true,\"actions\":{\"create\":{\"roles\":[\"member\",\"admin\"]},\"edit\":{\"roles\":[\"member\",\"admin\"],\"own_only\":true},\"delete\":{\"roles\":[\"member\",\"admin\"],\"own_only\":true},\"react\":{\"roles\":[\"member\",\"admin\"]},\"resolve\":{\"roles\":[\"admin\",\"owner\"]},\"pin\":{\"roles\":[\"admin\"]}},\"moderation\":{\"blocklist\":[],\"require_approval_roles\":[]}}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_sad() { let out = run("{\"action\":\"edit\",\"actor\":{\"id\":\"user-99\",\"roles\":[\"member\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\"}},\"comment\":{\"id\":\"cmt-55\",\"body\":\"Updated text\",\"parent_id\":null,\"created_by\":\"user-42\"},\"context\":{},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":10000,\"max_thread_depth\":8,\"allowed_markups\":[\"bold\",\"italic\",\"code\",\"link\"],\"mention_resolution\":\"strict\",\"visibility_model\":\"resource-acl\",\"soft_delete\":true,\"actions\":{\"edit\":{\"roles\":[\"member\",\"admin\"],\"own_only\":true}},\"moderation\":{\"blocklist\":[],\"require_approval_roles\":[]}}}"); - assert!(out.contains("\"reason_code\":\"not_owner\""), "expected not_owner in {out}"); + assert!( + out.contains("\"reason_code\":\"not_owner\""), + "expected not_owner in {out}" + ); } #[test] fn use_case_03_sad() { let out = run("{\"action\":\"reply\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\"}},\"comment\":{\"body\":\"One more level\",\"parent_id\":\"cmt-depth-7\",\"parent_depth\":7},\"context\":{},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":10000,\"max_thread_depth\":8,\"allowed_markups\":[\"bold\",\"italic\"],\"mention_resolution\":\"strict\",\"visibility_model\":\"resource-acl\",\"soft_delete\":true,\"actions\":{\"reply\":{\"roles\":[\"member\",\"admin\"]}},\"moderation\":{\"blocklist\":[],\"require_approval_roles\":[]}}}"); - assert!(out.contains("\"reason_code\":\"max_thread_depth_exceeded\""), "expected max_thread_depth_exceeded in {out}"); + assert!( + out.contains("\"reason_code\":\"max_thread_depth_exceeded\""), + "expected max_thread_depth_exceeded in {out}" + ); } #[test] fn use_case_04_happy() { let out = run("{\"action\":\"create\",\"actor\":{\"id\":\"user-77\",\"roles\":[\"member\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"type\":\"channel\",\"id\":\"ch-general\",\"attributes\":{\"tenant_id\":\"t-100\"}},\"comment\":{\"body\":\"This contains a blocked-term that should be flagged\",\"parent_id\":null},\"context\":{},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":10000,\"max_thread_depth\":8,\"allowed_markups\":[\"bold\",\"italic\"],\"mention_resolution\":\"strict\",\"visibility_model\":\"channel\",\"soft_delete\":true,\"actions\":{\"create\":{\"roles\":[\"member\",\"admin\"]}},\"moderation\":{\"blocklist\":[\"blocked-term\"],\"require_approval_roles\":[\"moderator\"]}}}"); - assert!(out.contains("\"reason_code\":\"moderation_quarantine\""), "expected moderation_quarantine in {out}"); + assert!( + out.contains("\"reason_code\":\"moderation_quarantine\""), + "expected moderation_quarantine in {out}" + ); } #[test] fn use_case_05_happy() { let out = run("{\"action\":\"react\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\"}},\"comment\":{\"id\":\"cmt-55\",\"reaction\":{\"emoji\":\"thumbsup\",\"op\":\"add\"}},\"context\":{},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":10000,\"max_thread_depth\":8,\"allowed_markups\":[],\"mention_resolution\":\"strict\",\"visibility_model\":\"resource-acl\",\"soft_delete\":true,\"actions\":{\"react\":{\"roles\":[\"member\",\"admin\"]}},\"moderation\":{\"blocklist\":[],\"require_approval_roles\":[]}}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_06_happy() { let out = run("{\"action\":\"delete\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\"}},\"comment\":{\"id\":\"cmt-55\",\"body\":\"Original text that must be retained\",\"created_by\":\"user-42\"},\"context\":{},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":10000,\"max_thread_depth\":8,\"allowed_markups\":[],\"mention_resolution\":\"strict\",\"visibility_model\":\"resource-acl\",\"soft_delete\":true,\"actions\":{\"delete\":{\"roles\":[\"member\",\"admin\"],\"own_only\":true}},\"moderation\":{\"blocklist\":[],\"require_approval_roles\":[]}}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_07_sad() { let out = run("{\"action\":\"create\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"admin\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-200\"}},\"comment\":{\"body\":\"Trying to comment across tenants\",\"parent_id\":null},\"context\":{},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":10000,\"max_thread_depth\":8,\"allowed_markups\":[],\"mention_resolution\":\"strict\",\"visibility_model\":\"resource-acl\",\"soft_delete\":true,\"actions\":{\"create\":{\"roles\":[\"member\",\"admin\"]}},\"moderation\":{\"blocklist\":[],\"require_approval_roles\":[]},\"enforce_tenant_isolation\":true}}"); - assert!(out.contains("\"reason_code\":\"tenant_isolation_violation\""), "expected tenant_isolation_violation in {out}"); + assert!( + out.contains("\"reason_code\":\"tenant_isolation_violation\""), + "expected tenant_isolation_violation in {out}" + ); } #[test] fn use_case_08_sad() { let out = run("{\"action\":\"create\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\"}},\"comment\":{\"body\":\" \",\"parent_id\":null},\"context\":{},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":10000,\"max_thread_depth\":8,\"allowed_markups\":[],\"mention_resolution\":\"strict\",\"visibility_model\":\"resource-acl\",\"soft_delete\":true,\"actions\":{\"create\":{\"roles\":[\"member\"]}},\"moderation\":{\"blocklist\":[],\"require_approval_roles\":[]}}}"); - assert!(out.contains("\"reason_code\":\"empty_body\""), "expected empty_body in {out}"); + assert!( + out.contains("\"reason_code\":\"empty_body\""), + "expected empty_body in {out}" + ); } #[test] fn use_case_09_sad() { let out = run("{\"action\":\"create\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\"}},\"comment\":{\"body\":\"xxxxxxxxxxxxxxxxxxxxx\",\"parent_id\":null},\"context\":{},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":20,\"max_thread_depth\":8,\"allowed_markups\":[],\"mention_resolution\":\"strict\",\"visibility_model\":\"resource-acl\",\"soft_delete\":true,\"actions\":{\"create\":{\"roles\":[\"member\"]}},\"moderation\":{\"blocklist\":[],\"require_approval_roles\":[]}}}"); - assert!(out.contains("\"reason_code\":\"body_too_long\""), "expected body_too_long in {out}"); + assert!( + out.contains("\"reason_code\":\"body_too_long\""), + "expected body_too_long in {out}" + ); } #[test] fn use_case_10_sad() { let out = run("{\"action\":\"react\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\"}},\"comment\":{\"id\":\"cmt-55\",\"reaction\":{\"op\":\"add\"}},\"context\":{},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":10000,\"max_thread_depth\":8,\"allowed_markups\":[],\"mention_resolution\":\"strict\",\"visibility_model\":\"resource-acl\",\"soft_delete\":true,\"actions\":{\"react\":{\"roles\":[\"member\"]}},\"moderation\":{\"blocklist\":[],\"require_approval_roles\":[]}}}"); - assert!(out.contains("\"reason_code\":\"invalid_reaction\""), "expected invalid_reaction in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_reaction\""), + "expected invalid_reaction in {out}" + ); } #[test] fn use_case_11_sad() { let out = run("{\"action\":\"create\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"guest\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\"}},\"comment\":{\"body\":\"Hello\",\"parent_id\":null},\"context\":{},\"comment_policy\":{\"version\":\"2026.08.1\",\"max_body_length\":10000,\"max_thread_depth\":8,\"allowed_markups\":[],\"mention_resolution\":\"strict\",\"visibility_model\":\"resource-acl\",\"soft_delete\":true,\"actions\":{\"create\":{\"roles\":[\"member\",\"admin\"]}},\"moderation\":{\"blocklist\":[],\"require_approval_roles\":[]}}}"); - assert!(out.contains("\"reason_code\":\"insufficient_role\""), "expected insufficient_role in {out}"); + assert!( + out.contains("\"reason_code\":\"insufficient_role\""), + "expected insufficient_role in {out}" + ); + } + + #[test] + fn missing_required_top_level_fields_denies_invalid_input() { + let out = run("{}"); + assert!( + out.contains("\"reason_code\":\"invalid_input\""), + "expected invalid_input in {out}" + ); + } + + #[test] + fn missing_actor_id_denies_invalid_actor() { + let out = run("{\"action\":\"create\",\"actor\":{\"roles\":[\"member\"]},\"resource\":{\"id\":\"doc-1\"},\"comment\":{\"body\":\"hi\"},\"comment_policy\":{\"actions\":{\"create\":{\"roles\":[\"member\"]}}}}"); + assert!( + out.contains("\"reason_code\":\"invalid_actor\""), + "expected invalid_actor in {out}" + ); + } + + #[test] + fn unknown_action_is_denied() { + let out = run("{\"action\":\"resolve\",\"actor\":{\"id\":\"user-1\",\"roles\":[\"admin\"]},\"resource\":{\"id\":\"doc-1\"},\"comment\":{\"body\":\"hi\"},\"comment_policy\":{\"actions\":{\"resolve\":{\"roles\":[\"admin\"]}}}}"); + assert!( + out.contains("\"reason_code\":\"invalid_action\""), + "expected invalid_action in {out}" + ); + } + + #[test] + fn successful_edit_by_owner_defaults_visibility_to_team() { + let out = run("{\"action\":\"edit\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"resource\":{\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\"}},\"comment\":{\"id\":\"cmt-1\",\"body\":\"Updated text\",\"created_by\":\"user-42\"},\"comment_policy\":{\"visibility_model\":\"resource-acl\",\"actions\":{\"edit\":{\"roles\":[\"member\"],\"own_only\":true}},\"moderation\":{\"blocklist\":[]}}}"); + assert!( + out.contains("\"reason\":\"Edit allowed\""), + "expected Edit allowed reason in {out}" + ); + assert!(out.contains("\"visibility\":\"team\"")); + } + + #[test] + fn successful_reply_under_depth_limit() { + let out = run("{\"action\":\"reply\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"]},\"resource\":{\"id\":\"doc-99\"},\"comment\":{\"body\":\"A short reply\",\"parent_id\":\"cmt-1\",\"parent_depth\":1},\"comment_policy\":{\"visibility_model\":\"channel\",\"max_thread_depth\":8,\"actions\":{\"reply\":{\"roles\":[\"member\"]}},\"moderation\":{\"blocklist\":[]}}}"); + assert!( + out.contains("\"reason\":\"Reply allowed\""), + "expected Reply allowed reason in {out}" + ); + assert!(out.contains("\"depth\":2")); + assert!(out.contains("\"visibility\":\"channel\"")); + } + + #[test] + fn multiple_mentions_and_links_are_all_emitted() { + let out = run("{\"action\":\"create\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"]},\"resource\":{\"id\":\"doc-99\"},\"comment\":{\"body\":\"cc @alice @bob see https://a.example and https://b.example\"},\"comment_policy\":{\"actions\":{\"create\":{\"roles\":[\"member\"]}},\"moderation\":{\"blocklist\":[]}}}"); + assert!(out.contains("\"id\":\"alice\"")); + assert!(out.contains("\"id\":\"bob\"")); + assert!(out.contains("https://a.example")); + assert!(out.contains("https://b.example")); } -} \ No newline at end of file + #[test] + fn parent_id_literal_string_null_is_emitted_as_json_string() { + let out = run("{\"action\":\"create\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"]},\"resource\":{\"id\":\"doc-99\"},\"comment\":{\"body\":\"hi\",\"parent_id\":\"null\"},\"comment_policy\":{\"actions\":{\"create\":{\"roles\":[\"member\"]}},\"moderation\":{\"blocklist\":[]}}}"); + assert!( + out.contains("\"parent_id\":null"), + "expected literal \"null\" string value to render as JSON null in {out}" + ); + } + + #[test] + fn delete_own_only_mismatch_is_denied() { + let out = run("{\"action\":\"delete\",\"actor\":{\"id\":\"user-99\",\"roles\":[\"member\"]},\"resource\":{\"id\":\"doc-99\"},\"comment\":{\"id\":\"cmt-1\",\"body\":\"x\",\"created_by\":\"user-42\"},\"comment_policy\":{\"soft_delete\":true,\"actions\":{\"delete\":{\"roles\":[\"member\"],\"own_only\":true}},\"moderation\":{\"blocklist\":[]}}}"); + assert!( + out.contains("\"reason_code\":\"not_owner\""), + "expected not_owner in {out}" + ); + } + + #[test] + fn delete_without_soft_delete_policy_is_denied() { + let out = run("{\"action\":\"delete\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"]},\"resource\":{\"id\":\"doc-99\"},\"comment\":{\"id\":\"cmt-1\",\"body\":\"x\",\"created_by\":\"user-42\"},\"comment_policy\":{\"soft_delete\":false,\"actions\":{\"delete\":{\"roles\":[\"member\"],\"own_only\":true}},\"moderation\":{\"blocklist\":[]}}}"); + assert!( + out.contains("\"reason_code\":\"hard_delete_unsupported\""), + "expected hard_delete_unsupported in {out}" + ); + } + + #[test] + fn policy_without_version_falls_back_to_fnv_hash() { + let out = run("{\"action\":\"create\",\"actor\":{\"id\":\"user-42\",\"roles\":[\"member\"]},\"resource\":{\"id\":\"doc-99\"},\"comment\":{\"body\":\"hi\"},\"comment_policy\":{\"actions\":{\"create\":{\"roles\":[\"member\"]}},\"moderation\":{\"blocklist\":[]}}}"); + assert!( + out.contains("\"policy_hash\":\"sha256:policy:"), + "expected fnv-derived policy hash in {out}" + ); + } + + #[test] + fn action_role_allowed_handles_missing_actions_and_unknown_action() { + assert!(!action_role_allowed(b"{}", b"create", &[b"member"])); + assert!(!action_role_allowed( + br#"{"actions":{"create":{"roles":[]}}}"#, + b"create", + &[b"member"] + )); + assert!(!action_role_allowed( + br#"{"actions":{"create":{"roles":["admin"]}}}"#, + b"create", + &[b"member"] + )); + } + + #[test] + fn action_own_only_handles_missing_actions_and_missing_action() { + assert!(!action_own_only(b"{}", b"delete")); + assert!(!action_own_only(br#"{"actions":{}}"#, b"delete")); + } + + #[test] + fn parse_moderation_blocklist_handles_missing_moderation() { + let mut out: [&[u8]; MAX_BLOCK] = [&b""[..]; MAX_BLOCK]; + assert_eq!(parse_moderation_blocklist(b"{}", &mut out), 0); + } + + #[test] + fn is_blank_and_contains_ascii_ci_edge_cases() { + assert!(is_blank(b"")); + assert!(is_blank(b" \t\n")); + assert!(!is_blank(b"a")); + assert!(!contains_ascii_ci(b"short", b"longer than short")); + assert!(!contains_ascii_ci(b"hello", b"")); + assert!(contains_ascii_ci(b"Hello World", b"WORLD")); + } + + #[test] + fn attr_string_handles_missing_attributes() { + assert_eq!(attr_string(b"{}", b"tenant_id"), b""); + } + + #[test] + fn object_after_key_and_array_after_key_handle_missing_and_wrong_type() { + assert_eq!(object_after_key(b"{}", b"\"missing\""), None); + assert_eq!(object_after_key(br#"{"k":5}"#, b"\"k\""), None); + assert_eq!(json_array_after_key(b"{}", b"\"missing\""), None); + assert_eq!(json_array_after_key(br#"{"k":5}"#, b"\"k\""), None); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn extract_string_at_depth_returns_empty_when_missing() { + assert_eq!(extract_string_at_depth(b"{}", b"\"missing\"", 1), b""); + } + + #[test] + fn extract_i32_handles_negative_and_none() { + assert_eq!(extract_i32(b"\"k\":-3", b"\"k\""), Some(-3)); + assert_eq!(extract_i32(b"{}", b"\"missing\""), None); + assert_eq!(extract_i32(b"\"k\":oops", b"\"k\""), None); + } + + #[test] + fn copy_i32_handles_negative() { + let mut buf = [0u8; 8]; + let n = copy_i32(&mut buf, 0, -42); + assert_eq!(&buf[..n], b"-42"); + } + + #[test] + fn extract_mentions_ignores_bare_at_sign() { + let mut out: [&[u8]; MAX_MENTIONS] = [&b""[..]; MAX_MENTIONS]; + assert_eq!(extract_mentions(b"just an @ sign", &mut out), 0); + } + + #[test] + fn extract_links_ignores_non_https_text() { + let mut out: [&[u8]; MAX_LINKS] = [&b""[..]; MAX_LINKS]; + assert_eq!(extract_links(b"no links here", &mut out), 0); + } +} From 635c17b3f19278d746f0279a4615738c31d99729 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:22:47 -0600 Subject: [PATCH 06/15] test(core-transition-action-status): backfill coverage to 100%/95%/97% Added tests for missing config, unknown status, owner_only=false, and missing/empty allowed_transitions branches, plus the low-level parser helpers, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../core-transition-action-status/src/main.rs | 161 +++++++++++++++--- 1 file changed, 141 insertions(+), 20 deletions(-) diff --git a/capability-src/core-transition-action-status/src/main.rs b/capability-src/core-transition-action-status/src/main.rs index 9f29437..481ea52 100644 --- a/capability-src/core-transition-action-status/src/main.rs +++ b/capability-src/core-transition-action-status/src/main.rs @@ -32,7 +32,9 @@ unsafe extern "C" { } #[cfg(test)] -unsafe fn emit_event(_ptr: i32, _len: i32) -> i32 { 0 } +unsafe fn emit_event(_ptr: i32, _len: i32) -> i32 { + 0 +} static mut INPUT_BUF: [u8; 8192] = [0; 8192]; static mut OUTPUT_BUF: [u8; 4096] = [0; 4096]; @@ -84,11 +86,7 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { return write_result( out, false, - if current.is_empty() { - b"" - } else { - current - }, + if current.is_empty() { b"" } else { current }, b"invalid_status", br#"["precondition failed: required fields missing"]"#, ); @@ -109,7 +107,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { if owner.is_empty() || actor != owner { let mut trace = [0u8; 96]; let mut t = 0usize; - t = copy(&mut trace, t, br#"["owner_only=true and actor is not owner"]"#); + t = copy( + &mut trace, + t, + br#"["owner_only=true and actor is not owner"]"#, + ); return write_result(out, false, current, b"not_owner", &trace[..t]); } } @@ -349,7 +351,9 @@ fn string_value_after<'a>(after_key: &'a [u8]) -> &'a [u8] { return b""; }; let mut rest = &after_key[colon + 1..]; - while rest.first() == Some(&b' ') || rest.first() == Some(&b'\n') || rest.first() == Some(&b'\t') + while rest.first() == Some(&b' ') + || rest.first() == Some(&b'\n') + || rest.first() == Some(&b'\t') { rest = &rest[1..]; } @@ -416,67 +420,184 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"action_item_id\":\"item-001\",\"current_status\":\"open\",\"requested_status\":\"in_progress\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_sad() { let out = run("{\"action_item_id\":\"item-002\",\"current_status\":\"done\",\"requested_status\":\"open\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"illegal_transition\""), "expected illegal_transition in {out}"); + assert!( + out.contains("\"reason_code\":\"illegal_transition\""), + "expected illegal_transition in {out}" + ); } #[test] fn use_case_03_happy() { let out = run("{\"action_item_id\":\"item-003\",\"current_status\":\"open\",\"requested_status\":\"snoozed\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_04_sad() { let out = run("{\"action_item_id\":\"item-004\",\"current_status\":\"open\",\"requested_status\":\"in_progress\",\"actor_id\":\"user-bob\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"not_owner\""), "expected not_owner in {out}"); + assert!( + out.contains("\"reason_code\":\"not_owner\""), + "expected not_owner in {out}" + ); } #[test] fn use_case_05_happy() { let out = run("{\"action_item_id\":\"item-005\",\"current_status\":\"in_progress\",\"requested_status\":\"blocked\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_06_happy() { let out = run("{\"action_item_id\":\"item-006\",\"current_status\":\"in_progress\",\"requested_status\":\"done\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_07_happy() { let out = run("{\"action_item_id\":\"item-007\",\"current_status\":\"blocked\",\"requested_status\":\"in_progress\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_08_happy() { let out = run("{\"action_item_id\":\"item-008\",\"current_status\":\"snoozed\",\"requested_status\":\"open\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_09_happy() { let out = run("{\"action_item_id\":\"item-009\",\"current_status\":\"open\",\"requested_status\":\"cancelled\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_10_sad() { let out = run("{\"action_item_id\":\"item-010\",\"current_status\":\"cancelled\",\"requested_status\":\"open\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"illegal_transition\""), "expected illegal_transition in {out}"); + assert!( + out.contains("\"reason_code\":\"illegal_transition\""), + "expected illegal_transition in {out}" + ); } #[test] fn use_case_11_sad() { let out = run("{\"action_item_id\":\"item-011\",\"current_status\":\"\",\"requested_status\":\"open\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"version\":\"1.0\",\"allowed_transitions\":{\"open\":[\"in_progress\",\"cancelled\",\"snoozed\"],\"in_progress\":[\"blocked\",\"done\",\"cancelled\",\"snoozed\"],\"blocked\":[\"in_progress\",\"cancelled\"],\"snoozed\":[\"open\",\"in_progress\",\"cancelled\"],\"done\":[],\"cancelled\":[]},\"owner_only\":true}}"); - assert!(out.contains("\"reason_code\":\"invalid_status\""), "expected invalid_status in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_status\""), + "expected invalid_status in {out}" + ); + } + + #[test] + fn missing_required_fields_yields_invalid_status() { + let out = run("{}"); + assert!( + out.contains("\"reason_code\":\"invalid_status\""), + "expected invalid_status in {out}" + ); + assert!(out.contains("\"new_status\":\"\"")); + } + + #[test] + fn unknown_requested_status_is_invalid() { + let out = run("{\"action_item_id\":\"item-1\",\"current_status\":\"open\",\"requested_status\":\"vanished\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"allowed_transitions\":{\"open\":[\"vanished\"]},\"owner_only\":true}}"); + assert!( + out.contains("\"reason_code\":\"invalid_status\""), + "expected invalid_status in {out}" + ); + } + + #[test] + fn owner_only_false_allows_non_owner_actor() { + let out = run("{\"action_item_id\":\"item-1\",\"current_status\":\"open\",\"requested_status\":\"in_progress\",\"actor_id\":\"user-bob\",\"owner_id\":\"user-ada\",\"transition_config\":{\"allowed_transitions\":{\"open\":[\"in_progress\"]},\"owner_only\":false}}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); + assert!(!out.contains("actor is owner")); } -} \ No newline at end of file + #[test] + fn missing_allowed_transitions_is_illegal_transition() { + let out = run("{\"action_item_id\":\"item-1\",\"current_status\":\"open\",\"requested_status\":\"in_progress\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"owner_only\":true}}"); + assert!( + out.contains("\"reason_code\":\"illegal_transition\""), + "expected illegal_transition in {out}" + ); + assert!(out.contains("allowed_transitions missing")); + } + + #[test] + fn status_with_no_transition_list_entry_is_illegal_transition() { + let out = run("{\"action_item_id\":\"item-1\",\"current_status\":\"blocked\",\"requested_status\":\"open\",\"actor_id\":\"user-ada\",\"owner_id\":\"user-ada\",\"transition_config\":{\"allowed_transitions\":{\"open\":[\"in_progress\"]},\"owner_only\":true}}"); + assert!( + out.contains("\"reason_code\":\"illegal_transition\""), + "expected illegal_transition in {out}" + ); + assert!(out.contains("has no transition list")); + } + + #[test] + fn object_after_key_handles_missing_and_non_object() { + assert_eq!(object_after_key(b"{}", b"\"missing\""), None); + assert_eq!(object_after_key(b"\"k\":5", b"\"k\""), None); + } + + #[test] + fn array_after_key_handles_missing_and_non_array() { + assert_eq!(array_after_key(b"{}", b"\"missing\""), None); + assert_eq!(array_after_key(b"\"k\":5", b"\"k\""), None); + } + + #[test] + fn extract_bool_handles_false_and_neither() { + assert_eq!(extract_bool(b"\"k\":false", b"\"k\""), Some(false)); + assert_eq!(extract_bool(b"\"k\":maybe", b"\"k\""), None); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn array_contains_string_matches_exact_element() { + assert!(array_contains_string(br#"["a","b"]"#, b"b")); + assert!(!array_contains_string(br#"["a","b"]"#, b"c")); + } +} From fc01e6a5213520669566f62054f152724bebdb99 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:23:41 -0600 Subject: [PATCH 07/15] test(core-extract-action-items): backfill coverage to 100%/96%/97% Added tests for missing text, empty-sentence skipping, confidence threshold branches (below both, below min only), capitalized-name matching, multi-item comma joining, due-date resolution, and the low-level parser helpers, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../core-extract-action-items/src/main.rs | 149 +++++++++++++++++- 1 file changed, 145 insertions(+), 4 deletions(-) diff --git a/capability-src/core-extract-action-items/src/main.rs b/capability-src/core-extract-action-items/src/main.rs index 263c05c..72d3408 100644 --- a/capability-src/core-extract-action-items/src/main.rs +++ b/capability-src/core-extract-action-items/src/main.rs @@ -116,7 +116,8 @@ pub unsafe fn extract(input: &[u8], out: &mut [u8]) -> usize { continue; } - if let Some(pidx) = match_action_participant(sent.body, &participants[..participant_count]) { + if let Some(pidx) = match_action_participant(sent.body, &participants[..participant_count]) + { let p = &participants[pidx]; let will = contains(sent.body, b" will "); let to_pat = contains(sent.body, b" to "); @@ -727,13 +728,153 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"text\":\"Ada will send the revised proposal by Friday. We should probably look at the API at some point. Bob to review security notes next week.\",\"participants\":[\"Ada Lovelace\",\"Bob Smith\"],\"meeting_date\":\"2026-08-07\",\"extraction_config\":{\"version\":\"1.1\",\"min_confidence\":0.75,\"review_threshold\":0.55,\"date_parsing\":\"relative_and_absolute\",\"owner_strategy\":\"name_match_first\",\"reject_vague\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_sad() { let out = run("{\"text\":\"We had a good discussion and aligned on the direction. No specific next steps today.\",\"participants\":[],\"meeting_date\":\"2026-08-07\",\"extraction_config\":{\"version\":\"1.1\",\"min_confidence\":0.75,\"review_threshold\":0.55,\"date_parsing\":\"relative_and_absolute\",\"owner_strategy\":\"name_match_first\",\"reject_vague\":true}}"); - assert!(out.contains("\"reason_code\":\"no_action_items_found\""), "expected no_action_items_found in {out}"); + assert!( + out.contains("\"reason_code\":\"no_action_items_found\""), + "expected no_action_items_found in {out}" + ); } -} \ No newline at end of file + #[test] + fn missing_text_yields_no_action_items_found() { + let out = + run("{\"participants\":[],\"meeting_date\":\"2026-08-07\",\"extraction_config\":{}}"); + assert!( + out.contains("\"reason_code\":\"no_action_items_found\""), + "expected no_action_items_found in {out}" + ); + assert!(out.contains("text missing")); + } + + #[test] + fn empty_sentence_between_periods_is_skipped() { + let out = run("{\"text\":\"Ada will send the report.. Just some extra context.\",\"participants\":[\"Ada Lovelace\"],\"meeting_date\":\"2026-08-07\",\"extraction_config\":{}}"); + assert!(out.contains("\"reason_code\":\"ok\"")); + } + + #[test] + fn capitalized_name_match_without_will_or_to_is_ignored() { + let out = run("{\"text\":\"Someone mentioned Ada during the call.\",\"participants\":[],\"meeting_date\":\"2026-08-07\",\"extraction_config\":{}}"); + assert!(out.contains("\"reason_code\":\"no_action_items_found\"")); + } + + #[test] + fn below_review_threshold_confidence_is_dropped_entirely() { + let out = run("{\"text\":\"Ada to help sometime.\",\"participants\":[\"Ada Lovelace\"],\"meeting_date\":\"2026-08-07\",\"extraction_config\":{\"min_confidence\":0.99,\"review_threshold\":0.9}}"); + assert!( + out.contains("\"reason_code\":\"no_action_items_found\""), + "expected everything dropped below review_threshold in {out}" + ); + } + + #[test] + fn below_min_confidence_but_above_review_threshold_moves_to_review() { + let out = run("{\"text\":\"Ada to help with review.\",\"participants\":[\"Ada Lovelace\"],\"meeting_date\":\"2026-08-07\",\"extraction_config\":{\"min_confidence\":0.99,\"review_threshold\":0.5}}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected review candidate to still count as ok in {out}" + ); + assert!(out.contains("below_min_confidence")); + } + + #[test] + fn capitalized_name_start_with_will_matches_as_participant_zero() { + let out = run("{\"text\":\"Zoe will file the report.\",\"participants\":[],\"meeting_date\":\"2026-08-07\",\"extraction_config\":{}}"); + assert!(out.contains("\"reason_code\":\"ok\"")); + } + + #[test] + fn multiple_action_items_and_multiple_review_items_are_comma_joined() { + let out = run("{\"text\":\"Ada will send the report. Bob will file the notes. We should probably revisit scope at some point. We should probably ping design at some point.\",\"participants\":[\"Ada Lovelace\",\"Bob Smith\"],\"meeting_date\":\"2026-08-07\",\"extraction_config\":{}}"); + assert!(out.contains("\"reason_code\":\"ok\"")); + assert!(out.matches("\"suggested_owner\"").count() >= 2); + assert!(out.matches("\"reason\":\"vague_language\"").count() >= 2); + } + + #[test] + fn resolve_due_date_handles_next_week_and_non_matching_meeting_date() { + let out = run("{\"text\":\"Ada will follow up next week.\",\"participants\":[\"Ada Lovelace\"],\"meeting_date\":\"2026-08-07\",\"extraction_config\":{}}"); + assert!(out.contains("2026-08-14")); + + let out2 = run("{\"text\":\"Ada will follow up by Friday.\",\"participants\":[\"Ada Lovelace\"],\"meeting_date\":\"2099-01-01\",\"extraction_config\":{}}"); + assert!(!out2.contains("suggested_due_date")); + } + + #[test] + fn simplify_action_title_strips_first_name_will_and_suffix() { + let mut out = [0u8; 64]; + let n = simplify_action_title( + b"Ada will send the report by Friday", + b"Ada", + true, + &mut out, + ); + assert_eq!(&out[..n], b"Send the report"); + } + + #[test] + fn strip_prefix_and_suffix_phrase_no_match_returns_input() { + assert_eq!(strip_prefix_phrase(b"hello world", b"We "), b"hello world"); + assert_eq!( + strip_suffix_phrase(b"hello world", b" next week"), + b"hello world" + ); + } + + #[test] + fn capitalize_first_handles_empty_and_already_uppercase() { + let mut out = [0u8; 8]; + assert_eq!(capitalize_first(b"", &mut out), 0); + let n = capitalize_first(b"Already", &mut out); + assert_eq!(&out[..n], b"Already"); + } + + #[test] + fn parse_participants_handles_missing_array() { + let mut out: [Participant; MAX_PARTICIPANTS] = [Participant { + full: b"", + first: b"", + }; MAX_PARTICIPANTS]; + assert_eq!(parse_participants(b"{}", &mut out), 0); + } + + #[test] + fn object_after_key_and_array_after_key_handle_missing_and_wrong_type() { + assert_eq!(object_after_key(b"{}", b"\"missing\""), None); + assert_eq!(object_after_key(br#"{"k":5}"#, b"\"k\""), None); + assert_eq!(json_array_after_key(b"{}", b"\"missing\""), None); + assert_eq!(json_array_after_key(br#"{"k":5}"#, b"\"k\""), None); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn extract_bool_handles_false_and_neither() { + assert_eq!(extract_bool(b"\"k\":false", b"\"k\""), Some(false)); + assert_eq!(extract_bool(b"\"k\":maybe", b"\"k\""), None); + } + + #[test] + fn parse_decimal_handles_no_digits() { + assert_eq!(parse_decimal(b"oops"), None); + assert_eq!(parse_decimal(b"3"), Some(3.0)); + } +} From 1ea9471934c53798c0dc11150e3fb1f27cfb29a9 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:24:28 -0600 Subject: [PATCH 08/15] test(core-validate-action-item): backfill coverage to 100%/96%/97% Added tests for missing fields, empty title, missing due date, owner/due-omitted ok output, malformed duplicate-check array elements, and the low-level parser helpers, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../core-validate-action-item/src/main.rs | 119 ++++++++++++++---- 1 file changed, 98 insertions(+), 21 deletions(-) diff --git a/capability-src/core-validate-action-item/src/main.rs b/capability-src/core-validate-action-item/src/main.rs index b3c904f..4ba5ff2 100644 --- a/capability-src/core-validate-action-item/src/main.rs +++ b/capability-src/core-validate-action-item/src/main.rs @@ -259,15 +259,13 @@ fn write_invalid_config(out: &mut [u8], trace: &[u8]) -> usize { i } -fn write_ok( - out: &mut [u8], - title: &[u8], - owner: &[u8], - due: &[u8], - traces: &[&[u8]], -) -> usize { +fn write_ok(out: &mut [u8], title: &[u8], owner: &[u8], due: &[u8], traces: &[&[u8]]) -> usize { let mut i = 0usize; - i = copy(out, i, b"{\"valid\":true,\"errors\":[],\"normalized\":{\"title\":\""); + i = copy( + out, + i, + b"{\"valid\":true,\"errors\":[],\"normalized\":{\"title\":\"", + ); i = copy(out, i, title); i = copy(out, i, b"\""); if !owner.is_empty() { @@ -286,12 +284,7 @@ fn write_ok( i } -fn write_fail( - out: &mut [u8], - reason: &[u8], - errors: &[ErrPart], - traces: &[&[u8]], -) -> usize { +fn write_fail(out: &mut [u8], reason: &[u8], errors: &[ErrPart], traces: &[&[u8]]) -> usize { let mut i = 0usize; i = copy(out, i, b"{\"valid\":false,\"errors\":["); for (idx, err) in errors.iter().enumerate() { @@ -510,37 +503,121 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"action_item\":{\"title\":\"Send proposal\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-09\",\"source\":\"meeting\"},\"existing_open_items\":[],\"validation_config\":{\"version\":\"1.0\",\"require_owner\":true,\"require_due_date\":false,\"allow_past_due\":false,\"duplicate_check\":\"title_and_owner\"},\"reference_date\":\"2026-08-07\"}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_sad() { let out = run("{\"action_item\":{\"title\":\"Old task\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-01\"},\"existing_open_items\":[],\"validation_config\":{\"version\":\"1.0\",\"require_owner\":true,\"require_due_date\":true,\"allow_past_due\":false,\"duplicate_check\":\"title_and_owner\"},\"reference_date\":\"2026-08-07\"}"); - assert!(out.contains("\"reason_code\":\"validation_failed\""), "expected validation_failed in {out}"); + assert!( + out.contains("\"reason_code\":\"validation_failed\""), + "expected validation_failed in {out}" + ); } #[test] fn use_case_03_sad() { let out = run("{\"action_item\":{\"title\":\"Draft agenda\",\"due_date\":\"2026-08-10\"},\"existing_open_items\":[],\"validation_config\":{\"version\":\"1.0\",\"require_owner\":true,\"require_due_date\":false,\"allow_past_due\":false,\"duplicate_check\":\"none\"},\"reference_date\":\"2026-08-07\"}"); - assert!(out.contains("\"reason_code\":\"validation_failed\""), "expected validation_failed in {out}"); + assert!( + out.contains("\"reason_code\":\"validation_failed\""), + "expected validation_failed in {out}" + ); } #[test] fn use_case_04_sad() { let out = run("{\"action_item\":{\"title\":\"Send proposal\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-12\"},\"existing_open_items\":[{\"title\":\"Send proposal\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-09\"}],\"validation_config\":{\"version\":\"1.0\",\"require_owner\":true,\"require_due_date\":false,\"allow_past_due\":false,\"duplicate_check\":\"title_and_owner\"},\"reference_date\":\"2026-08-07\"}"); - assert!(out.contains("\"reason_code\":\"duplicate\""), "expected duplicate in {out}"); + assert!( + out.contains("\"reason_code\":\"duplicate\""), + "expected duplicate in {out}" + ); } #[test] fn use_case_05_sad() { let out = run("{\"action_item\":{\"title\":\"Send proposal\",\"owner_id\":\"user-bob\",\"due_date\":\"2026-08-12\"},\"existing_open_items\":[{\"title\":\"Send proposal\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-09\"}],\"validation_config\":{\"version\":\"1.0\",\"require_owner\":true,\"require_due_date\":false,\"allow_past_due\":false,\"duplicate_check\":\"title\"},\"reference_date\":\"2026-08-07\"}"); - assert!(out.contains("\"reason_code\":\"duplicate\""), "expected duplicate in {out}"); + assert!( + out.contains("\"reason_code\":\"duplicate\""), + "expected duplicate in {out}" + ); } #[test] fn use_case_06_sad() { let out = run("{\"action_item\":{\"title\":\"Send proposal\",\"owner_id\":\"user-ada\",\"due_date\":\"2026-08-12\"},\"existing_open_items\":[],\"validation_config\":{\"version\":\"1.0\",\"require_owner\":true,\"require_due_date\":false,\"allow_past_due\":false,\"duplicate_check\":\"bogus\"},\"reference_date\":\"2026-08-07\"}"); - assert!(out.contains("\"reason_code\":\"invalid_config\""), "expected invalid_config in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_config\""), + "expected invalid_config in {out}" + ); + } + + #[test] + fn missing_required_fields_yields_invalid_config() { + let out = run("{}"); + assert!( + out.contains("\"reason_code\":\"invalid_config\""), + "expected invalid_config in {out}" + ); + } + + #[test] + fn empty_title_is_an_error() { + let out = run("{\"action_item\":{\"title\":\"\",\"owner_id\":\"user-ada\"},\"existing_open_items\":[],\"validation_config\":{\"require_owner\":true,\"duplicate_check\":\"none\"},\"reference_date\":\"2026-08-07\"}"); + assert!(out.contains("\"code\":\"empty_title\"")); } -} \ No newline at end of file + #[test] + fn missing_due_date_is_an_error_when_required() { + let out = run("{\"action_item\":{\"title\":\"Task\",\"owner_id\":\"user-ada\"},\"existing_open_items\":[],\"validation_config\":{\"require_owner\":true,\"require_due_date\":true,\"duplicate_check\":\"none\"},\"reference_date\":\"2026-08-07\"}"); + assert!(out.contains("\"code\":\"missing_due_date\"")); + } + + #[test] + fn ok_result_omits_absent_owner_and_due_date() { + let out = run("{\"action_item\":{\"title\":\"Task\"},\"existing_open_items\":[],\"validation_config\":{\"require_owner\":false,\"duplicate_check\":\"none\"},\"reference_date\":\"2026-08-07\"}"); + assert!(out.contains("\"reason_code\":\"ok\"")); + assert!(!out.contains("\"owner_id\"")); + assert!(!out.contains("\"due_date\"")); + } + + #[test] + fn find_duplicate_ignores_non_object_and_unterminated_elements() { + assert!(!find_duplicate(br#"[42]"#, b"Task", b"", false)); + assert!(!find_duplicate(br#"[{"title":"Task""#, b"Task", b"", false)); + assert!(!find_duplicate(b"[]", b"Task", b"", false)); + } + + #[test] + fn object_after_key_and_array_after_key_handle_missing_and_wrong_type() { + assert_eq!(object_after_key(b"{}", b"\"missing\""), None); + assert_eq!(object_after_key(br#"{"k":5}"#, b"\"k\""), None); + assert_eq!(array_after_key(b"{}", b"\"missing\""), None); + assert_eq!(array_after_key(br#"{"k":5}"#, b"\"k\""), None); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn extract_string_at_depth_returns_empty_when_missing() { + assert_eq!(extract_string_at_depth(b"{}", b"\"missing\"", 1), b""); + } + + #[test] + fn extract_bool_handles_false_and_neither() { + assert_eq!(extract_bool(b"\"k\":false", b"\"k\""), Some(false)); + assert_eq!(extract_bool(b"\"k\":maybe", b"\"k\""), None); + } +} From f428ed0d8d529c691bc12b250204fbddadb7f1dc Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:25:15 -0600 Subject: [PATCH 09/15] test(core-assign-ownership): backfill coverage to 100%/97%/97% Added tests for missing config, default fallback, fallback=creator branches (missing creator_id, active-member match, direct accept without require_active), and the low-level parser helpers, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../core-assign-ownership/src/main.rs | 138 +++++++++++++++--- 1 file changed, 121 insertions(+), 17 deletions(-) diff --git a/capability-src/core-assign-ownership/src/main.rs b/capability-src/core-assign-ownership/src/main.rs index 740c7f0..63cdf31 100644 --- a/capability-src/core-assign-ownership/src/main.rs +++ b/capability-src/core-assign-ownership/src/main.rs @@ -98,7 +98,15 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { if suggested_null || suggested.is_empty() { traces[trace_n] = b"suggested_owner null"; trace_n += 1; - return apply_fallback(out, fallback, creator, members, require_active, &mut traces, trace_n); + return apply_fallback( + out, + fallback, + creator, + members, + require_active, + &mut traces, + trace_n, + ); } if let Some((id, method, note)) = resolve_member(members, suggested, require_active) { @@ -112,7 +120,15 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { traces[0] = b"no member match for suggestion"; trace_n = 1; - apply_fallback(out, fallback, creator, members, require_active, &mut traces, trace_n) + apply_fallback( + out, + fallback, + creator, + members, + require_active, + &mut traces, + trace_n, + ) } fn apply_fallback<'a>( @@ -168,13 +184,7 @@ fn apply_fallback<'a>( traces[trace_n] = b"fallback=unassigned"; trace_n += 1; } - write_result( - out, - None, - b"fallback_unassigned", - b"ok", - &traces[..trace_n], - ) + write_result(out, None, b"fallback_unassigned", b"ok", &traces[..trace_n]) } _ => { if trace_n < MAX_TRACE { @@ -469,43 +479,137 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"suggested_owner\":\"Ada Lovelace\",\"creator_id\":\"user-carol\",\"workspace_members\":[{\"id\":\"user-ada\",\"name\":\"Ada Lovelace\",\"email\":\"ada@loop.dev\"},{\"id\":\"user-bob\",\"name\":\"Bob Smith\",\"email\":\"bob@loop.dev\"}],\"ownership_config\":{\"version\":\"1.0\",\"fallback\":\"creator\",\"require_active_member\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_happy() { let out = run("{\"suggested_owner\":\"bob@loop.dev\",\"creator_id\":\"user-carol\",\"workspace_members\":[{\"id\":\"user-ada\",\"name\":\"Ada Lovelace\",\"email\":\"ada@loop.dev\"},{\"id\":\"user-bob\",\"name\":\"Bob Smith\",\"email\":\"bob@loop.dev\"}],\"ownership_config\":{\"version\":\"1.0\",\"fallback\":\"creator\",\"require_active_member\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_03_happy() { let out = run("{\"suggested_owner\":null,\"creator_id\":\"user-carol\",\"workspace_members\":[{\"id\":\"user-carol\",\"name\":\"Carol Jones\",\"email\":\"carol@loop.dev\"}],\"ownership_config\":{\"version\":\"1.0\",\"fallback\":\"creator\",\"require_active_member\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_04_sad() { let out = run("{\"suggested_owner\":\"Unknown Person\",\"creator_id\":\"user-carol\",\"workspace_members\":[{\"id\":\"user-ada\",\"name\":\"Ada Lovelace\",\"email\":\"ada@loop.dev\"}],\"ownership_config\":{\"version\":\"1.0\",\"fallback\":\"fail\",\"require_active_member\":true}}"); - assert!(out.contains("\"reason_code\":\"unresolved\""), "expected unresolved in {out}"); + assert!( + out.contains("\"reason_code\":\"unresolved\""), + "expected unresolved in {out}" + ); } #[test] fn use_case_05_sad() { let out = run("{\"suggested_owner\":\"user-ada\",\"creator_id\":\"user-carol\",\"workspace_members\":[{\"id\":\"user-ada\",\"name\":\"Ada Lovelace\",\"email\":\"ada@loop.dev\",\"active\":false}],\"ownership_config\":{\"version\":\"1.0\",\"fallback\":\"fail\",\"require_active_member\":true}}"); - assert!(out.contains("\"reason_code\":\"inactive_member\""), "expected inactive_member in {out}"); + assert!( + out.contains("\"reason_code\":\"inactive_member\""), + "expected inactive_member in {out}" + ); } #[test] fn use_case_06_sad() { let out = run("{\"suggested_owner\":\"Ada Lovelace\",\"creator_id\":\"user-carol\",\"workspace_members\":[{\"id\":\"user-ada\",\"name\":\"Ada Lovelace\",\"email\":\"ada@loop.dev\"},{\"id\":\"user-bob\",\"name\":\"Bob Smith\",\"email\":\"bob@loop.dev\"}],\"ownership_config\":{\"version\":\"1.0\",\"fallback\":\"bogus\",\"require_active_member\":true}}"); - assert!(out.contains("\"reason_code\":\"config_error\""), "expected config_error in {out}"); + assert!( + out.contains("\"reason_code\":\"config_error\""), + "expected config_error in {out}" + ); } #[test] fn use_case_07_happy() { let out = run("{\"suggested_owner\":\"Unknown Person\",\"creator_id\":\"user-carol\",\"workspace_members\":[{\"id\":\"user-ada\",\"name\":\"Ada Lovelace\",\"email\":\"ada@loop.dev\"}],\"ownership_config\":{\"version\":\"1.0\",\"fallback\":\"unassigned\",\"require_active_member\":true}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); + } + + #[test] + fn missing_config_yields_config_error() { + let out = run( + "{\"suggested_owner\":null,\"creator_id\":\"user-carol\",\"workspace_members\":[]}", + ); + assert!( + out.contains("\"reason_code\":\"config_error\""), + "expected config_error in {out}" + ); + } + + #[test] + fn missing_fallback_key_defaults_to_creator() { + let out = run("{\"suggested_owner\":null,\"creator_id\":\"user-carol\",\"workspace_members\":[{\"id\":\"user-carol\",\"name\":\"Carol Jones\",\"active\":true}],\"ownership_config\":{\"require_active_member\":true}}"); + assert!(out.contains("\"resolution_method\":\"fallback_creator\"")); } -} \ No newline at end of file + #[test] + fn fallback_creator_with_missing_creator_id_is_unresolved() { + let out = run("{\"suggested_owner\":null,\"workspace_members\":[],\"ownership_config\":{\"fallback\":\"creator\"}}"); + assert!( + out.contains("\"reason_code\":\"unresolved\""), + "expected unresolved in {out}" + ); + assert!(out.contains("creator_id missing")); + } + + #[test] + fn fallback_creator_require_active_matches_creator_as_active_member() { + let out = run("{\"suggested_owner\":null,\"creator_id\":\"user-carol\",\"workspace_members\":[{\"id\":\"user-carol\",\"name\":\"Carol Jones\",\"active\":true}],\"ownership_config\":{\"fallback\":\"creator\",\"require_active_member\":true}}"); + assert!(out.contains("\"owner_id\":\"user-carol\"")); + assert!(out.contains("\"resolution_method\":\"fallback_creator\"")); + } + + #[test] + fn fallback_creator_without_require_active_accepts_creator_id_directly() { + let out = run("{\"suggested_owner\":null,\"creator_id\":\"user-zed\",\"workspace_members\":[],\"ownership_config\":{\"fallback\":\"creator\",\"require_active_member\":false}}"); + assert!(out.contains("\"owner_id\":\"user-zed\"")); + assert!(out.contains("\"resolution_method\":\"fallback_creator\"")); + } + + #[test] + fn is_null_at_depth_handles_missing_key_and_non_null_value() { + assert!(!is_null_at_depth(b"{}", b"\"missing\"", 1)); + assert!(!is_null_at_depth(br#"{"k":5}"#, b"\"k\"", 0)); + assert!(!is_null_at_depth(br#"{"k""#, b"\"k\"", 0)); + } + + #[test] + fn object_after_key_and_array_after_key_handle_missing_and_wrong_type() { + assert_eq!(object_after_key(b"{}", b"\"missing\""), None); + assert_eq!(object_after_key(br#"{"k":5}"#, b"\"k\""), None); + assert_eq!(array_after_key(b"{}", b"\"missing\""), None); + assert_eq!(array_after_key(br#"{"k":5}"#, b"\"k\""), None); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn extract_bool_handles_false_and_neither() { + assert_eq!(extract_bool(b"\"k\":false", b"\"k\""), Some(false)); + assert_eq!(extract_bool(b"\"k\":maybe", b"\"k\""), None); + } +} From f692fb1d0c515c5945016b5900af86ccdbf44d27 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:26:12 -0600 Subject: [PATCH 10/15] test(core-authorize): backfill coverage to 100%/96%/96% Added tests for missing rules key, default_effect=allow fallback, role/attribute/condition non-match branches, and the low-level parser helpers, per registry#301. Co-Authored-By: Claude Sonnet 5 --- capability-src/core-authorize/src/main.rs | 158 +++++++++++++++++++--- 1 file changed, 141 insertions(+), 17 deletions(-) diff --git a/capability-src/core-authorize/src/main.rs b/capability-src/core-authorize/src/main.rs index fea4f6b..908ad64 100644 --- a/capability-src/core-authorize/src/main.rs +++ b/capability-src/core-authorize/src/main.rs @@ -221,10 +221,7 @@ pub unsafe fn authorize(input: &[u8], out: &mut [u8]) -> usize { &b"break_glass_override"[..], ) } else { - ( - &b"Matched allow rule"[..], - &b"matched_allow_rule"[..], - ) + (&b"Matched allow rule"[..], &b"matched_allow_rule"[..]) }; return write_result( out, @@ -713,7 +710,9 @@ fn string_value_after<'a>(after_key: &'a [u8]) -> &'a [u8] { return b""; }; let mut rest = &after_key[colon + 1..]; - while rest.first() == Some(&b' ') || rest.first() == Some(&b'\n') || rest.first() == Some(&b'\t') + while rest.first() == Some(&b' ') + || rest.first() == Some(&b'\n') + || rest.first() == Some(&b'\t') { rest = &rest[1..]; } @@ -835,67 +834,192 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"principal\":{\"id\":\"user-42\",\"roles\":[\"admin\"],\"attributes\":{\"tenant_id\":\"t-100\"},\"groups\":[]},\"action\":\"delete\",\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"attributes\":{\"tenant_id\":\"t-100\"},\"owner_id\":\"user-7\"},\"context\":{\"request_time\":\"2026-08-07T22:00:00Z\"},\"policy\":{\"version\":\"1.0\",\"mode\":\"hybrid\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"admin-delete\",\"effect\":\"allow\",\"priority\":100,\"principal\":{\"roles\":[\"admin\"]},\"action\":[\"delete\"],\"resource\":{\"type\":\"document\"}}]}}"); - assert!(out.contains("\"reason_code\":\"matched_allow_rule\""), "expected matched_allow_rule in {out}"); + assert!( + out.contains("\"reason_code\":\"matched_allow_rule\""), + "expected matched_allow_rule in {out}" + ); } #[test] fn use_case_02_sad() { let out = run("{\"principal\":{\"id\":\"user-42\",\"roles\":[\"admin\"],\"attributes\":{\"tenant_id\":\"t-100\"}},\"action\":\"read\",\"resource\":{\"type\":\"document\",\"id\":\"doc-77\",\"attributes\":{\"tenant_id\":\"t-200\"}},\"context\":{},\"policy\":{\"version\":\"1.0\",\"mode\":\"hybrid\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"tenant-isolation\",\"effect\":\"deny\",\"priority\":200,\"condition\":{\"op\":\"neq\",\"left\":\"principal.attributes.tenant_id\",\"right\":\"resource.attributes.tenant_id\"}},{\"id\":\"admin-read\",\"effect\":\"allow\",\"priority\":100,\"principal\":{\"roles\":[\"admin\"]},\"action\":[\"read\"],\"resource\":{\"type\":\"document\"}}]}}"); - assert!(out.contains("\"reason_code\":\"matched_deny_rule\""), "expected matched_deny_rule in {out}"); + assert!( + out.contains("\"reason_code\":\"matched_deny_rule\""), + "expected matched_deny_rule in {out}" + ); } #[test] fn use_case_03_happy() { let out = run("{\"principal\":{\"id\":\"user-7\",\"roles\":[\"member\"]},\"action\":\"update\",\"resource\":{\"type\":\"document\",\"id\":\"doc-99\",\"owner_id\":\"user-7\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"mode\":\"hybrid\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"owner-update\",\"effect\":\"allow\",\"priority\":150,\"condition\":{\"op\":\"eq\",\"left\":\"principal.id\",\"right\":\"resource.owner_id\"},\"action\":[\"update\"]}]}}"); - assert!(out.contains("\"reason_code\":\"matched_allow_rule\""), "expected matched_allow_rule in {out}"); + assert!( + out.contains("\"reason_code\":\"matched_allow_rule\""), + "expected matched_allow_rule in {out}" + ); } #[test] fn use_case_04_sad() { let out = run("{\"principal\":{\"id\":\"user-99\",\"roles\":[\"admin\"],\"attributes\":{\"status\":\"suspended\"}},\"action\":\"delete\",\"resource\":{\"type\":\"document\",\"id\":\"doc-1\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"mode\":\"hybrid\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"suspended-deny\",\"effect\":\"deny\",\"priority\":300,\"principal\":{\"attributes\":{\"status\":\"suspended\"}}},{\"id\":\"admin-delete\",\"effect\":\"allow\",\"priority\":100,\"principal\":{\"roles\":[\"admin\"]},\"action\":[\"delete\"]}]}}"); - assert!(out.contains("\"reason_code\":\"matched_deny_rule\""), "expected matched_deny_rule in {out}"); + assert!( + out.contains("\"reason_code\":\"matched_deny_rule\""), + "expected matched_deny_rule in {out}" + ); } #[test] fn use_case_05_happy() { let out = run("{\"principal\":{\"id\":\"user-42\",\"roles\":[\"finance\"]},\"action\":\"transfer\",\"resource\":{\"type\":\"payment\",\"id\":\"pay-55\",\"attributes\":{\"amount\":50000}},\"context\":{\"amount\":50000},\"policy\":{\"version\":\"1.0\",\"mode\":\"hybrid\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"high-value-transfer\",\"effect\":\"allow\",\"priority\":100,\"principal\":{\"roles\":[\"finance\"]},\"action\":[\"transfer\"],\"resource\":{\"type\":\"payment\"},\"obligations\":[{\"type\":\"require_mfa\",\"severity\":\"required\"},{\"type\":\"audit_log\",\"severity\":\"required\",\"metadata\":{\"category\":\"high_value\"}}]}]}}"); - assert!(out.contains("\"reason_code\":\"matched_allow_rule\""), "expected matched_allow_rule in {out}"); + assert!( + out.contains("\"reason_code\":\"matched_allow_rule\""), + "expected matched_allow_rule in {out}" + ); } #[test] fn use_case_06_sad() { let out = run("{\"principal\":{\"id\":\"user-1\",\"roles\":[\"guest\"]},\"action\":\"delete\",\"resource\":{\"type\":\"document\",\"id\":\"doc-1\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"mode\":\"rbac\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"member-read\",\"effect\":\"allow\",\"priority\":50,\"principal\":{\"roles\":[\"member\"]},\"action\":[\"read\"]}]}}"); - assert!(out.contains("\"reason_code\":\"no_matching_rule\""), "expected no_matching_rule in {out}"); + assert!( + out.contains("\"reason_code\":\"no_matching_rule\""), + "expected no_matching_rule in {out}" + ); } #[test] fn use_case_07_sad() { let out = run("{\"principal\":{\"id\":\"user-1\"},\"action\":\"read\",\"resource\":{\"type\":\"document\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"mode\":\"rbac\",\"default_effect\":\"deny\",\"rules\":[]}}"); - assert!(out.contains("\"reason_code\":\"empty_or_invalid_policy\""), "expected empty_or_invalid_policy in {out}"); + assert!( + out.contains("\"reason_code\":\"empty_or_invalid_policy\""), + "expected empty_or_invalid_policy in {out}" + ); } #[test] fn use_case_08_happy() { let out = run("{\"principal\":{\"id\":\"user-ops\",\"roles\":[\"sre\"],\"attributes\":{\"break_glass\":true}},\"action\":\"read\",\"resource\":{\"type\":\"secret\",\"id\":\"sec-1\"},\"context\":{\"incident_id\":\"INC-2048\"},\"policy\":{\"version\":\"1.0\",\"mode\":\"hybrid\",\"default_effect\":\"deny\",\"break_glass\":{\"enabled\":true,\"required_attribute\":\"break_glass\",\"max_priority\":999},\"rules\":[{\"id\":\"break-glass-allow\",\"effect\":\"allow\",\"priority\":999,\"principal\":{\"attributes\":{\"break_glass\":true}},\"obligations\":[{\"type\":\"audit_log\",\"severity\":\"required\",\"metadata\":{\"break_glass\":true}},{\"type\":\"notify_security\",\"severity\":\"required\"}]}]}}"); - assert!(out.contains("\"reason_code\":\"break_glass_override\""), "expected break_glass_override in {out}"); + assert!( + out.contains("\"reason_code\":\"break_glass_override\""), + "expected break_glass_override in {out}" + ); } #[test] fn use_case_09_sad() { let out = run("{\"principal\":{\"roles\":[\"admin\"]},\"action\":\"read\",\"resource\":{\"type\":\"document\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"mode\":\"rbac\",\"default_effect\":\"deny\",\"rules\":[]}}"); - assert!(out.contains("\"reason_code\":\"invalid_principal\""), "expected invalid_principal in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_principal\""), + "expected invalid_principal in {out}" + ); } #[test] fn use_case_10_sad() { let out = run("{\"principal\":{\"id\":\"user-1\",\"roles\":[\"admin\"]},\"action\":\"\",\"resource\":{\"type\":\"document\",\"id\":\"doc-1\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"mode\":\"rbac\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"r1\",\"effect\":\"allow\",\"priority\":1,\"principal\":{\"roles\":[\"admin\"]},\"action\":[\"read\"]}]}}"); - assert!(out.contains("\"reason_code\":\"invalid_action\""), "expected invalid_action in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_action\""), + "expected invalid_action in {out}" + ); } #[test] fn use_case_11_sad() { let out = run("{\"principal\":{\"id\":\"user-1\",\"roles\":[\"admin\"]},\"action\":\"read\",\"resource\":{\"type\":\"\",\"id\":\"doc-1\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"mode\":\"rbac\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"r1\",\"effect\":\"allow\",\"priority\":1,\"principal\":{\"roles\":[\"admin\"]},\"action\":[\"read\"]}]}}"); - assert!(out.contains("\"reason_code\":\"invalid_resource\""), "expected invalid_resource in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_resource\""), + "expected invalid_resource in {out}" + ); + } + + #[test] + fn policy_missing_rules_key_entirely_is_invalid() { + let out = run("{\"principal\":{\"id\":\"user-1\",\"roles\":[\"admin\"]},\"action\":\"read\",\"resource\":{\"type\":\"document\"},\"context\":{},\"policy\":{\"version\":\"1.0\"}}"); + assert!( + out.contains("\"reason_code\":\"empty_or_invalid_policy\""), + "expected empty_or_invalid_policy in {out}" + ); + } + + #[test] + fn default_effect_allow_with_no_matching_rule_allows() { + let out = run("{\"principal\":{\"id\":\"user-1\",\"roles\":[\"guest\"]},\"action\":\"delete\",\"resource\":{\"type\":\"document\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"default_effect\":\"allow\",\"rules\":[{\"id\":\"member-read\",\"effect\":\"allow\",\"priority\":50,\"principal\":{\"roles\":[\"member\"]},\"action\":[\"read\"]}]}}"); + assert!( + out.contains("\"decision\":\"allow\""), + "expected allow in {out}" + ); + assert!(out.contains("\"reason_code\":\"no_matching_rule\"")); + } + + #[test] + fn rule_with_role_principal_lacks_does_not_match() { + let out = run("{\"principal\":{\"id\":\"user-1\",\"roles\":[\"guest\"]},\"action\":\"read\",\"resource\":{\"type\":\"document\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"admin-only\",\"effect\":\"allow\",\"priority\":1,\"principal\":{\"roles\":[\"admin\"]},\"action\":[\"read\"]}]}}"); + assert!(out.contains("\"reason_code\":\"no_matching_rule\"")); + } + + #[test] + fn rule_with_status_attribute_mismatch_does_not_match() { + let out = run("{\"principal\":{\"id\":\"user-1\",\"roles\":[\"admin\"],\"attributes\":{\"status\":\"active\"}},\"action\":\"read\",\"resource\":{\"type\":\"document\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"suspended-only\",\"effect\":\"deny\",\"priority\":1,\"principal\":{\"attributes\":{\"status\":\"suspended\"}}}]}}"); + assert!(out.contains("\"reason_code\":\"no_matching_rule\"")); + } + + #[test] + fn rule_with_tenant_attribute_mismatch_does_not_match() { + let out = run("{\"principal\":{\"id\":\"user-1\",\"roles\":[\"admin\"],\"attributes\":{\"tenant_id\":\"t-1\"}},\"action\":\"read\",\"resource\":{\"type\":\"document\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"tenant2-only\",\"effect\":\"allow\",\"priority\":1,\"principal\":{\"attributes\":{\"tenant_id\":\"t-2\"}},\"action\":[\"read\"]}]}}"); + assert!(out.contains("\"reason_code\":\"no_matching_rule\"")); } -} \ No newline at end of file + #[test] + fn condition_eq_false_does_not_match() { + let out = run("{\"principal\":{\"id\":\"user-1\",\"roles\":[\"admin\"]},\"action\":\"update\",\"resource\":{\"type\":\"document\",\"owner_id\":\"user-other\"},\"context\":{},\"policy\":{\"version\":\"1.0\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"owner-only\",\"effect\":\"allow\",\"priority\":1,\"condition\":{\"op\":\"eq\",\"left\":\"principal.id\",\"right\":\"resource.owner_id\"},\"action\":[\"update\"]}]}}"); + assert!(out.contains("\"reason_code\":\"no_matching_rule\"")); + } + + #[test] + fn condition_neq_true_when_equal_does_not_match() { + let out = run("{\"principal\":{\"id\":\"user-1\",\"roles\":[\"admin\"],\"attributes\":{\"tenant_id\":\"t-1\"}},\"action\":\"read\",\"resource\":{\"type\":\"document\",\"attributes\":{\"tenant_id\":\"t-1\"}},\"context\":{},\"policy\":{\"version\":\"1.0\",\"default_effect\":\"deny\",\"rules\":[{\"id\":\"cross-tenant-deny\",\"effect\":\"deny\",\"priority\":1,\"condition\":{\"op\":\"neq\",\"left\":\"principal.attributes.tenant_id\",\"right\":\"resource.attributes.tenant_id\"}}]}}"); + assert!(out.contains("\"reason_code\":\"no_matching_rule\"")); + } + + #[test] + fn attr_bool_true_matches_the_spaced_colon_form() { + assert!(attr_bool_true( + br#"{"attributes":{"break_glass": true}}"#, + b"break_glass" + )); + assert!(!attr_bool_true(b"{}", b"break_glass")); + } + + #[test] + fn value_object_after_and_value_array_after_handle_missing_and_wrong_type() { + assert_eq!(value_object_after(b"no colon"), None); + assert_eq!(value_object_after(b":5"), None); + assert_eq!(value_array_after(b"no colon"), None); + assert_eq!(value_array_after(b":5"), None); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn extract_i32_handles_negative_and_none() { + assert_eq!(extract_i32(b"\"k\":-3", b"\"k\""), Some(-3)); + assert_eq!(extract_i32(b"{}", b"\"missing\""), None); + assert_eq!(extract_i32(b"\"k\":oops", b"\"k\""), None); + } + + #[test] + fn resolve_path_defaults_to_empty_for_unknown_path() { + assert_eq!( + resolve_path(b"unknown.path", b"p", b"t", b"s", b"o", b"rt"), + b"" + ); + } +} From 67ae9e286c3fd69d1763d0ddee0fc778c08baeb2 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:26:51 -0600 Subject: [PATCH 11/15] test(core-generate-nudge-message): backfill coverage to 100%/96%/98% Added tests for missing fields, invalid intensity, owner/due-omitted message variants for each intensity level, tone default, and the low-level parser helpers, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../core-generate-nudge-message/src/main.rs | 110 ++++++++++++++++-- 1 file changed, 102 insertions(+), 8 deletions(-) diff --git a/capability-src/core-generate-nudge-message/src/main.rs b/capability-src/core-generate-nudge-message/src/main.rs index 7b6aa1f..2cf350d 100644 --- a/capability-src/core-generate-nudge-message/src/main.rs +++ b/capability-src/core-generate-nudge-message/src/main.rs @@ -60,7 +60,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { return fail(out, b"config_error", br#"["missing required fields"]"#); } if !matches!(intensity, b"soft" | b"direct" | b"escalate") { - return fail(out, b"invalid_intensity", br#"["intensity must be soft|direct|escalate"]"#); + return fail( + out, + b"invalid_intensity", + br#"["intensity must be soft|direct|escalate"]"#, + ); } let tone = extract_string(config, b"\"tone\""); let tone = if tone.is_empty() { b"friendly" } else { tone }; @@ -130,7 +134,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { i = copy_json_escaped(out, i, &msg[..m]); i = copy(out, i, b"\",\"preview\":\""); i = copy_json_escaped(out, i, &prev[..p]); - i = copy(out, i, b"\",\"reason_code\":\"ok\",\"evaluation_trace\":[\"intensity="); + i = copy( + out, + i, + b"\",\"reason_code\":\"ok\",\"evaluation_trace\":[\"intensity=", + ); i = copy(out, i, intensity); i = copy(out, i, b"\",\"tone="); i = copy(out, i, tone); @@ -140,7 +148,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { fn fail(out: &mut [u8], code: &[u8], trace: &[u8]) -> usize { let mut i = 0usize; - i = copy(out, i, b"{\"message\":\"\",\"preview\":\"\",\"reason_code\":\""); + i = copy( + out, + i, + b"{\"message\":\"\",\"preview\":\"\",\"reason_code\":\"", + ); i = copy(out, i, code); i = copy(out, i, b"\",\"evaluation_trace\":"); i = copy(out, i, trace); @@ -332,25 +344,107 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"item\":{\"id\":\"ai-1\",\"title\":\"Send the revised proposal\",\"owner_name\":\"Ada\",\"due_date\":\"2026-08-09\",\"status\":\"open\",\"nudge_count\":0},\"intensity\":\"soft\",\"message_config\":{\"version\":\"1.0\",\"tone\":\"friendly\",\"include_due_date\":true,\"language\":\"en\"}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_happy() { let out = run("{\"item\":{\"id\":\"ai-9\",\"title\":\"Close security review\",\"owner_name\":\"Bob\",\"due_date\":\"2026-08-01\",\"status\":\"open\",\"nudge_count\":3},\"intensity\":\"escalate\",\"message_config\":{\"version\":\"1.0\",\"tone\":\"direct\",\"include_due_date\":true,\"language\":\"en\"}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_03_happy() { let out = run("{\"item\":{\"id\":\"ai-2\",\"title\":\"Ship docs\",\"owner_name\":\"Ada\",\"due_date\":\"2026-08-12\",\"status\":\"open\",\"nudge_count\":1},\"intensity\":\"direct\",\"message_config\":{\"version\":\"1.0\",\"tone\":\"neutral\",\"include_due_date\":true,\"language\":\"en\"}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_04_sad() { let out = run("{\"item\":{\"id\":\"ai-x\",\"title\":\"\",\"owner_name\":\"Ada\",\"due_date\":\"2026-08-09\",\"status\":\"open\",\"nudge_count\":0},\"intensity\":\"soft\",\"message_config\":{\"version\":\"1.0\",\"tone\":\"friendly\",\"include_due_date\":true,\"language\":\"en\"}}"); - assert!(out.contains("\"reason_code\":\"config_error\""), "expected config_error in {out}"); + assert!( + out.contains("\"reason_code\":\"config_error\""), + "expected config_error in {out}" + ); } -} \ No newline at end of file + #[test] + fn missing_top_level_fields_yields_config_error() { + let out = run("{}"); + assert!( + out.contains("\"reason_code\":\"config_error\""), + "expected config_error in {out}" + ); + } + + #[test] + fn invalid_intensity_is_rejected() { + let out = + run("{\"item\":{\"title\":\"T\"},\"intensity\":\"urgent\",\"message_config\":{}}"); + assert!( + out.contains("\"reason_code\":\"invalid_intensity\""), + "expected invalid_intensity in {out}" + ); + } + + #[test] + fn soft_message_without_owner_or_due_date_uses_defaults() { + let out = run("{\"item\":{\"title\":\"Ship docs\"},\"intensity\":\"soft\",\"message_config\":{\"include_due_date\":false}}"); + assert!(out.contains("Hey there")); + assert!(!out.contains(" is due ")); + } + + #[test] + fn direct_message_without_owner_or_due_date_omits_prefix_and_date() { + let out = run("{\"item\":{\"title\":\"Ship docs\"},\"intensity\":\"direct\",\"message_config\":{\"include_due_date\":false}}"); + assert!(out.contains("Please complete")); + assert!(!out.contains(" by ")); + } + + #[test] + fn escalate_message_without_owner_or_due_date_omits_both() { + let out = run("{\"item\":{\"title\":\"Ship docs\"},\"intensity\":\"escalate\",\"message_config\":{\"include_due_date\":false}}"); + assert!(out.contains("Escalation")); + assert!(!out.contains(" from ")); + assert!(!out.contains(" (due ")); + } + + #[test] + fn tone_defaults_to_friendly_when_missing() { + let out = run("{\"item\":{\"title\":\"T\"},\"intensity\":\"soft\",\"message_config\":{}}"); + assert!(out.contains("\"tone=friendly\"")); + } + + #[test] + fn object_after_key_handles_missing_and_non_object() { + assert_eq!(object_after_key(b"{}", b"\"missing\""), None); + assert_eq!(object_after_key(br#"{"k":5}"#, b"\"k\""), None); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn extract_bool_handles_false_and_neither() { + assert_eq!(extract_bool(b"\"k\":false", b"\"k\""), Some(false)); + assert_eq!(extract_bool(b"\"k\":maybe", b"\"k\""), None); + } +} From 31d60ec4843fa31e2fe761efb559ab714fdb172f Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:27:53 -0600 Subject: [PATCH 12/15] test(core-calculate-price): backfill coverage to 100%/96%/97% Added tests for missing config/quantity, discount SKU/tax-code mismatch continue branches, fixed-discount capping, unparseable tax rates, the inclusive-tax branch, and the low-level parser helpers, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../core-calculate-price/src/main.rs | 184 ++++++++++++++++-- 1 file changed, 163 insertions(+), 21 deletions(-) diff --git a/capability-src/core-calculate-price/src/main.rs b/capability-src/core-calculate-price/src/main.rs index de5a72b..0676d5b 100644 --- a/capability-src/core-calculate-price/src/main.rs +++ b/capability-src/core-calculate-price/src/main.rs @@ -120,8 +120,16 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { if starts_with_minus(qty_raw) || parse_int(qty_raw).unwrap_or(0) <= 0 { let mut trace = [0u8; 128]; let mut t = 0usize; - t = copy(&mut trace, t, br#"["precondition failed: quantity must be > 0 for "#); - t = copy(&mut trace, t, if line_id.is_empty() { b"line" } else { line_id }); + t = copy( + &mut trace, + t, + br#"["precondition failed: quantity must be > 0 for "#, + ); + t = copy( + &mut trace, + t, + if line_id.is_empty() { b"line" } else { line_id }, + ); t = copy(&mut trace, t, br#""]"#); return fail(out, currency, b"invalid_quantity", &trace[..t]); } @@ -133,7 +141,11 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { t, br#"["precondition failed: unit_price must be >= 0 for "#, ); - t = copy(&mut trace, t, if line_id.is_empty() { b"line" } else { line_id }); + t = copy( + &mut trace, + t, + if line_id.is_empty() { b"line" } else { line_id }, + ); t = copy(&mut trace, t, br#""]"#); return fail(out, currency, b"invalid_unit_price", &trace[..t]); } @@ -154,8 +166,7 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { let gross = quantity.saturating_mul(unit_cents); // Discount: first percentage/fixed rule that applies to this SKU (or has no applies_to). - let discount_rules = - array_after_key(config, b"\"discount_rules\"").unwrap_or(b"[]"); + let discount_rules = array_after_key(config, b"\"discount_rules\"").unwrap_or(b"[]"); let (disc_id, disc_type, disc_value, discount_amount) = select_discount(discount_rules, sku, gross); @@ -286,16 +297,16 @@ pub unsafe fn evaluate(input: &[u8], out: &mut [u8]) -> usize { i = write_cents_into_json_str(out, i, net); i = copy(out, i, b"\"],\"config_hash\":\""); i = copy(out, i, &hash_buf[..hash_len]); - i = copy(out, i, b"\",\"reason_code\":\"ok\",\"confidence\":\"high\"}"); + i = copy( + out, + i, + b"\",\"reason_code\":\"ok\",\"confidence\":\"high\"}", + ); let _ = inclusive; i } -fn select_discount<'a>( - rules: &'a [u8], - sku: &[u8], - gross: i64, -) -> (&'a [u8], &'a [u8], i64, i64) { +fn select_discount<'a>(rules: &'a [u8], sku: &[u8], gross: i64) -> (&'a [u8], &'a [u8], i64, i64) { if rules.first() != Some(&b'[') { return (b"", b"", 0, 0); } @@ -366,7 +377,15 @@ fn extract_inclusive_tax(inclusive_base: i64, _exclusive_style: i64) -> i64 { fn fail(out: &mut [u8], currency: &[u8], code: &[u8], trace: &[u8]) -> usize { let mut i = 0usize; i = copy(out, i, b"{\"currency\":\""); - i = copy(out, i, if currency.is_empty() { b"USD" } else { currency }); + i = copy( + out, + i, + if currency.is_empty() { + b"USD" + } else { + currency + }, + ); i = copy( out, i, @@ -835,49 +854,172 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-100\",\"quantity\":2,\"unit_price\":50.0,\"tax_code\":\"STANDARD\"}],\"customer\":{\"id\":\"cust-42\",\"attributes\":{\"segment\":\"retail\"}},\"context\":{},\"pricing_config\":{\"version\":\"1.0\",\"currency\":\"USD\",\"rounding\":\"half_up\",\"decimal_places\":2,\"discount_rules\":[{\"id\":\"summer-10\",\"priority\":100,\"type\":\"percentage\",\"value\":10,\"stackable\":false,\"applies_to\":[\"SKU-100\"]}],\"tax_rules\":[{\"id\":\"us-standard\",\"tax_code\":\"STANDARD\",\"rate\":0.08,\"inclusive\":false}]}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_happy() { let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"sub-1\",\"sku\":\"PLAN-PRO\",\"quantity\":1,\"unit_price\":99.0}],\"customer\":{\"id\":\"cust-99\"},\"context\":{},\"pricing_config\":{\"version\":\"1.0\",\"currency\":\"USD\",\"rounding\":\"half_up\",\"decimal_places\":2,\"discount_rules\":[{\"id\":\"loyalty-5\",\"priority\":50,\"type\":\"fixed\",\"value\":5.0,\"stackable\":true}],\"tax_rules\":[]}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_03_happy() { let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-100\",\"quantity\":1,\"unit_price\":50.0}],\"customer\":{\"id\":\"cust-1\"},\"context\":{},\"pricing_config\":{\"version\":\"1.0\",\"currency\":\"USD\",\"rounding\":\"half_up\",\"decimal_places\":2,\"discount_rules\":[],\"tax_rules\":[]}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_04_sad() { let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-100\",\"quantity\":0,\"unit_price\":50.0}],\"customer\":{\"id\":\"cust-1\"},\"context\":{},\"pricing_config\":{\"version\":\"1.0\",\"currency\":\"USD\",\"rounding\":\"half_up\",\"decimal_places\":2,\"discount_rules\":[],\"tax_rules\":[]}}"); - assert!(out.contains("\"reason_code\":\"invalid_quantity\""), "expected invalid_quantity in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_quantity\""), + "expected invalid_quantity in {out}" + ); } #[test] fn use_case_05_sad() { let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-X\",\"quantity\":1,\"unit_price\":-10.0}],\"customer\":{\"id\":\"cust-1\"},\"context\":{},\"pricing_config\":{\"version\":\"1.0\",\"currency\":\"USD\",\"rounding\":\"half_up\",\"decimal_places\":2,\"discount_rules\":[],\"tax_rules\":[]}}"); - assert!(out.contains("\"reason_code\":\"invalid_unit_price\""), "expected invalid_unit_price in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_unit_price\""), + "expected invalid_unit_price in {out}" + ); } #[test] fn use_case_06_sad() { let out = run("{\"currency\":\"\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-100\",\"quantity\":1,\"unit_price\":50.0}],\"customer\":{\"id\":\"cust-1\"},\"context\":{},\"pricing_config\":{\"version\":\"1.0\",\"currency\":\"USD\",\"rounding\":\"half_up\",\"decimal_places\":2,\"discount_rules\":[],\"tax_rules\":[]}}"); - assert!(out.contains("\"reason_code\":\"invalid_config\""), "expected invalid_config in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_config\""), + "expected invalid_config in {out}" + ); } #[test] fn use_case_07_sad() { let out = run("{\"currency\":\"EUR\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-100\",\"quantity\":1,\"unit_price\":50.0}],\"customer\":{\"id\":\"cust-1\"},\"context\":{},\"pricing_config\":{\"version\":\"1.0\",\"currency\":\"USD\",\"rounding\":\"half_up\",\"decimal_places\":2,\"discount_rules\":[],\"tax_rules\":[]}}"); - assert!(out.contains("\"reason_code\":\"currency_mismatch\""), "expected currency_mismatch in {out}"); + assert!( + out.contains("\"reason_code\":\"currency_mismatch\""), + "expected currency_mismatch in {out}" + ); } #[test] fn use_case_08_sad() { let out = run("{\"currency\":\"USD\",\"lines\":[],\"customer\":{\"id\":\"cust-1\"},\"context\":{},\"pricing_config\":{\"version\":\"1.0\",\"currency\":\"USD\",\"rounding\":\"half_up\",\"decimal_places\":2,\"discount_rules\":[],\"tax_rules\":[]}}"); - assert!(out.contains("\"reason_code\":\"empty_cart\""), "expected empty_cart in {out}"); + assert!( + out.contains("\"reason_code\":\"empty_cart\""), + "expected empty_cart in {out}" + ); + } + + #[test] + fn missing_pricing_config_yields_invalid_config() { + let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-100\",\"quantity\":1,\"unit_price\":50.0}]}"); + assert!( + out.contains("\"reason_code\":\"invalid_config\""), + "expected invalid_config in {out}" + ); + } + + #[test] + fn missing_quantity_field_yields_invalid_quantity() { + let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-100\",\"unit_price\":50.0}],\"pricing_config\":{\"currency\":\"USD\",\"discount_rules\":[],\"tax_rules\":[]}}"); + assert!( + out.contains("\"reason_code\":\"invalid_quantity\""), + "expected invalid_quantity in {out}" + ); + assert!(out.contains("quantity missing")); + } + + #[test] + fn discount_not_matching_sku_is_skipped() { + let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-200\",\"quantity\":1,\"unit_price\":50.0}],\"pricing_config\":{\"currency\":\"USD\",\"discount_rules\":[{\"id\":\"summer-10\",\"type\":\"percentage\",\"value\":10,\"applies_to\":[\"SKU-100\"]}],\"tax_rules\":[]}}"); + assert!(out.contains("\"applied_discounts\":[]")); + } + + #[test] + fn fixed_discount_is_capped_at_gross() { + let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-1\",\"quantity\":1,\"unit_price\":5.0}],\"pricing_config\":{\"currency\":\"USD\",\"discount_rules\":[{\"id\":\"big-fixed\",\"type\":\"fixed\",\"value\":9999.0}],\"tax_rules\":[]}}"); + assert!(out.contains("\"discount_amount\":5.00")); + } + + #[test] + fn tax_code_mismatch_skips_rule_then_falls_through() { + let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-1\",\"quantity\":1,\"unit_price\":100.0,\"tax_code\":\"OTHER\"}],\"pricing_config\":{\"currency\":\"USD\",\"discount_rules\":[],\"tax_rules\":[{\"id\":\"standard\",\"tax_code\":\"STANDARD\",\"rate\":0.08}]}}"); + assert!(out.contains("\"applied_taxes\":[]")); + } + + #[test] + fn tax_rule_with_unparseable_rate_is_skipped() { + let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-1\",\"quantity\":1,\"unit_price\":100.0}],\"pricing_config\":{\"currency\":\"USD\",\"discount_rules\":[],\"tax_rules\":[{\"id\":\"bad\",\"rate\":\"oops\"},{\"id\":\"good\",\"rate\":0.05}]}}"); + assert!(out.contains("\"rule_id\":\"good\"")); + } + + #[test] + fn inclusive_tax_rule_takes_the_inclusive_branch() { + let out = run("{\"currency\":\"USD\",\"lines\":[{\"id\":\"line-1\",\"sku\":\"SKU-1\",\"quantity\":1,\"unit_price\":100.0}],\"pricing_config\":{\"currency\":\"USD\",\"discount_rules\":[],\"tax_rules\":[{\"id\":\"vat\",\"rate\":0.2,\"inclusive\":true}]}}"); + assert!(out.contains("\"reason_code\":\"ok\"")); + } + + #[test] + fn half_up_div_returns_zero_for_non_positive_denominator() { + assert_eq!(half_up_div(100, 0), 0); + assert_eq!(half_up_div(100, -5), 0); + } + + #[test] + fn parse_scaled_stops_at_second_dot() { + assert_eq!(parse_money_cents(b"1.2.3"), Some(120)); + } + + #[test] + fn parse_int_and_starts_with_minus_handle_negative() { + assert_eq!(parse_int(b"-42"), Some(-42)); + assert!(starts_with_minus(b"-1")); + assert!(!starts_with_minus(b"1")); + } + + #[test] + fn array_contains_string_handles_non_array_and_non_quote_element() { + assert!(!array_contains_string(b"not-array", b"x")); + assert!(!array_contains_string(b"[1,2]", b"x")); + assert!(array_contains_string(br#"["a","b"]"#, b"b")); + } + + #[test] + fn array_after_key_and_depth_variants_handle_missing_and_wrong_type() { + assert_eq!(array_after_key(b"{}", b"\"missing\""), None); + assert_eq!(array_after_key(br#""k":5"#, b"\"k\""), None); + assert_eq!(array_after_key_at_depth(b"{}", b"\"missing\"", 1), None); + assert_eq!(object_after_key_at_depth(b"{}", b"\"missing\""), None); } -} \ No newline at end of file + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn extract_bool_handles_false_and_neither() { + assert_eq!(extract_bool(b"\"k\":false", b"\"k\""), Some(false)); + assert_eq!(extract_bool(b"\"k\":maybe", b"\"k\""), None); + } +} From 85009f5612d9d135faaa639e7c13ebcc3630ec81 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:28:46 -0600 Subject: [PATCH 13/15] test(core-aggregate-team-action-health): backfill coverage to 100%/97%/97% Added tests for missing config, empty items, closed-status skipping, overloaded owners, top-2 pressure selection with a tie-break, malformed array elements, and the low-level parser helpers, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../src/main.rs | 146 +++++++++++++++++- 1 file changed, 139 insertions(+), 7 deletions(-) diff --git a/capability-src/core-aggregate-team-action-health/src/main.rs b/capability-src/core-aggregate-team-action-health/src/main.rs index 00240b4..8e29358 100644 --- a/capability-src/core-aggregate-team-action-health/src/main.rs +++ b/capability-src/core-aggregate-team-action-health/src/main.rs @@ -195,10 +195,7 @@ fn write_error(out: &mut [u8], reason: &[u8]) -> usize { } fn is_open_status(s: &[u8]) -> bool { - matches!( - s, - b"open" | b"in_progress" | b"blocked" | b"snoozed" - ) + matches!(s, b"open" | b"in_progress" | b"blocked" | b"snoozed") } fn bump_owner(owners: &mut [OwnerSlot], owner_count: &mut usize, owner: &[u8]) { @@ -552,13 +549,148 @@ mod catalog_coverage_tests { #[test] fn use_case_01_happy() { let out = run("{\"items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"status\":\"open\",\"due_date\":\"2026-08-08\",\"pressure_score\":0.9},{\"id\":\"ai-2\",\"owner_id\":\"user-ada\",\"status\":\"in_progress\",\"due_date\":\"2026-08-15\",\"pressure_score\":0.3},{\"id\":\"ai-3\",\"owner_id\":\"user-bob\",\"status\":\"open\",\"due_date\":\"2026-08-01\",\"pressure_score\":0.95}],\"reference_date\":\"2026-08-07\",\"aggregation_config\":{\"version\":\"1.0\",\"overdue_threshold_days\":0}}"); - assert!(out.contains("\"reason_code\":\"ok\""), "expected ok in {out}"); + assert!( + out.contains("\"reason_code\":\"ok\""), + "expected ok in {out}" + ); } #[test] fn use_case_02_sad() { let out = run("{\"items\":[],\"reference_date\":\"\",\"aggregation_config\":{\"version\":\"1.0\",\"overdue_threshold_days\":0}}"); - assert!(out.contains("\"reason_code\":\"invalid_input\""), "expected invalid_input in {out}"); + assert!( + out.contains("\"reason_code\":\"invalid_input\""), + "expected invalid_input in {out}" + ); } -} \ No newline at end of file + #[test] + fn missing_config_yields_invalid_input() { + let out = run("{\"items\":[],\"reference_date\":\"2026-08-07\"}"); + assert!( + out.contains("\"reason_code\":\"invalid_input\""), + "expected invalid_input in {out}" + ); + } + + #[test] + fn empty_items_array_yields_zero_percent_and_totals() { + let out = run("{\"items\":[],\"reference_date\":\"2026-08-07\",\"aggregation_config\":{}}"); + assert!(out.contains("\"total_open\":0")); + assert!(out.contains("\"on_track_pct\":0")); + } + + #[test] + fn closed_status_items_are_skipped() { + let out = run("{\"items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"status\":\"done\",\"due_date\":\"2026-08-01\"}],\"reference_date\":\"2026-08-07\",\"aggregation_config\":{}}"); + assert!(out.contains("\"total_open\":0")); + } + + #[test] + fn owner_with_two_or_more_open_items_is_overloaded() { + let out = run("{\"items\":[{\"id\":\"ai-1\",\"owner_id\":\"user-ada\",\"status\":\"open\",\"due_date\":\"2026-08-08\"},{\"id\":\"ai-2\",\"owner_id\":\"user-ada\",\"status\":\"open\",\"due_date\":\"2026-08-09\"}],\"reference_date\":\"2026-08-07\",\"aggregation_config\":{}}"); + assert!(out.contains("\"owner_id\":\"user-ada\",\"open_count\":2")); + } + + #[test] + fn three_items_keep_only_top_two_by_pressure() { + let out = run("{\"items\":[{\"id\":\"low\",\"status\":\"open\",\"pressure_score\":0.1},{\"id\":\"high\",\"status\":\"open\",\"pressure_score\":0.9},{\"id\":\"mid\",\"status\":\"open\",\"pressure_score\":0.5}],\"reference_date\":\"2026-08-07\",\"aggregation_config\":{}}"); + assert!(out.contains("\"high\"")); + assert!(out.contains("\"mid\"")); + assert!(!out.contains("\"low\"")); + } + + #[test] + fn tied_pressure_scores_break_by_lexical_id() { + let out = run("{\"items\":[{\"id\":\"bbb\",\"status\":\"open\",\"pressure_score\":0.5},{\"id\":\"aaa\",\"status\":\"open\",\"pressure_score\":0.5}],\"reference_date\":\"2026-08-07\",\"aggregation_config\":{}}"); + assert!(out.contains("\"aaa\"")); + assert!(out.contains("\"bbb\"")); + } + + #[test] + fn non_object_item_element_stops_scanning() { + let out = run("{\"items\":[{\"id\":\"ai-1\",\"status\":\"open\"},42],\"reference_date\":\"2026-08-07\",\"aggregation_config\":{}}"); + assert!(out.contains("\"total_open\":1")); + } + + #[test] + fn unterminated_item_object_stops_scanning() { + let out = run("{\"reference_date\":\"2026-08-07\",\"aggregation_config\":{},\"items\":[{\"id\":\"ai-1\",\"status\":\"open\"},{\"id\":\"ai-2\"]}"); + assert!(out.contains("\"total_open\":1")); + } + + #[test] + fn owner_slot_and_pressure_slot_clone_are_bitwise_copies() { + let owner = OwnerSlot { + id: [1u8; ID_MAX], + len: 1, + count: 5, + }; + let cloned = owner.clone(); + assert_eq!(cloned.count, 5); + + let pressure = PressureSlot { + id: [2u8; ID_MAX], + len: 1, + score_millis: 900, + }; + let cloned_p = pressure.clone(); + assert_eq!(cloned_p.score_millis, 900); + } + + #[test] + fn bump_owner_ignores_ids_longer_than_id_max() { + let mut owners = [OwnerSlot { + id: [0; ID_MAX], + len: 0, + count: 0, + }; MAX_OWNERS]; + let mut count = 0usize; + let long_id = vec![b'x'; ID_MAX + 1]; + bump_owner(&mut owners, &mut count, &long_id); + assert_eq!(count, 0); + } + + #[test] + fn insert_top_pressure_ignores_ids_longer_than_id_max() { + let mut top = [ + PressureSlot { + id: [0; ID_MAX], + len: 0, + score_millis: 0, + }, + PressureSlot { + id: [0; ID_MAX], + len: 0, + score_millis: 0, + }, + ]; + let long_id = vec![b'x'; ID_MAX + 1]; + insert_top_pressure(&mut top, &long_id, 500); + assert_eq!(top[0].len, 0); + } + + #[test] + fn array_after_key_at_depth_and_object_after_key_at_depth_handle_wrong_type() { + assert_eq!(array_after_key_at_depth(b"\"k\":5", b"\"k\"", 0), None); + assert_eq!(object_after_key_at_depth(b"\"k\":5", b"\"k\""), None); + } + + #[test] + fn balanced_end_returns_none_when_unterminated() { + assert_eq!(balanced_end(b"{\"a\":\"b\"", b'{', b'}'), None); + } + + #[test] + fn string_value_after_handles_missing_colon_quote_and_terminator() { + assert_eq!(string_value_after(b"no colon"), b""); + assert_eq!(string_value_after(b":not-a-quote"), b""); + assert_eq!(string_value_after(b":\"unterminated"), b""); + } + + #[test] + fn parse_pressure_score_handles_missing_key_and_no_colon() { + assert_eq!(parse_pressure_score(b"{}"), 0); + assert_eq!(parse_pressure_score(b"\"pressure_score\"no-colon"), 0); + } +} From f4c8a0da06800d57282a4ccb743da7e923b571cc Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:29:58 -0600 Subject: [PATCH 14/15] test(validate-email): backfill coverage to literal 100%/100%/100% Added tests for invalid local/domain characters and exercised the max_length input path through handle(), the last uncovered branches, per registry#301. Co-Authored-By: Claude Sonnet 5 --- capability-src/validate-email/src/main.rs | 53 ++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/capability-src/validate-email/src/main.rs b/capability-src/validate-email/src/main.rs index 0f2ceaa..97c4761 100644 --- a/capability-src/validate-email/src/main.rs +++ b/capability-src/validate-email/src/main.rs @@ -14,7 +14,11 @@ use wasi_capability_runtime::{object, Value}; const DEFAULT_MAX_LENGTH: usize = 254; -fn validate_email(email: &str, allow_plus_addressing: bool, max_length: usize) -> Option<&'static str> { +fn validate_email( + email: &str, + allow_plus_addressing: bool, + max_length: usize, +) -> Option<&'static str> { if email.is_empty() { return Some("empty"); } @@ -61,7 +65,9 @@ fn validate_email(email: &str, allow_plus_addressing: bool, max_length: usize) - if !local_ok { return Some("invalid_character"); } - let domain_ok = domain.chars().all(|c| c.is_alphanumeric() || c == '.' || c == '-'); + let domain_ok = domain + .chars() + .all(|c| c.is_alphanumeric() || c == '.' || c == '-'); if !domain_ok { return Some("invalid_character"); } @@ -122,11 +128,27 @@ mod tests { assert_eq!(check("@example.com"), Some("empty_local_part")); assert_eq!(check("alice@"), Some("empty_domain")); assert_eq!(check("alice@localhost"), Some("domain_missing_dot")); - assert_eq!(check("alice@example..com"), Some("consecutive_dots_in_domain")); - assert_eq!(check(".alice@example.com"), Some("leading_dot_in_local_part")); + assert_eq!( + check("alice@example..com"), + Some("consecutive_dots_in_domain") + ); + assert_eq!( + check(".alice@example.com"), + Some("leading_dot_in_local_part") + ); assert_eq!(check("alice @example.com"), Some("invalid_character")); } + #[test] + fn invalid_character_in_local_part_is_rejected() { + assert_eq!(check("ali!ce@example.com"), Some("invalid_character")); + } + + #[test] + fn invalid_character_in_domain_is_rejected() { + assert_eq!(check("alice@exa!mple.com"), Some("invalid_character")); + } + #[test] fn plus_addressing_disabled_config() { assert_eq!( @@ -151,10 +173,29 @@ mod tests { ); } + #[test] + fn handle_reads_max_length_from_input() { + let out = handle(object(alloc::vec![ + ("email", Value::String(String::from("alice@example.com"))), + ("max_length", Value::Number(5.0)), + ])); + assert_eq!(out.get("valid").unwrap().as_bool(), Some(false)); + assert_eq!( + out.get("reason").unwrap().as_str(), + Some("exceeds_max_length") + ); + } + #[test] fn output_genuinely_differs_across_distinct_inputs() { - let out_a = handle(object(alloc::vec![("email", Value::String(String::from("alice@example.com")))])); - let out_b = handle(object(alloc::vec![("email", Value::String(String::from("not-an-email")))])); + let out_a = handle(object(alloc::vec![( + "email", + Value::String(String::from("alice@example.com")) + )])); + let out_b = handle(object(alloc::vec![( + "email", + Value::String(String::from("not-an-email")) + )])); assert_ne!( out_a.get("valid").unwrap().as_bool(), out_b.get("valid").unwrap().as_bool(), From 71379b817c7fe6354d658c550fe39c2ea91f4c12 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 21 Aug 2026 22:30:46 -0600 Subject: [PATCH 15/15] test(score-password-strength): backfill coverage to 100%/100%/99% Exercised the min_length input path through handle(), the last uncovered branch, per registry#301. Co-Authored-By: Claude Sonnet 5 --- .../score-password-strength/src/main.rs | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/capability-src/score-password-strength/src/main.rs b/capability-src/score-password-strength/src/main.rs index 0b4b4a9..f1d5afb 100644 --- a/capability-src/score-password-strength/src/main.rs +++ b/capability-src/score-password-strength/src/main.rs @@ -36,9 +36,14 @@ fn has_sequential_run(chars: &[char]) -> bool { return false; } for window in chars.windows(4) { - let codes: Vec = window.iter().map(|c| c.to_ascii_lowercase() as i32).collect(); - let ascending = codes[1] == codes[0] + 1 && codes[2] == codes[1] + 1 && codes[3] == codes[2] + 1; - let descending = codes[1] == codes[0] - 1 && codes[2] == codes[1] - 1 && codes[3] == codes[2] - 1; + let codes: Vec = window + .iter() + .map(|c| c.to_ascii_lowercase() as i32) + .collect(); + let ascending = + codes[1] == codes[0] + 1 && codes[2] == codes[1] + 1 && codes[3] == codes[2] + 1; + let descending = + codes[1] == codes[0] - 1 && codes[2] == codes[1] - 1 && codes[3] == codes[2] - 1; if ascending || descending { return true; } @@ -50,7 +55,9 @@ fn has_repeated_run(chars: &[char]) -> bool { if chars.len() < 4 { return false; } - chars.windows(4).any(|w| w[0] == w[1] && w[1] == w[2] && w[2] == w[3]) + chars + .windows(4) + .any(|w| w[0] == w[1] && w[1] == w[2] && w[2] == w[3]) } fn score_password(password: &str, min_length: usize, require_symbol: bool) -> ScoreResult { @@ -63,7 +70,8 @@ fn score_password(password: &str, min_length: usize, require_symbol: bool) -> Sc let has_digit = chars.iter().any(|c| c.is_ascii_digit()); let has_symbol = chars.iter().any(|&c| is_symbol(c)); - let structural = i32::from(has_lower) + i32::from(has_upper) + i32::from(has_digit) + i32::from(has_symbol); + let structural = + i32::from(has_lower) + i32::from(has_upper) + i32::from(has_digit) + i32::from(has_symbol); let length_bonus = i32::from(char_count >= min_length.saturating_mul(2)); let sequential = has_sequential_run(&chars); let repeated = has_repeated_run(&chars); @@ -107,7 +115,11 @@ fn score_password(password: &str, min_length: usize, require_symbol: bool) -> Sc issues.push("repeated_characters"); } - ScoreResult { score, strength, issues } + ScoreResult { + score, + strength, + issues, + } } fn handle(input: Value) -> Value { @@ -191,7 +203,11 @@ mod tests { fn too_short_caps_score_even_with_diverse_characters() { let r = score_password("Short1!", 12, false); assert!(r.issues.contains(&"too_short")); - assert!(r.score <= 1, "a too-short password should be capped low regardless of character diversity, got {}", r.score); + assert!( + r.score <= 1, + "a too-short password should be capped low regardless of character diversity, got {}", + r.score + ); } #[test] @@ -220,6 +236,16 @@ mod tests { assert!(!written.contains("SuperSecretValue123")); } + #[test] + fn handle_reads_min_length_from_input() { + let out = handle(object(alloc::vec![ + ("password", Value::String(String::from("Short1!"))), + ("min_length", Value::Number(12.0)), + ])); + let issues = wasi_capability_runtime::write_json(&out); + assert!(issues.contains("too_short")); + } + #[test] fn output_genuinely_differs_across_distinct_inputs() { let out_a = handle(object(alloc::vec![(