summaryrefslogtreecommitdiff
path: root/tests/integration/rate_limit.rs
blob: 81378a2a43a64af2372f70b698fe5f9a816909b6 (plain)
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use crate::harness::{SiteBuilder, TestServer, test_config_with_site, test_config_with_sites};

#[tokio::test]
async fn rate_limit_exceeded_returns_429() {
    let dir = tempfile::tempdir().unwrap().keep();
    let site = SiteBuilder::new("my-site", "https://example.com/repo.git", "secret-token").build();
    let config = test_config_with_site(dir, site);

    // Rate limit of 2 per minute
    let server = TestServer::start_with_rate_limit(config, 2).await;

    // First request — accepted (or 202)
    let resp1 = TestServer::client()
        .post(server.url("/my-site"))
        .header("Authorization", "Bearer secret-token")
        .send()
        .await
        .unwrap();
    let status1 = resp1.status().as_u16();
    assert!(
        status1 == 202 || status1 == 409,
        "expected 202 or 409, got {status1}"
    );

    // Second request
    let resp2 = TestServer::client()
        .post(server.url("/my-site"))
        .header("Authorization", "Bearer secret-token")
        .send()
        .await
        .unwrap();
    let status2 = resp2.status().as_u16();
    assert!(
        status2 == 202 || status2 == 409,
        "expected 202 or 409, got {status2}"
    );

    // Third request should hit rate limit
    let resp3 = TestServer::client()
        .post(server.url("/my-site"))
        .header("Authorization", "Bearer secret-token")
        .send()
        .await
        .unwrap();
    assert_eq!(resp3.status().as_u16(), 429);
    let body = resp3.text().await.unwrap();
    let json: serde_json::Value = serde_json::from_str(&body).unwrap();
    assert_eq!(json["error"], "rate_limit_exceeded");
}

#[tokio::test]
async fn rate_limit_different_tokens_independent() {
    let dir = tempfile::tempdir().unwrap().keep();
    let sites = vec![
        SiteBuilder::new("site-one", "https://example.com/one.git", "token-one").build(),
        SiteBuilder::new("site-two", "https://example.com/two.git", "token-two").build(),
    ];
    let config = test_config_with_sites(dir, sites);

    // Rate limit of 1 per minute
    let server = TestServer::start_with_rate_limit(config, 1).await;

    // token-one: first request succeeds
    let resp1 = TestServer::client()
        .post(server.url("/site-one"))
        .header("Authorization", "Bearer token-one")
        .send()
        .await
        .unwrap();
    assert_eq!(resp1.status().as_u16(), 202);

    // token-one: second request hits rate limit
    let resp2 = TestServer::client()
        .post(server.url("/site-one"))
        .header("Authorization", "Bearer token-one")
        .send()
        .await
        .unwrap();
    assert_eq!(resp2.status().as_u16(), 429);

    // token-two: still has its own budget
    let resp3 = TestServer::client()
        .post(server.url("/site-two"))
        .header("Authorization", "Bearer token-two")
        .send()
        .await
        .unwrap();
    assert_eq!(resp3.status().as_u16(), 202);
}

#[tokio::test]
async fn rate_limit_checked_after_auth() {
    let dir = tempfile::tempdir().unwrap().keep();
    let site = SiteBuilder::new("my-site", "https://example.com/repo.git", "secret-token").build();
    let config = test_config_with_site(dir, site);
    let server = TestServer::start_with_rate_limit(config, 1).await;

    // Exhaust rate limit
    let _ = TestServer::client()
        .post(server.url("/my-site"))
        .header("Authorization", "Bearer secret-token")
        .send()
        .await
        .unwrap();

    // Wrong token should get 401, not 429
    let resp = TestServer::client()
        .post(server.url("/my-site"))
        .header("Authorization", "Bearer wrong-token")
        .send()
        .await
        .unwrap();
    assert_eq!(resp.status().as_u16(), 401);
}