refactor: split src into domain / application / infrastructure layers
Replace the history-driven lib/ + utils/ split with DDD layers: - domain/: aggregate, repositories, value objects, pure parsers/format - application/: feed-service, email-processor, feed-fetcher, stats - infrastructure/: logging, auth, KV/R2 adapters, HTTP, framework glue Pure file relocation; imports updated mechanically. Behaviour unchanged. Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
1 parent
ab1c15e69a
commit
7bf0f71f86
45 files changed
+90
-68
No files matched your search
@@ -0,0 +1,126 @@
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
import "../test/setup";
|
||||
import { createMockEnv } from "../test/setup";
|
||||
import { handleCloudflareEmail } from "./cloudflare-email";
|
||||
|
||||
const VALID_FEED_ID = "apple.mountain.42";
|
||||
const DOMAIN = "test.getmynews.app";
|
||||
|
||||
const RAW_EMAIL = [
|
||||
"From: Sender Name <[email protected]>",
|
||||
`To: ${VALID_FEED_ID}@${DOMAIN}`,
|
||||
"Subject: Hello World",
|
||||
"Date: Thu, 01 Jan 2026 12:00:00 +0000",
|
||||
"MIME-Version: 1.0",
|
||||
"Content-Type: text/plain; charset=utf-8",
|
||||
"",
|
||||
"This is the email body.",
|
||||
].join("\r\n");
|
||||
|
||||
function makeMessage(
|
||||
overrides: Partial<{ from: string; to: string; rawText: string }> = {},
|
||||
): ForwardableEmailMessage {
|
||||
const rawText = overrides.rawText ?? RAW_EMAIL;
|
||||
const encoder = new TextEncoder();
|
||||
const bytes = encoder.encode(rawText);
|
||||
const stream = new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(bytes);
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
from: overrides.from ?? "[email protected]",
|
||||
to: overrides.to ?? `${VALID_FEED_ID}@${DOMAIN}`,
|
||||
headers: new Headers(),
|
||||
raw: stream,
|
||||
rawSize: bytes.length,
|
||||
forward: async () => {},
|
||||
reply: async () => {},
|
||||
setReject: () => {},
|
||||
} as unknown as ForwardableEmailMessage;
|
||||
}
|
||||
|
||||
describe("handleCloudflareEmail", () => {
|
||||
let env: ReturnType<typeof createMockEnv>;
|
||||
|
||||
beforeEach(() => {
|
||||
env = createMockEnv();
|
||||
});
|
||||
|
||||
it("stores email in KV when feed exists", async () => {
|
||||
await env.EMAIL_STORAGE.put(
|
||||
`feed:${VALID_FEED_ID}:config`,
|
||||
JSON.stringify({}),
|
||||
);
|
||||
|
||||
await handleCloudflareEmail(
|
||||
makeMessage(),
|
||||
env as any,
|
||||
{ waitUntil: () => {} } as any,
|
||||
);
|
||||
|
||||
const metadata = await env.EMAIL_STORAGE.get(
|
||||
`feed:${VALID_FEED_ID}:metadata`,
|
||||
"json",
|
||||
);
|
||||
expect(metadata.emails).toHaveLength(1);
|
||||
expect(metadata.emails[0].subject).toBe("Hello World");
|
||||
});
|
||||
|
||||
it("does not throw when feed does not exist", async () => {
|
||||
await expect(
|
||||
handleCloudflareEmail(
|
||||
makeMessage(),
|
||||
env as any,
|
||||
{ waitUntil: () => {} } as any,
|
||||
),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not throw when email is malformed", async () => {
|
||||
const msg = makeMessage({ rawText: "not a valid email" });
|
||||
await expect(
|
||||
handleCloudflareEmail(msg, env as any, { waitUntil: () => {} } as any),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("uses sender from message.from for allowlist check", async () => {
|
||||
await env.EMAIL_STORAGE.put(
|
||||
`feed:${VALID_FEED_ID}:config`,
|
||||
JSON.stringify({ allowed_senders: ["[email protected]"] }),
|
||||
);
|
||||
|
||||
await handleCloudflareEmail(
|
||||
makeMessage(),
|
||||
env as any,
|
||||
{ waitUntil: () => {} } as any,
|
||||
);
|
||||
|
||||
const metadata = await env.EMAIL_STORAGE.get(
|
||||
`feed:${VALID_FEED_ID}:metadata`,
|
||||
"json",
|
||||
);
|
||||
expect(metadata.emails).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("rejects email when sender is not in allowlist (stored nothing)", async () => {
|
||||
await env.EMAIL_STORAGE.put(
|
||||
`feed:${VALID_FEED_ID}:config`,
|
||||
JSON.stringify({ allowed_senders: ["[email protected]"] }),
|
||||
);
|
||||
|
||||
await handleCloudflareEmail(
|
||||
makeMessage(),
|
||||
env as any,
|
||||
{ waitUntil: () => {} } as any,
|
||||
);
|
||||
|
||||
const metadata = await env.EMAIL_STORAGE.get(
|
||||
`feed:${VALID_FEED_ID}:metadata`,
|
||||
"json",
|
||||
);
|
||||
expect(metadata).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user