Skip to content

Commit 4c9198d

Browse files
authored
fix(Microsoft SQL Node): Handle connection errors correctly with continueOnFail (#15962)
1 parent 30d75db commit 4c9198d

File tree

2 files changed

+98
-4
lines changed

2 files changed

+98
-4
lines changed

packages/nodes-base/nodes/Microsoft/Sql/MicrosoftSql.node.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,24 @@ export class MicrosoftSql implements INodeType {
247247
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
248248
const credentials = await this.getCredentials('microsoftSql');
249249

250-
const pool = configurePool(credentials);
251-
await pool.connect();
252-
253250
let responseData: IDataObject | IDataObject[] = [];
254251
let returnData: INodeExecutionData[] = [];
255-
256252
const items = this.getInputData();
253+
const pairedItem = generatePairedItemData(items.length);
254+
255+
const pool = configurePool(credentials);
256+
try {
257+
await pool.connect();
258+
} catch (error) {
259+
void pool.close();
260+
261+
if (this.continueOnFail()) {
262+
return [[{ json: { error: error.message }, pairedItem }]];
263+
} else {
264+
throw error;
265+
}
266+
}
267+
257268
const operation = this.getNodeParameter('operation', 0);
258269
const nodeVersion = this.getNode().typeVersion;
259270

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { mock } from 'jest-mock-extended';
2+
import * as mssql from 'mssql';
3+
import { constructExecutionMetaData, returnJsonArray } from 'n8n-core';
4+
import type { IExecuteFunctions } from 'n8n-workflow';
5+
6+
import { MicrosoftSql } from '../MicrosoftSql.node';
7+
8+
jest.mock('mssql');
9+
10+
function getMockedExecuteFunctions(overrides: Partial<IExecuteFunctions> = {}) {
11+
return mock<IExecuteFunctions>({
12+
getCredentials: jest.fn().mockResolvedValue({
13+
server: 'localhost',
14+
database: 'testdb',
15+
user: 'testuser',
16+
password: 'testpass',
17+
port: 1433,
18+
tls: false,
19+
allowUnauthorizedCerts: true,
20+
tdsVersion: '7_4',
21+
connectTimeout: 1000,
22+
requestTimeout: 10000,
23+
}),
24+
getInputData: jest.fn().mockReturnValue([{ json: {} }]),
25+
getNode: jest.fn().mockReturnValue({ typeVersion: 1 }),
26+
continueOnFail: jest.fn().mockReturnValue(true),
27+
helpers: {
28+
constructExecutionMetaData,
29+
returnJsonArray,
30+
},
31+
evaluateExpression: jest.fn((_val) => _val),
32+
...overrides,
33+
});
34+
}
35+
36+
describe('MicrosoftSql Node', () => {
37+
let mockedConnectionPool: jest.MockedClass<typeof mssql.ConnectionPool>;
38+
39+
beforeEach(() => {
40+
mockedConnectionPool = mssql.ConnectionPool as jest.MockedClass<typeof mssql.ConnectionPool>;
41+
});
42+
43+
test('handles connection error with continueOnFail', async () => {
44+
const fakeError = new Error('Connection failed');
45+
mockedConnectionPool.mockReturnValue(
46+
mock<mssql.ConnectionPool>({
47+
connect: jest.fn().mockRejectedValue(fakeError),
48+
close: jest.fn(),
49+
}),
50+
);
51+
52+
const node = new MicrosoftSql();
53+
const context = getMockedExecuteFunctions();
54+
const result = await node.execute.call(context);
55+
56+
expect(result).toEqual([[{ json: { error: 'Connection failed' }, pairedItem: [{ item: 0 }] }]]);
57+
});
58+
59+
test('executes query on happy path', async () => {
60+
const queryResult = { recordsets: [[{ value: 1 }]] };
61+
const mockRequest = { query: jest.fn().mockResolvedValue(queryResult) };
62+
const mockPool = mock<mssql.ConnectionPool>({
63+
connect: jest.fn().mockResolvedValue(undefined),
64+
close: jest.fn(),
65+
request: jest.fn().mockReturnValue(mockRequest),
66+
});
67+
68+
mockedConnectionPool.mockReturnValue(mockPool);
69+
70+
const node = new MicrosoftSql();
71+
const context = getMockedExecuteFunctions({
72+
getNodeParameter: jest
73+
.fn()
74+
.mockReturnValueOnce('executeQuery')
75+
.mockReturnValueOnce('SELECT 1 AS value'),
76+
});
77+
const result = await node.execute.call(context);
78+
79+
expect(result).toEqual([[{ json: { value: 1 }, pairedItem: [{ item: 0 }] }]]);
80+
expect(mockRequest.query).toHaveBeenCalledWith('SELECT 1 AS value');
81+
expect(mockPool.close).toHaveBeenCalled();
82+
});
83+
});

0 commit comments

Comments
 (0)