From ef1e3e2d4042b2397e8df81e6d196e8de7ef3e02 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sun, 15 Dec 2024 01:57:28 -0800 Subject: [PATCH] logout on end --- src/email.ts | 24 ++++++++++++++++-------- tst/email.spec.ts | 4 +++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/email.ts b/src/email.ts index 6ad05a0..906b86d 100644 --- a/src/email.ts +++ b/src/email.ts @@ -14,7 +14,7 @@ interface ImapClientI { connect: () => Promise; getMailboxLock: (mailbox: string) => Promise; messageDelete: (uids: number[], opts: Record) => Promise; - close: () => void; + logout: () => Promise; } type Email = { @@ -26,13 +26,16 @@ type Email = { class ErrorWithLock extends Error { lock: O.Option; - constructor(message: string, lock?: MailboxLockObject) { + imap: O.Option; + constructor(message: string, lock?: MailboxLockObject, imap?: ImapClientI) { super(message); this.lock = O.fromNullable(lock); + this.imap = O.fromNullable(imap); } } -const ToErrorWithLock = (lock?: MailboxLockObject) => (error: unknown) => - new ErrorWithLock(error instanceof Error ? error.message : "Unknown error", lock); + +const ToErrorWithLock = (lock?: MailboxLockObject, imap?: ImapClientI) => (error: unknown) => + new ErrorWithLock(error instanceof Error ? error.message : "Unknown error", lock, imap); /** * Generate a unique email. @@ -203,19 +206,19 @@ export const perform = ( // act. TE.tap(({ email }) => pipe(getSendImpl(from)(email), TE.mapLeft(ToErrorWithLock()))), TE.bind("imap", () => pipe(getImapImpl(to), TE.mapLeft(ToErrorWithLock()))), - TE.bind("mailboxLock", ({ imap }) => TE.tryCatch(() => imap.getMailboxLock("INBOX"), ToErrorWithLock())), + TE.bind("mailboxLock", ({ imap }) => TE.tryCatch(() => imap.getMailboxLock("INBOX"), ToErrorWithLock(undefined, imap))), // "assert". TE.bind("uid", ({ imap, email, mailboxLock }) => pipe( findEmailUidInInboxImpl(imap, matchesEmailImpl(email), retries, interval), - TE.mapLeft(ToErrorWithLock(mailboxLock)) + TE.mapLeft(ToErrorWithLock(mailboxLock, imap)) ) ), // cleanup. TE.bind("deleted", ({ imap, uid, mailboxLock }) => TE.tryCatch( () => imap.messageDelete([uid], { uid: true }), - ToErrorWithLock(mailboxLock), + ToErrorWithLock(mailboxLock, imap), ), ), TE.fold( @@ -223,10 +226,15 @@ export const perform = ( if (O.isSome(e.lock)) { e.lock.value.release(); } + if (O.isSome(e.imap)) { + const imap = e.imap.value; + return pipe(TE.tryCatch(() => imap.logout(), toError), TE.flatMap(() => TE.left(e))); + } return TE.left(e); }, - ({ mailboxLock, deleted }) => { + ({ mailboxLock, deleted, imap }) => { mailboxLock.release(); + imap.logout(); return TE.right(deleted); } ) diff --git a/tst/email.spec.ts b/tst/email.spec.ts index 7315816..e966030 100644 --- a/tst/email.spec.ts +++ b/tst/email.spec.ts @@ -31,7 +31,7 @@ const getMocks = () => { connect: mock(() => Promise.resolve()), getMailboxLock: mock(() => Promise.resolve(lock)), messageDelete: mock(() => Promise.resolve(true)), - close: mock(() => constVoid()), + logout: mock(() => Promise.resolve()), }; const mockDependencies: Partial = { @@ -116,6 +116,7 @@ test("releases lock on left", async () => { TE.mapLeft(() => { expect(imap.getMailboxLock).toHaveBeenCalledTimes(1); expect(lock.release).toHaveBeenCalledTimes(1); + expect(imap.logout).toHaveBeenCalledTimes(1); }), )(); }); @@ -131,6 +132,7 @@ test("releases lock on right", async () => { TE.map(() => { expect(imap.getMailboxLock).toHaveBeenCalledTimes(1); expect(lock.release).toHaveBeenCalledTimes(1); + expect(imap.logout).toHaveBeenCalledTimes(1); }), TE.mapLeft(() => expect(false).toBeTruthy()), )();