From a6719b8db71802ee6818f87266558417e302d11f Mon Sep 17 00:00:00 2001 From: Bernhard Wittmann Date: Fri, 16 Oct 2026 13:10:25 +0000 Subject: [PATCH] fix(Email Trigger (IMAP) Node): Check the mailbox when the trigger activates (#41013) --- .../v2/EmailReadImapV2.node.test.ts | 62 ++++++++++++++----- .../EmailReadImap/v2/EmailReadImapV2.node.ts | 3 +- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.test.ts b/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.test.ts index bf12beaf6ca..03dd20e01fa 100644 --- a/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.test.ts +++ b/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.test.ts @@ -303,25 +303,53 @@ describe('EmailReadImapV2', () => { }); }); - describe('catching up after a gap', () => { - it('looks at the mailbox when it has processed mail before', async () => { - staticData.lastMessageUid = 42; - const connection = createConnection(); - connectMock.mockResolvedValueOnce(connection); + describe('catching up on activation', () => { + // Mail that landed while the workflow was inactive sends no arrival event. + it.each([ + { + case: 'mail processed before', + typeVersion: 2.1, + lastMessageUid: 42, + options: {}, + criteria: ['UNSEEN', ['UID', '42:*']], + }, + { + case: 'a first activation', + typeVersion: 2.1, + lastMessageUid: undefined, + options: {}, + criteria: ['UNSEEN', ['SINCE', '13-Oct-2026']], + }, + { + case: 'an untracked last message id', + typeVersion: 2.1, + lastMessageUid: 42, + options: { trackLastMessageId: false }, + criteria: ['UNSEEN'], + }, + { + case: 'version 2', + typeVersion: 2, + lastMessageUid: 42, + options: {}, + criteria: ['UNSEEN'], + }, + ])( + 'searches the mailbox after $case', + async ({ typeVersion, lastMessageUid, options, criteria }) => { + vi.setSystemTime(new Date('2026-10-13T12:00:00Z')); + triggerFunctions.getNode.mockReturnValue(mock({ typeVersion })); + staticData.lastMessageUid = lastMessageUid; + const connection = createConnection(); + connectMock.mockResolvedValueOnce(connection); - await startTrigger(); + await startTrigger(options); + expect(connection.catchUp).toHaveBeenCalledTimes(1); + await arrive(connection, 'unknown'); - expect(connection.catchUp).toHaveBeenCalledTimes(1); - }); - - it('leaves the mailbox alone on a first activation', async () => { - const connection = createConnection(); - connectMock.mockResolvedValueOnce(connection); - - await startTrigger(); - - expect(connection.catchUp).not.toHaveBeenCalled(); - }); + expect(fetched).toHaveBeenCalledWith(expect.objectContaining({ searchCriteria: criteria })); + }, + ); }); describe('credential test', () => { diff --git a/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts b/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts index ec0b33466d7..fc6475f969f 100644 --- a/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts +++ b/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts @@ -342,7 +342,8 @@ export class EmailReadImapV2 implements INodeType { connection.onArrival(async ({ count }) => await fetchNewEmails(connection, count)); - if (staticData.lastMessageUid !== undefined) connection.catchUp(); + // Mail that landed while the trigger was inactive sends no arrival event. + connection.catchUp(); connection.onFlags((info) => { this.logger.debug(`Email Read Imap:update ${info.seq}`, { uid: info.uid });