feat(stats): count emails forwarded to the catch-all fallback

Adds an emails_forwarded counter (a subset of emails_rejected) bumped on a
successful FALLBACK_FORWARD_ADDRESS forward. Dropped = rejected − forwarded.
Surfaced in the /api/v1/stats response and the public status page.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
Julien HerrandClaude Opus 4.7 committed 2026-05-24 17:19:12 +02:00
1 parent 1583e95875
commit 81e46c9026
7 files changed
+64

No files matched your search

@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach } from "vitest";
import "../test/setup";
import { createMockEnv } from "../test/setup";
import { handleCloudflareEmail } from "./cloudflare-email";
import { getCounters } from "../application/stats";
const VALID_FEED_ID = "apple.mountain.42";
const DOMAIN = "test.getmynews.app";
@@ -245,5 +246,53 @@ describe("handleCloudflareEmail", () => {
),
).resolves.toBeUndefined();
});
it("increments the emails_forwarded counter on a successful forward", async () => {
const { forward } = spyForward();
env.FALLBACK_FORWARD_ADDRESS = FALLBACK;
await handleCloudflareEmail(
makeMessage({ forward }),
env as any,
{ waitUntil: () => {} } as any,
);
const counters = await getCounters(env.EMAIL_STORAGE as any);
expect(counters.emails_forwarded).toBe(1);
});
it("does not increment emails_forwarded when the forward fails", async () => {
env.FALLBACK_FORWARD_ADDRESS = FALLBACK;
const forward = async () => {
throw new Error("destination address not verified");
};
await handleCloudflareEmail(
makeMessage({ forward }),
env as any,
{ waitUntil: () => {} } as any,
);
const counters = await getCounters(env.EMAIL_STORAGE as any);
expect(counters.emails_forwarded).toBe(0);
});
it("does not increment emails_forwarded for dropped reasons", async () => {
const { forward } = spyForward();
env.FALLBACK_FORWARD_ADDRESS = FALLBACK;
await env.EMAIL_STORAGE.put(
`feed:${VALID_FEED_ID}:config`,
JSON.stringify({ expires_at: Date.now() - 1000 }),
);
await handleCloudflareEmail(
makeMessage({ forward }),
env as any,
{ waitUntil: () => {} } as any,
);
const counters = await getCounters(env.EMAIL_STORAGE as any);
expect(counters.emails_forwarded).toBe(0);
});
});
});