huskies: merge 940

This commit is contained in:
dave
2026-05-12 23:05:50 +00:00
parent b8ec3e2025
commit 0f0cf59329
2 changed files with 172 additions and 45 deletions
+169 -42
View File
@@ -33,12 +33,16 @@ pub fn format_stage_notification(
to_stage: &str,
) -> (String, String) {
let number = extract_item_number(item_id).unwrap_or(item_id);
let name = story_name.unwrap_or(item_id);
let effective_name = story_name.filter(|n| !n.is_empty());
let name_plain = effective_name.map(|n| format!("{n} ")).unwrap_or_default();
let name_html = effective_name
.map(|n| format!("<em>{n}</em> "))
.unwrap_or_default();
let prefix = if to_stage == "Done" { "\u{1f389} " } else { "" };
let plain = format!("{prefix}#{number} {name} \u{2014} {from_stage} \u{2192} {to_stage}");
let plain = format!("{prefix}#{number} {name_plain}\u{2014} {from_stage} \u{2192} {to_stage}");
let html = format!(
"{prefix}<strong>#{number}</strong> <em>{name}</em> \u{2014} {from_stage} \u{2192} {to_stage}"
"{prefix}<strong>#{number}</strong> {name_html}\u{2014} {from_stage} \u{2192} {to_stage}"
);
(plain, html)
}
@@ -52,10 +56,14 @@ pub fn format_error_notification(
reason: &str,
) -> (String, String) {
let number = extract_item_number(item_id).unwrap_or(item_id);
let name = story_name.unwrap_or(item_id);
let effective_name = story_name.filter(|n| !n.is_empty());
let name_plain = effective_name.map(|n| format!("{n} ")).unwrap_or_default();
let name_html = effective_name
.map(|n| format!("<em>{n}</em> "))
.unwrap_or_default();
let plain = format!("\u{274c} #{number} {name} \u{2014} {reason}");
let html = format!("\u{274c} <strong>#{number}</strong> <em>{name}</em> \u{2014} {reason}");
let plain = format!("\u{274c} #{number} {name_plain}\u{2014} {reason}");
let html = format!("\u{274c} <strong>#{number}</strong> {name_html}\u{2014} {reason}");
(plain, html)
}
@@ -68,11 +76,15 @@ pub fn format_blocked_notification(
reason: &str,
) -> (String, String) {
let number = extract_item_number(item_id).unwrap_or(item_id);
let name = story_name.unwrap_or(item_id);
let effective_name = story_name.filter(|n| !n.is_empty());
let name_plain = effective_name.map(|n| format!("{n} ")).unwrap_or_default();
let name_html = effective_name
.map(|n| format!("<em>{n}</em> "))
.unwrap_or_default();
let plain = format!("\u{1f6ab} #{number} {name} \u{2014} BLOCKED: {reason}");
let plain = format!("\u{1f6ab} #{number} {name_plain}\u{2014} BLOCKED: {reason}");
let html =
format!("\u{1f6ab} <strong>#{number}</strong> <em>{name}</em> \u{2014} BLOCKED: {reason}");
format!("\u{1f6ab} <strong>#{number}</strong> {name_html}\u{2014} BLOCKED: {reason}");
(plain, html)
}
@@ -85,12 +97,17 @@ pub fn format_rate_limit_notification(
agent_name: &str,
) -> (String, String) {
let number = extract_item_number(item_id).unwrap_or(item_id);
let name = story_name.unwrap_or(item_id);
let effective_name = story_name.filter(|n| !n.is_empty());
let name_plain = effective_name.map(|n| format!("{n} ")).unwrap_or_default();
let name_html = effective_name
.map(|n| format!("<em>{n}</em> "))
.unwrap_or_default();
let plain =
format!("\u{26a0}\u{fe0f} #{number} {name} \u{2014} {agent_name} hit an API rate limit");
let plain = format!(
"\u{26a0}\u{fe0f} #{number} {name_plain}\u{2014} {agent_name} hit an API rate limit"
);
let html = format!(
"\u{26a0}\u{fe0f} <strong>#{number}</strong> <em>{name}</em> \u{2014} \
"\u{26a0}\u{fe0f} <strong>#{number}</strong> {name_html}\u{2014} \
{agent_name} hit an API rate limit"
);
(plain, html)
@@ -127,11 +144,15 @@ pub fn format_agent_started_notification(
agent_name: &str,
) -> (String, String) {
let number = extract_item_number(item_id).unwrap_or(item_id);
let name = story_name.unwrap_or(item_id);
let plain = format!("\u{1F916} #{number} {name} \u{2014} {agent_name} started");
let html = format!(
"\u{1F916} <strong>#{number}</strong> <em>{name}</em> \u{2014} {agent_name} started"
);
let effective_name = story_name.filter(|n| !n.is_empty());
let name_plain = effective_name.map(|n| format!("{n} ")).unwrap_or_default();
let name_html = effective_name
.map(|n| format!("<em>{n}</em> "))
.unwrap_or_default();
let plain = format!("\u{1F916} #{number} {name_plain}\u{2014} {agent_name} started");
let html =
format!("\u{1F916} <strong>#{number}</strong> {name_html}\u{2014} {agent_name} started");
(plain, html)
}
@@ -146,16 +167,20 @@ pub fn format_agent_completed_notification(
success: bool,
) -> (String, String) {
let number = extract_item_number(item_id).unwrap_or(item_id);
let name = story_name.unwrap_or(item_id);
let effective_name = story_name.filter(|n| !n.is_empty());
let name_plain = effective_name.map(|n| format!("{n} ")).unwrap_or_default();
let name_html = effective_name
.map(|n| format!("<em>{n}</em> "))
.unwrap_or_default();
let (emoji, result) = if success {
("\u{2705}", "completed") // ✅
} else {
("\u{274C}", "failed") // ❌
};
let plain = format!("{emoji} #{number} {name} \u{2014} {agent_name} {result}");
let html = format!(
"{emoji} <strong>#{number}</strong> <em>{name}</em> \u{2014} {agent_name} {result}"
);
let plain = format!("{emoji} #{number} {name_plain}\u{2014} {agent_name} {result}");
let html =
format!("{emoji} <strong>#{number}</strong> {name_html}\u{2014} {agent_name} {result}");
(plain, html)
}
@@ -241,9 +266,10 @@ mod tests {
}
#[test]
fn format_notification_without_story_name_falls_back_to_item_id() {
let (plain, _html) = format_stage_notification("42_bug_fix_thing", None, "Current", "QA");
assert_eq!(plain, "#42 42_bug_fix_thing \u{2014} Current \u{2192} QA");
fn format_stage_notification_without_story_name_falls_back_to_number() {
let (plain, html) = format_stage_notification("42_bug_fix_thing", None, "Current", "QA");
assert_eq!(plain, "#42 \u{2014} Current \u{2192} QA");
assert_eq!(html, "<strong>#42</strong> \u{2014} Current \u{2192} QA");
}
#[test]
@@ -265,12 +291,10 @@ mod tests {
}
#[test]
fn format_stage_notification_empty_story_name_falls_back_to_id() {
// Some("") is a valid Some but empty — treat as missing? Currently we use it as-is.
let (plain, _html) = format_stage_notification("42_story_empty", Some(""), "Current", "QA");
// The name slot is empty but the structure is still correct.
assert!(plain.contains("#42"));
assert!(plain.contains("Current \u{2192} QA"));
fn format_stage_notification_empty_story_name_falls_back_to_number() {
let (plain, html) = format_stage_notification("42_story_empty", Some(""), "Current", "QA");
assert_eq!(plain, "#42 \u{2014} Current \u{2192} QA");
assert_eq!(html, "<strong>#42</strong> \u{2014} Current \u{2192} QA");
}
#[test]
@@ -301,9 +325,10 @@ mod tests {
}
#[test]
fn format_error_notification_without_story_name_falls_back_to_item_id() {
let (plain, _html) = format_error_notification("42_bug_fix_thing", None, "tests failed");
assert_eq!(plain, "\u{274c} #42 42_bug_fix_thing \u{2014} tests failed");
fn format_error_notification_without_story_name_falls_back_to_number() {
let (plain, html) = format_error_notification("42_bug_fix_thing", None, "tests failed");
assert_eq!(plain, "\u{274c} #42 \u{2014} tests failed");
assert_eq!(html, "\u{274c} <strong>#42</strong> \u{2014} tests failed");
}
#[test]
@@ -330,6 +355,13 @@ mod tests {
assert!(plain.contains("错误:合并冲突"));
}
#[test]
fn format_error_notification_empty_story_name_falls_back_to_number() {
let (plain, _html) =
format_error_notification("42_bug_fix_thing", Some(""), "tests failed");
assert_eq!(plain, "\u{274c} #42 \u{2014} tests failed");
}
// ── format_blocked_notification ───────────────────────────────────────────
#[test]
@@ -350,14 +382,21 @@ mod tests {
}
#[test]
fn format_blocked_notification_falls_back_to_item_id() {
let (plain, _html) = format_blocked_notification("42_story_thing", None, "empty diff");
fn format_blocked_notification_falls_back_to_number() {
let (plain, html) = format_blocked_notification("42_story_thing", None, "empty diff");
assert_eq!(plain, "\u{1f6ab} #42 \u{2014} BLOCKED: empty diff");
assert_eq!(
plain,
"\u{1f6ab} #42 42_story_thing \u{2014} BLOCKED: empty diff"
html,
"\u{1f6ab} <strong>#42</strong> \u{2014} BLOCKED: empty diff"
);
}
#[test]
fn format_blocked_notification_empty_story_name_falls_back_to_number() {
let (plain, _html) = format_blocked_notification("42_story_thing", Some(""), "empty diff");
assert_eq!(plain, "\u{1f6ab} #42 \u{2014} BLOCKED: empty diff");
}
#[test]
fn format_blocked_notification_unicode_reason() {
let (plain, _html) = format_blocked_notification("3_story_x", Some("X"), "理由:空の差分");
@@ -381,11 +420,24 @@ mod tests {
}
#[test]
fn format_rate_limit_notification_falls_back_to_item_id() {
let (plain, _html) = format_rate_limit_notification("42_story_thing", None, "coder-1");
fn format_rate_limit_notification_falls_back_to_number() {
let (plain, html) = format_rate_limit_notification("42_story_thing", None, "coder-1");
assert_eq!(
plain,
"\u{26a0}\u{fe0f} #42 42_story_thing \u{2014} coder-1 hit an API rate limit"
"\u{26a0}\u{fe0f} #42 \u{2014} coder-1 hit an API rate limit"
);
assert_eq!(
html,
"\u{26a0}\u{fe0f} <strong>#42</strong> \u{2014} coder-1 hit an API rate limit"
);
}
#[test]
fn format_rate_limit_notification_empty_story_name_falls_back_to_number() {
let (plain, _html) = format_rate_limit_notification("42_story_thing", Some(""), "coder-1");
assert_eq!(
plain,
"\u{26a0}\u{fe0f} #42 \u{2014} coder-1 hit an API rate limit"
);
}
@@ -395,4 +447,79 @@ mod tests {
assert!(plain.contains("агент-1"));
assert!(plain.contains("hit an API rate limit"));
}
// ── format_agent_started_notification ─────────────────────────────────────
#[test]
fn format_agent_started_notification_with_story_name() {
let (plain, html) =
format_agent_started_notification("42_story_foo", Some("My Feature"), "coder-1");
assert_eq!(plain, "\u{1F916} #42 My Feature \u{2014} coder-1 started");
assert_eq!(
html,
"\u{1F916} <strong>#42</strong> <em>My Feature</em> \u{2014} coder-1 started"
);
}
#[test]
fn format_agent_started_notification_falls_back_to_number() {
let (plain, html) = format_agent_started_notification("42_story_foo", None, "coder-1");
assert_eq!(plain, "\u{1F916} #42 \u{2014} coder-1 started");
assert_eq!(
html,
"\u{1F916} <strong>#42</strong> \u{2014} coder-1 started"
);
}
#[test]
fn format_agent_started_notification_empty_name_falls_back_to_number() {
let (plain, _html) = format_agent_started_notification("42_story_foo", Some(""), "coder-1");
assert_eq!(plain, "\u{1F916} #42 \u{2014} coder-1 started");
}
// ── format_agent_completed_notification ───────────────────────────────────
#[test]
fn format_agent_completed_notification_success_with_story_name() {
let (plain, html) = format_agent_completed_notification(
"42_story_foo",
Some("My Feature"),
"coder-1",
true,
);
assert_eq!(plain, "\u{2705} #42 My Feature \u{2014} coder-1 completed");
assert_eq!(
html,
"\u{2705} <strong>#42</strong> <em>My Feature</em> \u{2014} coder-1 completed"
);
}
#[test]
fn format_agent_completed_notification_failure_with_story_name() {
let (plain, _html) = format_agent_completed_notification(
"42_story_foo",
Some("My Feature"),
"coder-1",
false,
);
assert_eq!(plain, "\u{274C} #42 My Feature \u{2014} coder-1 failed");
}
#[test]
fn format_agent_completed_notification_falls_back_to_number() {
let (plain, html) =
format_agent_completed_notification("42_story_foo", None, "coder-1", true);
assert_eq!(plain, "\u{2705} #42 \u{2014} coder-1 completed");
assert_eq!(
html,
"\u{2705} <strong>#42</strong> \u{2014} coder-1 completed"
);
}
#[test]
fn format_agent_completed_notification_empty_name_falls_back_to_number() {
let (plain, _html) =
format_agent_completed_notification("42_story_foo", Some(""), "coder-1", false);
assert_eq!(plain, "\u{274C} #42 \u{2014} coder-1 failed");
}
}