From c2622f155111c582346446a274d32bae47b117a3 Mon Sep 17 00:00:00 2001 From: mohammadmk47 Date: Wed, 14 Oct 2026 07:19:19 +0330 Subject: [PATCH 1/2] test: cover email gateway destination configuration --- email-worker/test/policy.test.js | 82 ++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/email-worker/test/policy.test.js b/email-worker/test/policy.test.js index bc052ca..1316591 100644 --- a/email-worker/test/policy.test.js +++ b/email-worker/test/policy.test.js @@ -1,6 +1,6 @@ import test from "node:test"; import assert from "node:assert/strict"; -import { +import emailWorker, { attachmentProblem, csvSet, getLocalPart, @@ -53,13 +53,10 @@ test("rejects attachment count and total size limits", () => { ); assert.equal( - attachmentProblem( - [{ - filename: "large.txt", - content: new Uint8Array(16) - }], - { MAX_TOTAL_ATTACHMENT_BYTES: "10" } - ), + attachmentProblem([{ + filename: "large.txt", + content: new Uint8Array(16) + }], { MAX_TOTAL_ATTACHMENT_BYTES: "10" }), "attachment_size_limit" ); }); @@ -74,3 +71,72 @@ test("allows ordinary small attachments", () => { null ); }); + +function mockMessage(overrides = {}) { + const state = { rejected: null, forwardedTo: null, headers: null }; + const message = { + from: "sender@example.net", + to: "support@voidone.dpdns.org", + canBeForwarded: true, + rawSize: 100, + raw: "From: sender@example.net\r\nTo: support@voidone.dpdns.org\r\nSubject: Test\r\n\r\nHello", + setReject(reason) { + state.rejected = reason; + }, + async forward(destination, headers) { + state.forwardedTo = destination; + state.headers = headers; + }, + ...overrides + }; + return { message, state }; +} + +function mockContext() { + const tasks = []; + return { + tasks, + ctx: { + waitUntil(promise) { + tasks.push(promise); + } + } + }; +} + +test("rejects with destination_not_configured when no inbox is configured", async () => { + const { message, state } = mockMessage(); + const { ctx, tasks } = mockContext(); + + await emailWorker.email(message, {}, ctx); + await Promise.all(tasks); + + assert.equal(state.rejected, "destination_not_configured"); + assert.equal(state.forwardedTo, null); +}); + +test("forwards to GMAIL_INBOX when a route-specific inbox is absent", async () => { + const { message, state } = mockMessage(); + const { ctx, tasks } = mockContext(); + + await emailWorker.email(message, { GMAIL_INBOX: "team@example.net" }, ctx); + await Promise.all(tasks); + + assert.equal(state.rejected, null); + assert.equal(state.forwardedTo, "team@example.net"); + assert.equal(state.headers.get("X-VoidOne-Route"), "support"); +}); + +test("prefers a route-specific inbox over GMAIL_INBOX", async () => { + const { message, state } = mockMessage(); + const { ctx, tasks } = mockContext(); + + await emailWorker.email(message, { + GMAIL_INBOX: "general@example.net", + SUPPORT_INBOX: "support-team@example.net" + }, ctx); + await Promise.all(tasks); + + assert.equal(state.rejected, null); + assert.equal(state.forwardedTo, "support-team@example.net"); +}); From 875844e634c45edee16310f62c18043b62a64c07 Mon Sep 17 00:00:00 2001 From: mohammadmk47 Date: Wed, 14 Oct 2026 07:19:28 +0330 Subject: [PATCH 2/2] docs: explain email gateway destination errors --- email-worker/README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/email-worker/README.md b/email-worker/README.md index 2e92b0a..6d1ca41 100644 --- a/email-worker/README.md +++ b/email-worker/README.md @@ -29,7 +29,7 @@ The workflow can create the Worker via Wrangler, but it cannot configure your pr After the first successful deployment, open Cloudflare Dashboard → Workers & Pages → `voidone-email-gateway` → Settings → Variables and Secrets. Add: -- `GMAIL_INBOX`: your verified forwarding destination. +- `GMAIL_INBOX`: your verified forwarding destination. This is required unless every alias has its own route-specific inbox configured. - Optional `SUPPORT_INBOX`, `BUGS_INBOX`, `CONTACT_INBOX`, `INFO_INBOX`, `SECURITY_INBOX`: separate verified destinations. If omitted, the main inbox is used. - Optional `BLOCKED_SENDERS`: comma-separated exact email addresses. - Optional `BLOCKED_DOMAINS`: comma-separated domains. @@ -42,6 +42,12 @@ After the first successful deployment, open Cloudflare Dashboard → Workers & P Variables are kept across Wrangler deployments. Secrets should be added using Cloudflare's secret manager. +## Troubleshooting + +- **Activity log says `destination_not_configured`:** the Worker received the message, but it found no forwarding address. Add `GMAIL_INBOX` with the verified destination address, or configure the matching route-specific variable (for example, `SUPPORT_INBOX` for `support@voidone.dpdns.org`). Save the setting and retry with a new test email. +- **Activity log says `forward_failed`:** a destination was configured, but Cloudflare failed to forward to it. Check that the address is verified in Email Routing and inspect the Worker logs for the underlying error. +- **No Worker event appears:** confirm the Email Routing rule's action is **Send to a Worker** and the selected Worker is `voidone-email-gateway`. + ## Optional persistent audit log (KV) Without KV, structured logs are available through Workers Observability and are subject to Cloudflare's log availability/retention settings. @@ -56,7 +62,7 @@ The Worker writes only metadata to KV and sets a 90-day TTL. Restrict access to ## Email Routing setup -1. Deploy the Worker and configure `GMAIL_INBOX` first. +1. Deploy the Worker and configure a forwarding destination first. 2. In Cloudflare Email Routing, create a rule for each supported address and select **Send to a Worker** → `voidone-email-gateway`. 3. Verify the destination address. 4. Ensure Cloudflare Email Routing's MX records are active. Do not leave conflicting InstantNode and Cloudflare Email Routing MX configurations in place. @@ -73,4 +79,4 @@ npm install npm test ``` -Tests cover routing and policy helpers, not Cloudflare's live email infrastructure. Complete a real end-to-end test only after configuring the Worker and Email Routing. +Tests cover routing and policy helpers plus the handler's configured/missing-destination behavior. They do not validate Cloudflare's live email infrastructure. Complete a real end-to-end test only after configuring the Worker and Email Routing.