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);
}
|