//! Gateway notification polling — pure event formatting. //! //! Formats pipeline events from project containers into gateway notifications //! with `[project-name]` prefixes. The actual I/O (HTTP polling, spawning //! tasks, sending messages) lives in `io.rs`. use crate::service::events::StoredEvent; use crate::service::notifications::{ format_blocked_notification, format_error_notification, format_stage_notification, stage_display_name, }; /// Format a [`StoredEvent`] from a project into a gateway notification. /// /// Prefixes the message with `[project-name]` so users can distinguish which /// project emitted the event. pub fn format_gateway_event(project_name: &str, event: &StoredEvent) -> (String, String) { let prefix = format!("[{project_name}] "); match event { StoredEvent::StageTransition { story_id, from_stage, to_stage, .. } => { let from_display = stage_display_name(from_stage); let to_display = stage_display_name(to_stage); let (plain, html) = format_stage_notification(story_id, None, from_display, to_display); (format!("{prefix}{plain}"), format!("{prefix}{html}")) } StoredEvent::MergeFailure { story_id, reason, .. } => { let (plain, html) = format_error_notification(story_id, None, reason); (format!("{prefix}{plain}"), format!("{prefix}{html}")) } StoredEvent::StoryBlocked { story_id, reason, .. } => { let (plain, html) = format_blocked_notification(story_id, None, reason); (format!("{prefix}{plain}"), format!("{prefix}{html}")) } } } // ── Tests ──────────────────────────────────────────────────────────────────── #[cfg(test)] mod tests { use super::*; #[test] fn stage_transition_prefixes_project_name() { let event = StoredEvent::StageTransition { story_id: "42_story_my_feature".to_string(), from_stage: "2_current".to_string(), to_stage: "3_qa".to_string(), timestamp_ms: 1000, }; let (plain, html) = format_gateway_event("huskies", &event); assert!(plain.starts_with("[huskies] ")); assert!(html.starts_with("[huskies] ")); assert!(plain.contains("Current")); assert!(plain.contains("QA")); } #[test] fn merge_failure_prefixes_project_name() { let event = StoredEvent::MergeFailure { story_id: "42_story_my_feature".to_string(), reason: "merge conflict".to_string(), timestamp_ms: 1000, }; let (plain, _html) = format_gateway_event("robot-studio", &event); assert!(plain.starts_with("[robot-studio] ")); assert!(plain.contains("merge conflict")); } #[test] fn story_blocked_prefixes_project_name() { let event = StoredEvent::StoryBlocked { story_id: "43_story_bar".to_string(), reason: "retry limit exceeded".to_string(), timestamp_ms: 2000, }; let (plain, _html) = format_gateway_event("huskies", &event); assert!(plain.starts_with("[huskies] ")); assert!(plain.contains("BLOCKED")); } }