use crate::{
notifier::NotificationPayload,
reactions::{default_priority_for_reaction_key, EventPriority, ReactionAction, ReactionConfig},
types::{Session, SessionStatus},
};
pub const fn status_to_reaction_key(status: SessionStatus) -> Option<&'static str> {
match status {
SessionStatus::ChangesRequested => Some("changes-requested"),
SessionStatus::Mergeable => Some("approved-and-green"),
SessionStatus::Approved => Some("approved-and-green"),
SessionStatus::Stuck => Some("agent-stuck"),
SessionStatus::NeedsInput => Some("agent-needs-input"),
SessionStatus::Killed => Some("agent-exited"),
_ => None,
}
}
pub(super) fn merge_reaction_config(
global: ReactionConfig,
project: ReactionConfig,
) -> ReactionConfig {
let mut out = global;
out.auto = project.auto;
out.action = project.action;
if project.message.is_some() {
out.message = project.message;
}
if project.priority.is_some() {
out.priority = project.priority;
}
if project.retries.is_some() {
out.retries = project.retries;
}
if project.escalate_after.is_some() {
out.escalate_after = project.escalate_after;
}
if project.threshold.is_some() {
out.threshold = project.threshold;
}
out.include_summary = project.include_summary;
if project.merge_method.is_some() {
out.merge_method = project.merge_method;
}
out
}
pub(super) fn resolve_priority(reaction_key: &str, cfg: &ReactionConfig) -> EventPriority {
cfg.priority
.unwrap_or_else(|| default_priority_for_reaction_key(reaction_key))
}
pub(super) fn build_payload(
session: &Session,
reaction_key: &str,
cfg: &ReactionConfig,
priority: EventPriority,
escalated: bool,
) -> NotificationPayload {
let title = if escalated {
format!("[escalated] {} on {}", reaction_key, session.id)
} else {
format!("{} on {}", reaction_key, session.id)
};
let body = cfg.message.clone().unwrap_or_else(|| {
if escalated {
format!(
"{} escalated to notify after retries exhausted",
reaction_key
)
} else {
format!("Reaction {} fired for session {}", reaction_key, session.id)
}
});
NotificationPayload {
session_id: session.id.clone(),
reaction_key: reaction_key.to_string(),
action: ReactionAction::Notify,
priority,
title,
body,
escalated,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reactions::ReactionAction;
use crate::types::SessionStatus;
#[test]
fn status_map_covers_reactions_through_phase_h() {
assert_eq!(
status_to_reaction_key(SessionStatus::CiFailed),
None,
"CiFailed dispatches via check_ci_failed, not status_to_reaction_key"
);
assert_eq!(
status_to_reaction_key(SessionStatus::ChangesRequested),
Some("changes-requested")
);
assert_eq!(
status_to_reaction_key(SessionStatus::Mergeable),
Some("approved-and-green")
);
assert_eq!(
status_to_reaction_key(SessionStatus::Stuck),
Some("agent-stuck")
);
assert_eq!(
status_to_reaction_key(SessionStatus::NeedsInput),
Some("agent-needs-input"),
"NeedsInput must map to agent-needs-input"
);
assert_eq!(
status_to_reaction_key(SessionStatus::Killed),
Some("agent-exited"),
"Killed must map to agent-exited"
);
assert_eq!(
status_to_reaction_key(SessionStatus::Approved),
Some("approved-and-green"),
"Approved must map to approved-and-green"
);
assert_eq!(status_to_reaction_key(SessionStatus::Working), None);
assert_eq!(status_to_reaction_key(SessionStatus::MergeFailed), None);
assert_eq!(status_to_reaction_key(SessionStatus::Errored), None);
}
#[test]
fn resolve_priority_uses_config_override() {
let mut cfg = ReactionConfig::new(ReactionAction::Notify);
cfg.priority = Some(EventPriority::Urgent);
assert_eq!(resolve_priority("ci-failed", &cfg), EventPriority::Urgent);
}
#[test]
fn resolve_priority_falls_back_to_defaults() {
let cfg = ReactionConfig::new(ReactionAction::Notify);
assert_eq!(resolve_priority("ci-failed", &cfg), EventPriority::Warning);
assert_eq!(
resolve_priority("changes-requested", &cfg),
EventPriority::Info
);
assert_eq!(
resolve_priority("merge-conflicts", &cfg),
EventPriority::Warning
);
assert_eq!(
resolve_priority("approved-and-green", &cfg),
EventPriority::Action
);
assert_eq!(resolve_priority("agent-idle", &cfg), EventPriority::Info);
assert_eq!(resolve_priority("agent-stuck", &cfg), EventPriority::Urgent);
assert_eq!(
resolve_priority("agent-needs-input", &cfg),
EventPriority::Urgent
);
assert_eq!(
resolve_priority("agent-exited", &cfg),
EventPriority::Urgent
);
assert_eq!(resolve_priority("all-complete", &cfg), EventPriority::Info);
assert_eq!(
resolve_priority("unknown-reaction", &cfg),
EventPriority::Warning
);
}
}