diff --git a/packages/nodes-base/nodes/Microsoft/Sql/MicrosoftSql.node.ts b/packages/nodes-base/nodes/Microsoft/Sql/MicrosoftSql.node.ts index e1056ed5298..0290b643b2a 100644 --- a/packages/nodes-base/nodes/Microsoft/Sql/MicrosoftSql.node.ts +++ b/packages/nodes-base/nodes/Microsoft/Sql/MicrosoftSql.node.ts @@ -247,13 +247,24 @@ export class MicrosoftSql implements INodeType { async execute(this: IExecuteFunctions): Promise { const credentials = await this.getCredentials('microsoftSql'); - const pool = configurePool(credentials); - await pool.connect(); - let responseData: IDataObject | IDataObject[] = []; let returnData: INodeExecutionData[] = []; - const items = this.getInputData(); + const pairedItem = generatePairedItemData(items.length); + + const pool = configurePool(credentials); + try { + await pool.connect(); + } catch (error) { + void pool.close(); + + if (this.continueOnFail()) { + return [[{ json: { error: error.message }, pairedItem }]]; + } else { + throw error; + } + } + const operation = this.getNodeParameter('operation', 0); const nodeVersion = this.getNode().typeVersion; diff --git a/packages/nodes-base/nodes/Microsoft/Sql/test/MicrosoftSql.node.test.ts b/packages/nodes-base/nodes/Microsoft/Sql/test/MicrosoftSql.node.test.ts new file mode 100644 index 00000000000..f07f1eb7798 --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Sql/test/MicrosoftSql.node.test.ts @@ -0,0 +1,83 @@ +import { mock } from 'jest-mock-extended'; +import * as mssql from 'mssql'; +import { constructExecutionMetaData, returnJsonArray } from 'n8n-core'; +import type { IExecuteFunctions } from 'n8n-workflow'; + +import { MicrosoftSql } from '../MicrosoftSql.node'; + +jest.mock('mssql'); + +function getMockedExecuteFunctions(overrides: Partial = {}) { + return mock({ + getCredentials: jest.fn().mockResolvedValue({ + server: 'localhost', + database: 'testdb', + user: 'testuser', + password: 'testpass', + port: 1433, + tls: false, + allowUnauthorizedCerts: true, + tdsVersion: '7_4', + connectTimeout: 1000, + requestTimeout: 10000, + }), + getInputData: jest.fn().mockReturnValue([{ json: {} }]), + getNode: jest.fn().mockReturnValue({ typeVersion: 1 }), + continueOnFail: jest.fn().mockReturnValue(true), + helpers: { + constructExecutionMetaData, + returnJsonArray, + }, + evaluateExpression: jest.fn((_val) => _val), + ...overrides, + }); +} + +describe('MicrosoftSql Node', () => { + let mockedConnectionPool: jest.MockedClass; + + beforeEach(() => { + mockedConnectionPool = mssql.ConnectionPool as jest.MockedClass; + }); + + test('handles connection error with continueOnFail', async () => { + const fakeError = new Error('Connection failed'); + mockedConnectionPool.mockReturnValue( + mock({ + connect: jest.fn().mockRejectedValue(fakeError), + close: jest.fn(), + }), + ); + + const node = new MicrosoftSql(); + const context = getMockedExecuteFunctions(); + const result = await node.execute.call(context); + + expect(result).toEqual([[{ json: { error: 'Connection failed' }, pairedItem: [{ item: 0 }] }]]); + }); + + test('executes query on happy path', async () => { + const queryResult = { recordsets: [[{ value: 1 }]] }; + const mockRequest = { query: jest.fn().mockResolvedValue(queryResult) }; + const mockPool = mock({ + connect: jest.fn().mockResolvedValue(undefined), + close: jest.fn(), + request: jest.fn().mockReturnValue(mockRequest), + }); + + mockedConnectionPool.mockReturnValue(mockPool); + + const node = new MicrosoftSql(); + const context = getMockedExecuteFunctions({ + getNodeParameter: jest + .fn() + .mockReturnValueOnce('executeQuery') + .mockReturnValueOnce('SELECT 1 AS value'), + }); + const result = await node.execute.call(context); + + expect(result).toEqual([[{ json: { value: 1 }, pairedItem: [{ item: 0 }] }]]); + expect(mockRequest.query).toHaveBeenCalledWith('SELECT 1 AS value'); + expect(mockPool.close).toHaveBeenCalled(); + }); +});