92 lines
3.4 KiB
Rust
92 lines
3.4 KiB
Rust
|
|
//! 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"));
|
||
|
|
}
|
||
|
|
}
|