use crate::channels::telegram::governor::test_support;
use crate::channels::telegram::plan_card::{
EditOutcome, handle_create_failure, handle_edit_failure,
};
use crate::channels::telegram::rate_limit::{is_global_cooldown_active, reset_global_cooldown};
use crate::channels::telegram::state::TelegramState;
use teloxide::types::{ChatId, MessageId};
use uuid::Uuid;
#[tokio::test]
async fn plan_card_create_throttle_arms_the_global_cooldown() {
let _guard = test_support::registry_guard().await;
let _cooldown = crate::tests::telegram_cooldown_lock::guard().await;
test_support::reset(0);
reset_global_cooldown();
assert!(!is_global_cooldown_active(), "precondition: no cooldown");
let state = TelegramState::new();
handle_create_failure("Too Many Requests: retry after 9", &state, Uuid::new_v4()).await;
assert!(
is_global_cooldown_active(),
"a 429 on card create must reach the process-wide deadline"
);
reset_global_cooldown();
}
#[tokio::test]
async fn plan_card_edit_throttle_arms_the_global_cooldown() {
let _guard = test_support::registry_guard().await;
let _cooldown = crate::tests::telegram_cooldown_lock::guard().await;
test_support::reset(0);
reset_global_cooldown();
assert!(!is_global_cooldown_active(), "precondition: no cooldown");
let state = TelegramState::new();
let outcome = handle_edit_failure(
"Too Many Requests: retry after 9",
&state,
Uuid::new_v4(),
ChatId(-100_123),
None,
"sig",
MessageId(7),
)
.await;
assert!(matches!(outcome, EditOutcome::Suppressed));
assert!(
is_global_cooldown_active(),
"a 429 on card edit must reach the process-wide deadline"
);
reset_global_cooldown();
}
#[tokio::test]
async fn a_non_throttle_card_failure_leaves_the_cooldown_alone() {
let _guard = test_support::registry_guard().await;
let _cooldown = crate::tests::telegram_cooldown_lock::guard().await;
test_support::reset(0);
reset_global_cooldown();
let state = TelegramState::new();
handle_create_failure("Bad Request: message text is empty", &state, Uuid::new_v4()).await;
assert!(
!is_global_cooldown_active(),
"only a retry-after may arm the global deadline"
);
}
#[test]
fn both_pacers_take_the_global_permit_before_the_per_chat_gate() {
let src = include_str!("../channels/telegram/governor.rs");
for pacer in [
"pub(crate) async fn pace_send(",
"pub(crate) async fn pace_rich(",
] {
let start = src
.find(pacer)
.unwrap_or_else(|| panic!("{pacer} not found — rename?"));
let body = &src[start..];
let permit = body
.find("acquire_global_permit()")
.unwrap_or_else(|| panic!("{pacer} no longer takes a global permit"));
let gate = body
.find("if !lim.enabled")
.unwrap_or_else(|| panic!("{pacer} no longer has the per-chat enable gate"));
assert!(
permit < gate,
"{pacer}: the global permit must be acquired before the per-chat \
enable gate, otherwise disabling per-chat pacing also disables \
the process-wide ceiling and the 429 cooldown"
);
}
}