summaryrefslogtreecommitdiff
path: root/tst/email.spec.ts
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-12-14 23:53:26 -0800
committerElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-12-14 23:55:51 -0800
commit4fd40b1f9de400a5d859789e1dad3e1a4ba6587c (patch)
tree74fbae949aa3fb9711c06e31cb6649e90a8cdb97 /tst/email.spec.ts
downloaduptime-4fd40b1f9de400a5d859789e1dad3e1a4ba6587c.tar.gz
uptime-4fd40b1f9de400a5d859789e1dad3e1a4ba6587c.zip
initial commit
Diffstat (limited to 'tst/email.spec.ts')
-rw-r--r--tst/email.spec.ts153
1 files changed, 153 insertions, 0 deletions
diff --git a/tst/email.spec.ts b/tst/email.spec.ts
new file mode 100644
index 0000000..5f2aa90
--- /dev/null
+++ b/tst/email.spec.ts
@@ -0,0 +1,153 @@
+import { mock, test, expect } from "bun:test";
+import * as TE from "fp-ts/lib/TaskEither";
+
+import { constVoid, pipe } from "fp-ts/lib/function";
+import type { EmailFromInstruction, EmailToInstruction } from "../src/job";
+import { perform, type EmailJobDependencies } from "../src/email";
+
+const from: EmailFromInstruction = {
+ send_port: 465,
+ email: "test@localhost",
+ username: "test",
+ password: "password",
+ server: "localhost",
+};
+
+const to: EmailToInstruction = {
+ read_port: 993,
+ email: "test@localhost",
+ username: "test",
+ password: "password",
+ server: "localhost",
+};
+
+const getMocks = () => {
+ const lock = {
+ path: "INBOX",
+ release: mock(() => constVoid()),
+ };
+ const imap = {
+ fetchAll: mock(() => Promise.resolve([])),
+ connect: mock(() => Promise.resolve()),
+ getMailboxLock: mock(() => Promise.resolve(lock)),
+ messageDelete: mock(() => Promise.resolve(true)),
+ close: mock(() => constVoid()),
+ };
+
+ const mockDependencies: Partial<EmailJobDependencies> = {
+ getImapImpl: () => TE.right(imap),
+ getSendImpl: mock(() => (email: any) => TE.right(email)),
+ matchesEmailImpl: mock(() => () => true),
+ };
+
+ return { lock, imap, mockDependencies };
+};
+
+test("retries until message is in inbox", async () => {
+ const { imap, mockDependencies } = getMocks();
+
+ const retry = { retries: 3, interval: 400 };
+ const emailJob = { from, to, readRetry: retry };
+
+ let attempts = 0;
+ const messageInInbox = { uid: 1 } as any;
+ imap.fetchAll = mock(() => {
+ attempts++;
+ if (attempts === 3) {
+ return Promise.resolve([messageInInbox] as any);
+ }
+ return Promise.resolve([]);
+ });
+ mockDependencies.matchesEmailImpl = mock(
+ (_: any) => (message: any) => message.uid === 1,
+ );
+
+ await pipe(
+ perform(emailJob, mockDependencies),
+ TE.map((x) => {
+ expect(x).toBeTruthy();
+ expect(attempts).toBe(3);
+ }),
+ TE.mapLeft(() => expect(false).toBeTruthy()),
+ )();
+});
+
+test("failure to send message goes left", async () => {
+ const { mockDependencies } = getMocks();
+
+ const emailJob = { from, to, readRetry: { retries: 1, interval: 1 } };
+ mockDependencies.getSendImpl = mock(() => () => TE.left(new Error("fail")));
+
+ await pipe(
+ perform(emailJob, mockDependencies),
+ TE.map(() => expect(false).toBeTruthy()),
+ TE.mapLeft((e) => {
+ expect(e.message).toBe("fail");
+ }),
+ )();
+});
+
+test("goes left when message not ever received", async () => {
+ const { imap, mockDependencies } = getMocks();
+
+ const emailJob = { from, to, readRetry: { retries: 3, interval: 1 } };
+ imap.fetchAll = mock(() => Promise.resolve([]));
+
+ expect(
+ await pipe(
+ perform(emailJob, mockDependencies),
+ TE.map(() => expect(false).toBeTruthy()),
+ TE.mapLeft((e) => {
+ expect(e.message).toBe("Email message not found");
+ }),
+ )(),
+ );
+});
+
+test("releases lock on left", async () => {
+ const { lock, imap, mockDependencies } = getMocks();
+
+ const emailJob = { from, to, readRetry: { retries: 1, interval: 1 } };
+ imap.fetchAll = mock(() => Promise.resolve([]));
+
+ await pipe(
+ perform(emailJob, mockDependencies),
+ TE.map(() => expect(false).toBeTruthy()),
+ TE.mapLeft(() => {
+ expect(imap.getMailboxLock).toHaveBeenCalledTimes(1);
+ expect(lock.release).toHaveBeenCalledTimes(1);
+ }),
+ )();
+});
+
+test("releases lock on right", async () => {
+ const { lock, imap, mockDependencies } = getMocks();
+
+ const emailJob = { from, to, readRetry: { retries: 1, interval: 1 } };
+ mockDependencies.findEmailUidInInboxImpl = () => TE.right(1);
+
+ await pipe(
+ perform(emailJob, mockDependencies),
+ TE.map(() => {
+ expect(imap.getMailboxLock).toHaveBeenCalledTimes(1);
+ expect(lock.release).toHaveBeenCalledTimes(1);
+ }),
+ TE.mapLeft(() => expect(false).toBeTruthy()),
+ )();
+});
+
+test("cleans up sent messages from inbox", async () => {
+ const { imap, mockDependencies } = getMocks();
+
+ const emailJob = { from, to, readRetry: { retries: 1, interval: 1 } };
+ mockDependencies.findEmailUidInInboxImpl = () => TE.right(1);
+
+ await pipe(
+ perform(emailJob, mockDependencies),
+ TE.map(() => {
+ expect(imap.messageDelete).toHaveBeenCalledTimes(1);
+ expect(imap.messageDelete).toHaveBeenCalledWith([1]);
+ }),
+ TE.mapLeft(() => expect(false).toBeTruthy()),
+ )();
+});