summaryrefslogtreecommitdiff
path: root/tests/integration/rate_limit.rs
diff options
context:
space:
mode:
authorDawid Rycerz <dawid@rycerz.xyz>2026-01-22 22:07:32 +0100
committerDawid Rycerz <dawid@rycerz.xyz>2026-02-10 18:44:26 +0100
commit064a1d01c5c14f5ecc032fa9b8346a4a88b893f6 (patch)
treea2023f9ccd297ed8a41a3a0cc5699c2add09244d /tests/integration/rate_limit.rs
witryna 0.1.0 — initial releasev0.1.0
Minimalist Git-based static site deployment orchestrator. Webhook-triggered builds in Podman/Docker containers with atomic symlink publishing, SIGHUP hot-reload, and zero-downtime deploys. See README.md for usage, CHANGELOG.md for details.
Diffstat (limited to 'tests/integration/rate_limit.rs')
-rw-r--r--tests/integration/rate_limit.rs114
1 files changed, 114 insertions, 0 deletions
diff --git a/tests/integration/rate_limit.rs b/tests/integration/rate_limit.rs
new file mode 100644
index 0000000..81378a2
--- /dev/null
+++ b/tests/integration/rate_limit.rs
@@ -0,0 +1,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);
+}