CLI app for developers prototyping atproto functionality
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(sentinel): move tests into module, simplify hex encoding, and tidy docs

- Move sentinel_tests module from create_report.rs into sentinel.rs (tests
belong with the code they test, per CLAUDE.md convention).
- Add comprehensive test coverage for format_rfc3339_utc including epoch,
pre-epoch, leap years (1972, 2000, 2024), and year 9999 boundary.
- Make format_rfc3339_utc and unix_to_civil pub to enable direct testing.
- Simplify new_run_id hex encoding to iter().map().collect() pattern
with #[expect(clippy::format_collect)] for clarity.
- Update run_id doc comment to drop stale "(see Cargo.toml edit below)"
reference and clarify that panicking is impossible on supported platforms.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

authored by

Jack Grigg
Claude Haiku 4.5
and committed by
Tangled
9cfd4eda 779c07af

+67 -59
-52
src/commands/test/labeler/create_report.rs
··· 6 6 //! by later phases for the pollution-avoidance sentinel reason string. 7 7 8 8 pub mod sentinel; 9 - 10 - #[cfg(test)] 11 - mod sentinel_tests { 12 - use super::sentinel; 13 - 14 - #[test] 15 - fn format_rfc3339_utc_pins_known_points() { 16 - use std::time::UNIX_EPOCH; 17 - // 1970-01-01T00:00:00Z 18 - assert_eq!( 19 - sentinel::build("test0000test0000", UNIX_EPOCH), 20 - "atproto-devtool conformance test 1970-01-01T00:00:00Z test0000test0000" 21 - ); 22 - // 2026-04-20T00:00:00Z (1_776_643_200 UNIX seconds) 23 - let t = UNIX_EPOCH + std::time::Duration::from_secs(1_776_643_200); 24 - let s = sentinel::build("test0000test0000", t); 25 - assert_eq!( 26 - s, 27 - "atproto-devtool conformance test 2026-04-20T00:00:00Z test0000test0000" 28 - ); 29 - // Leap year: 2024-02-29T12:34:56Z (1_709_210_096 UNIX seconds) 30 - let t = UNIX_EPOCH + std::time::Duration::from_secs(1_709_210_096); 31 - let s = sentinel::build("test0000test0000", t); 32 - assert_eq!( 33 - s, 34 - "atproto-devtool conformance test 2024-02-29T12:34:56Z test0000test0000" 35 - ); 36 - } 37 - 38 - #[test] 39 - fn build_contains_prefix_and_run_id() { 40 - use std::time::UNIX_EPOCH; 41 - let s = sentinel::build("abcdef1234567890", UNIX_EPOCH); 42 - assert!(s.starts_with(sentinel::SENTINEL_PREFIX)); 43 - assert!(s.ends_with("abcdef1234567890")); 44 - assert!(s.contains("1970-01-01T00:00:00Z")); 45 - } 46 - 47 - #[test] 48 - fn new_run_id_is_16_hex_chars() { 49 - let id = sentinel::new_run_id(); 50 - assert_eq!(id.len(), 16); 51 - assert!(id.chars().all(|c| c.is_ascii_hexdigit())); 52 - } 53 - 54 - #[test] 55 - fn new_run_id_is_unique_between_calls() { 56 - let a = sentinel::new_run_id(); 57 - let b = sentinel::new_run_id(); 58 - assert_ne!(a, b); 59 - } 60 - }
+67 -7
src/commands/test/labeler/create_report/sentinel.rs
··· 34 34 /// the sentinel reason is a human-readable label, not a parseable timestamp. 35 35 /// For times before the UNIX epoch or more than `i64::MAX` seconds in the 36 36 /// future we degrade gracefully to `1970-01-01T00:00:00Z`. 37 - fn format_rfc3339_utc(ts: SystemTime) -> String { 37 + pub fn format_rfc3339_utc(ts: SystemTime) -> String { 38 38 let secs = ts 39 39 .duration_since(UNIX_EPOCH) 40 40 .map(|d| d.as_secs() as i64) ··· 45 45 46 46 /// Convert UNIX seconds to a civil date-time (UTC) using Howard Hinnant's 47 47 /// algorithm for the Gregorian calendar. Correct for all years in [1, 9999]. 48 - fn unix_to_civil(secs: i64) -> (i32, u32, u32, u32, u32, u32) { 48 + pub fn unix_to_civil(secs: i64) -> (i32, u32, u32, u32, u32, u32) { 49 49 // Seconds-of-day. 50 50 let days = secs.div_euclid(86_400); 51 51 let sod = secs.rem_euclid(86_400); ··· 69 69 } 70 70 71 71 /// Generate a random 16-hex-char run identifier. Uses `getrandom` 72 - /// (added as a direct dep during this task — see Cargo.toml edit below). 72 + /// (panics if the OS CSPRNG is unavailable, which cannot happen on supported 73 + /// platforms). 73 74 pub fn new_run_id() -> String { 74 75 let mut bytes = [0u8; 8]; 75 76 getrandom::getrandom(&mut bytes).expect("OS CSPRNG is always available on supported platforms"); 76 - bytes.iter().fold(String::new(), |mut s, b| { 77 - s.push_str(&format!("{b:02x}")); 78 - s 79 - }) 77 + #[expect(clippy::format_collect)] 78 + { 79 + bytes.iter().map(|b| format!("{b:02x}")).collect() 80 + } 81 + } 82 + 83 + #[cfg(test)] 84 + mod tests { 85 + use super::*; 86 + 87 + #[test] 88 + fn format_rfc3339_utc_pins_known_points() { 89 + // Pre-epoch time degrades gracefully to 1970-01-01T00:00:00Z. 90 + let before_epoch = UNIX_EPOCH.checked_sub(std::time::Duration::from_secs(1)); 91 + if let Some(t) = before_epoch { 92 + assert_eq!(format_rfc3339_utc(t), "1970-01-01T00:00:00Z"); 93 + } 94 + 95 + // 1970-01-01T00:00:00Z (epoch). 96 + assert_eq!(format_rfc3339_utc(UNIX_EPOCH), "1970-01-01T00:00:00Z"); 97 + 98 + // 2024-02-29T00:00:00Z (leap year, leap day). 99 + let t = UNIX_EPOCH + std::time::Duration::from_secs(1_709_251_200); 100 + assert_eq!(format_rfc3339_utc(t), "2024-02-29T00:00:00Z"); 101 + 102 + // 2024-02-29T12:34:56Z (leap year, leap day with time). 103 + let t = UNIX_EPOCH + std::time::Duration::from_secs(1_709_296_496); 104 + assert_eq!(format_rfc3339_utc(t), "2024-02-29T12:34:56Z"); 105 + 106 + // 2000-02-29T00:00:00Z (leap year, leap day in year 2000). 107 + let t = UNIX_EPOCH + std::time::Duration::from_secs(951_868_800); 108 + assert_eq!(format_rfc3339_utc(t), "2000-02-29T00:00:00Z"); 109 + 110 + // 1972-02-29T00:00:00Z (leap year). 111 + let t = UNIX_EPOCH + std::time::Duration::from_secs(68_169_600); 112 + assert_eq!(format_rfc3339_utc(t), "1972-02-29T00:00:00Z"); 113 + 114 + // Year 9999-12-31T23:59:59Z (boundary). 115 + let t = UNIX_EPOCH + std::time::Duration::from_secs(253_402_300_799); 116 + assert_eq!(format_rfc3339_utc(t), "9999-12-31T23:59:59Z"); 117 + } 118 + 119 + #[test] 120 + fn build_contains_prefix_and_run_id() { 121 + let s = build("abcdef1234567890", UNIX_EPOCH); 122 + assert!(s.starts_with(SENTINEL_PREFIX)); 123 + assert!(s.ends_with("abcdef1234567890")); 124 + assert!(s.contains("1970-01-01T00:00:00Z")); 125 + } 126 + 127 + #[test] 128 + fn new_run_id_is_16_hex_chars() { 129 + let id = new_run_id(); 130 + assert_eq!(id.len(), 16); 131 + assert!(id.chars().all(|c| c.is_ascii_hexdigit())); 132 + } 133 + 134 + #[test] 135 + fn new_run_id_is_unique_between_calls() { 136 + let a = new_run_id(); 137 + let b = new_run_id(); 138 + assert_ne!(a, b); 139 + } 80 140 }