From d09e44c055497e9a40d8f8a2a4b11f68792afd24 Mon Sep 17 00:00:00 2001 From: dreamhunter2333 Date: Sun, 23 Aug 2026 21:58:23 +0800 Subject: [PATCH] test: isolate user send rate limits --- e2e/docker-compose.yml | 21 ++++++ e2e/fixtures/wrangler.toml.e2e | 5 -- e2e/fixtures/wrangler.toml.e2e.rate-limit | 34 ++++++++++ e2e/tests/api/user-send-mail.spec.ts | 78 +++++++++++++++++------ 4 files changed, 113 insertions(+), 25 deletions(-) create mode 100644 e2e/fixtures/wrangler.toml.e2e.rate-limit diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 678b124..98f2cbc 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -90,6 +90,24 @@ services: start_period: 10s retries: 20 + worker-rate-limit: + build: + context: .. + dockerfile: e2e/Dockerfile.worker + args: + WRANGLER_TOML: e2e/fixtures/wrangler.toml.e2e.rate-limit + ports: + - "8792:8792" + command: ["pnpm", "exec", "wrangler", "dev", "--port", "8792", "--ip", "0.0.0.0"] + depends_on: + - mailpit + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:8792/health_check"] + interval: 3s + timeout: 5s + start_period: 10s + retries: 20 + frontend: build: context: .. @@ -147,6 +165,7 @@ services: WORKER_URL_ENV_OFF: http://worker-env-off:8790 WORKER_GZIP_URL: http://worker-gzip:8788 WORKER_URL_SEND_MAIL_DOMAIN: http://worker-send-mail-domain:8791 + WORKER_URL_RATE_LIMIT: http://worker-rate-limit:8792 FRONTEND_URL: https://frontend:5173 MAILPIT_API: http://mailpit:8025/api SMTP_PROXY_HOST: smtp-proxy @@ -167,6 +186,8 @@ services: condition: service_healthy worker-send-mail-domain: condition: service_healthy + worker-rate-limit: + condition: service_healthy frontend: condition: service_started smtp-proxy: diff --git a/e2e/fixtures/wrangler.toml.e2e b/e2e/fixtures/wrangler.toml.e2e index e1ab0ee..0756436 100644 --- a/e2e/fixtures/wrangler.toml.e2e +++ b/e2e/fixtures/wrangler.toml.e2e @@ -39,8 +39,3 @@ id = "e2e-test-kv-00000000-0000-0000-0000-000000000000" binding = "DB" database_name = "e2e-temp-email" database_id = "e2e-test-db-00000000-0000-0000-0000-000000000000" - -[[ratelimits]] -name = "RATE_LIMITER" -namespace_id = "1001" -simple = { limit = 2, period = 60 } diff --git a/e2e/fixtures/wrangler.toml.e2e.rate-limit b/e2e/fixtures/wrangler.toml.e2e.rate-limit new file mode 100644 index 0000000..57948de --- /dev/null +++ b/e2e/fixtures/wrangler.toml.e2e.rate-limit @@ -0,0 +1,34 @@ +name = "cloudflare_temp_email_rate_limit" +main = "src/worker.ts" +compatibility_date = "2025-04-01" +compatibility_flags = [ "nodejs_compat" ] +keep_vars = true + +[vars] +PREFIX = "tmp" +DEFAULT_DOMAINS = ["test.example.com"] +DOMAINS = ["test.example.com"] +JWT_SECRET = "e2e-test-secret-key-rate-limit" +BLACK_LIST = "" +ENABLE_USER_CREATE_EMAIL = true +ENABLE_USER_DELETE_EMAIL = true +DEFAULT_SEND_BALANCE = 10 +DISABLE_ADMIN_PASSWORD_CHECK = true +E2E_TEST_MODE = true +SMTP_CONFIG = """ +{"test.example.com":{"host":"mailpit","port":1025,"secure":false}} +""" + +[[kv_namespaces]] +binding = "KV" +id = "e2e-test-kv-rate-limit-00000000-0000-0000-0000-000000000000" + +[[d1_databases]] +binding = "DB" +database_name = "e2e-temp-email-rate-limit" +database_id = "e2e-test-db-rate-limit-00000000-0000-0000-0000-000000000000" + +[[ratelimits]] +name = "RATE_LIMITER" +namespace_id = "1001" +simple = { limit = 2, period = 60 } diff --git a/e2e/tests/api/user-send-mail.spec.ts b/e2e/tests/api/user-send-mail.spec.ts index 1c86cfc..3db3d13 100644 --- a/e2e/tests/api/user-send-mail.spec.ts +++ b/e2e/tests/api/user-send-mail.spec.ts @@ -11,15 +11,15 @@ import { updateAddressSender, } from '../../fixtures/test-helpers'; -async function createUser(request: APIRequestContext) { +async function createUser(request: APIRequestContext, workerUrl = WORKER_URL) { const email = `user-send-${Date.now()}@test.example.com`; const password = hashPassword('test-password-123'); - const registerRes = await request.post(`${WORKER_URL}/user_api/register`, { + const registerRes = await request.post(`${workerUrl}/user_api/register`, { data: { email, password }, }); expect(registerRes.ok()).toBe(true); - const loginRes = await request.post(`${WORKER_URL}/user_api/login`, { + const loginRes = await request.post(`${workerUrl}/user_api/login`, { data: { email, password }, }); expect(loginRes.ok()).toBe(true); @@ -32,8 +32,9 @@ async function bindAddress( request: APIRequestContext, userJwt: string, addressJwt: string, + workerUrl = WORKER_URL, ) { - const response = await request.post(`${WORKER_URL}/user_api/bind_address`, { + const response = await request.post(`${workerUrl}/user_api/bind_address`, { headers: { Authorization: `Bearer ${addressJwt}`, 'x-user-token': userJwt, @@ -283,8 +284,22 @@ test.describe('User send mail API', () => { test('applies unlimited balance from the user role access token', async ({ request }) => { const addresses: Awaited>[] = []; let userId: number | undefined; + let originalUserSettings: Record | undefined; try { + const settingsRes = await request.get(`${WORKER_URL}/admin/user_settings`); + expect(settingsRes.ok()).toBe(true); + originalUserSettings = await settingsRes.json(); + const enableUserRes = await request.post(`${WORKER_URL}/admin/user_settings`, { + data: { + ...originalUserSettings, + enable: true, + enableMailVerify: false, + maxAddressCount: 0, + }, + }); + expect(enableUserRes.ok()).toBe(true); + const user = await createUser(request); userId = user.userId; const address = await createTestAddress(request, 'user-send-role-'); @@ -346,25 +361,50 @@ test.describe('User send mail API', () => { if (userId !== undefined) { await request.delete(`${WORKER_URL}/admin/users/${userId}`); } + if (originalUserSettings) { + await request.post(`${WORKER_URL}/admin/user_settings`, { + data: originalUserSettings, + }); + } } }); test('shares one rate-limit bucket across bound addresses', async ({ request }) => { - const addresses: Awaited>[] = []; + const workerUrl = process.env.WORKER_URL_RATE_LIMIT || ''; + test.skip(!workerUrl, 'WORKER_URL_RATE_LIMIT is not configured'); + const addressIds: number[] = []; let userId: number | undefined; try { - const user = await createUser(request); + const enableUserRes = await request.post(`${workerUrl}/admin/user_settings`, { + data: { + enable: true, + enableMailVerify: false, + maxAddressCount: 0, + }, + }); + expect(enableUserRes.ok()).toBe(true); + const user = await createUser(request, workerUrl); userId = user.userId; - const first = await createTestAddress(request, 'user-rate-first-'); - const second = await createTestAddress(request, 'user-rate-second-'); - addresses.push(first, second); - await bindAddress(request, user.jwt, first.jwt); - await bindAddress(request, user.jwt, second.jwt); + const createAddress = async (name: string) => { + const response = await request.post(`${workerUrl}/api/new_address`, { + data: { name, domain: 'test.example.com' }, + }); + expect(response.ok()).toBe(true); + return await response.json() as { + jwt: string; + address_id: number; + }; + }; + const first = await createAddress(`rate-first-${Date.now()}`); + const second = await createAddress(`rate-second-${Date.now()}`); + addressIds.push(first.address_id, second.address_id); + await bindAddress(request, user.jwt, first.jwt, workerUrl); + await bindAddress(request, user.jwt, second.jwt, workerUrl); const requestAccess = async (address: typeof first) => { const accessRes = await request.post( - `${WORKER_URL}/user_api/address/${address.address_id}/request_send_mail_access`, + `${workerUrl}/user_api/address/${address.address_id}/request_send_mail_access`, { headers: { 'x-user-token': user.jwt } }, ); expect(accessRes.ok()).toBe(true); @@ -372,14 +412,10 @@ test.describe('User send mail API', () => { await requestAccess(first); await requestAccess(second); - const reqIp = `198.51.100.${Math.floor(Math.random() * 200) + 1}`; const send = (address: typeof first, sequence: number) => request.post( - `${WORKER_URL}/user_api/address/${address.address_id}/send_mail`, + `${workerUrl}/user_api/address/${address.address_id}/send_mail`, { - headers: { - 'x-user-token': user.jwt, - 'cf-connecting-ip': reqIp, - }, + headers: { 'x-user-token': user.jwt }, data: { to_mail: 'recipient@test.example.com', subject: `Shared rate limit ${sequence} ${Date.now()}`, @@ -395,9 +431,11 @@ test.describe('User send mail API', () => { expect(limitedRes.status()).toBe(429); expect(await limitedRes.text()).toContain('Rate limit exceeded'); } finally { - await Promise.allSettled(addresses.map((address) => deleteAddress(request, address.jwt))); + await Promise.allSettled(addressIds.map((addressId) => ( + request.delete(`${workerUrl}/admin/delete_address/${addressId}`) + ))); if (userId !== undefined) { - await request.delete(`${WORKER_URL}/admin/users/${userId}`); + await request.delete(`${workerUrl}/admin/users/${userId}`); } } });