File size: 2,127 Bytes
a21c316
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//! 测试 RateLimitTracker::parse_from_error 对 404 状态码的处理逻辑:
//! - 短时锁定(5s)
//! - 不累加失败计数
//! - 与 5xx 锁定时长的差异

use crate::proxy::rate_limit::{RateLimitReason, RateLimitTracker};

#[test]
fn test_parse_from_error_404_short_lockout() {
    let tracker = RateLimitTracker::new();
    let backoff_steps = vec![60, 300, 1800, 7200];

    let info = tracker.parse_from_error("acc_404", 404, None, "Not Found", None, &backoff_steps);
    assert!(info.is_some(), "404 should return Some(RateLimitInfo)");
    let info = info.unwrap();
    assert_eq!(info.retry_after_sec, 5, "404 should lock out for 5 seconds");
    assert_eq!(info.reason, RateLimitReason::ServerError, "404 reason should be ServerError");
}

#[test]
fn test_404_does_not_accumulate_failure_count() {
    let tracker = RateLimitTracker::new();
    let backoff_steps = vec![60, 300, 1800, 7200];

    // 连续多次 404,锁定时间应始终为 5s(不像 429 QuotaExhausted 那样递增)
    for i in 1..=5 {
        // 清除上一次的限流记录,模拟轮换后再次遇到 404
        tracker.clear("acc_404_repeat");
        let info = tracker.parse_from_error(
            "acc_404_repeat", 404, None, "Not Found", None, &backoff_steps,
        );
        assert!(info.is_some(), "404 attempt {} should return Some", i);
        assert_eq!(
            info.unwrap().retry_after_sec, 5,
            "404 attempt {} should still lock for 5s, not escalate", i
        );
    }
}

#[test]
fn test_404_vs_5xx_lockout_duration() {
    let tracker = RateLimitTracker::new();
    let backoff_steps = vec![60, 300, 1800, 7200];

    // 404 → 5s lockout
    let info_404 = tracker.parse_from_error(
        "acc_cmp_404", 404, None, "Not Found", None, &backoff_steps,
    );
    assert_eq!(info_404.unwrap().retry_after_sec, 5, "404 should lock for 5s");

    // 503 → 8s lockout
    let info_503 = tracker.parse_from_error(
        "acc_cmp_503", 503, None, "Service Unavailable", None, &backoff_steps,
    );
    assert_eq!(info_503.unwrap().retry_after_sec, 8, "503 should lock for 8s");
}