diff options
author | Elizabeth Hunt <elizabeth.hunt@simponic.xyz> | 2024-12-14 23:53:26 -0800 |
---|---|---|
committer | Elizabeth Hunt <elizabeth.hunt@simponic.xyz> | 2024-12-14 23:55:51 -0800 |
commit | 4fd40b1f9de400a5d859789e1dad3e1a4ba6587c (patch) | |
tree | 74fbae949aa3fb9711c06e31cb6649e90a8cdb97 /tst/email.spec.ts | |
download | uptime-4fd40b1f9de400a5d859789e1dad3e1a4ba6587c.tar.gz uptime-4fd40b1f9de400a5d859789e1dad3e1a4ba6587c.zip |
initial commit
Diffstat (limited to 'tst/email.spec.ts')
-rw-r--r-- | tst/email.spec.ts | 153 |
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()), + )(); +}); |