import { mocked } from 'jest-mock'; import type { INode, IWorkflowCredentials } from 'n8n-workflow'; import * as Db from '@/Db'; import { WorkflowCredentials } from '@/WorkflowCredentials'; // Define a function used to mock the findOneBy function async function mockFind({ id, type, }: { id: string; type: string; }): Promise { // Simple statement that maps a return value based on the `id` parameter if (id === notFoundNode.credentials!.test.id) { return null; } // Otherwise just build some kind of credential object and return it return { [type]: { [id]: { id, name: type, type, nodesAccess: [], data: '', }, }, }; } jest.mock('@/Db', () => { return { collections: { Credentials: { findOneBy: jest.fn(mockFind), }, }, }; }); // Create an array of Nodes with info that pass or fail the checks as required. // DB returns an object of type { [id: string]: ICredentialsEncrypted } but as it isn't checked // All the mock will need to return is some form of Object when successful describe('WorkflowCredentials', () => { afterEach(() => { jest.clearAllMocks(); }); test('Should return an error if any node has no credential ID', async () => { const credentials = noIdNode.credentials!.test; const expectedError = new Error( `Credentials with name "${credentials.name}" for type "test" miss an ID.`, ); await expect(WorkflowCredentials([noIdNode])).rejects.toEqual(expectedError); expect(mocked(Db.collections.Credentials.findOneBy)).toHaveBeenCalledTimes(0); }); test('Should return an error if credentials cannot be found in the DB', async () => { const credentials = notFoundNode.credentials!.test; const expectedError = new Error( `Could not find credentials for type "test" with ID "${credentials.id}".`, ); await expect(WorkflowCredentials([notFoundNode])).rejects.toEqual(expectedError); expect(mocked(Db.collections.Credentials.findOneBy)).toHaveBeenCalledTimes(1); }); test('Should ignore duplicates', async () => { const response = await WorkflowCredentials([validNode, validNode, validNode]); expect(Object.keys(response)).toEqual(['test']); }); test('Should ignore Nodes with no credentials set', async () => { const response = await WorkflowCredentials([validNode, noCredentialsNode]); expect(Object.keys(response)).toEqual(['test']); }); test('Should work for Nodes with multiple credentials', async () => { const response = await WorkflowCredentials([multiCredNode]); expect(Object.keys(response)).toEqual(['mcTest', 'mcTest2']); }); }); const validNode: INode = { id: '1', name: 'Node with Valid Credentials', typeVersion: 1, type: '', position: [0, 0], credentials: { test: { id: 'cred#1', name: 'Test Credentials', }, }, parameters: {}, }; const noIdNode: INode = { id: '2', name: 'Node with no Credential ID', typeVersion: 1, type: '', position: [0, 0], credentials: { test: { id: null, name: 'NOFIND', }, }, parameters: {}, }; const notFoundNode: INode = { id: '3', name: 'Node that will not be found in the DB', typeVersion: 1, type: '', position: [0, 0], credentials: { test: { id: 'noDB', name: 'Not in Database', }, }, parameters: {}, }; const noCredentialsNode: INode = { id: '4', name: 'Node that has no credentials', typeVersion: 1, type: '', position: [0, 0], parameters: {}, }; // Reference this as a DataObject so I can provide a null credential for testing const multiCredNode: INode = { id: '5', name: 'Node that has mutliple credential elements', typeVersion: 1, type: '', position: [0, 0], credentials: { mcTest: { id: 'multicred#1', name: 'First of Multiple Credentials', }, mcTest2: { id: 'multicred#2', name: 'Second of Multiple Credentials', }, }, parameters: {}, };