-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(HTTP Request Tool Node): Fix the undefined response issue when au…
…thentication is enabled (#11343) Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <[email protected]>
- Loading branch information
1 parent
cade9b2
commit 094ec68
Showing
3 changed files
with
183 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
287 changes: 181 additions & 106 deletions
287
packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/test/ToolHttpRequest.node.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,165 +1,240 @@ | ||
import get from 'lodash/get'; | ||
import type { IDataObject, IExecuteFunctions } from 'n8n-workflow'; | ||
import { mock } from 'jest-mock-extended'; | ||
import type { IExecuteFunctions, INode } from 'n8n-workflow'; | ||
import { jsonParse } from 'n8n-workflow'; | ||
|
||
import type { N8nTool } from '../../../../utils/N8nTool'; | ||
import { ToolHttpRequest } from '../ToolHttpRequest.node'; | ||
|
||
const createExecuteFunctionsMock = (parameters: IDataObject, requestMock: any) => { | ||
const nodeParameters = parameters; | ||
|
||
return { | ||
getNodeParameter(parameter: string) { | ||
return get(nodeParameters, parameter); | ||
}, | ||
getNode() { | ||
return { | ||
name: 'HTTP Request', | ||
}; | ||
}, | ||
getInputData() { | ||
return [{ json: {} }]; | ||
}, | ||
getWorkflow() { | ||
return { | ||
name: 'Test Workflow', | ||
}; | ||
}, | ||
continueOnFail() { | ||
return false; | ||
}, | ||
addInputData() { | ||
return { index: 0 }; | ||
}, | ||
addOutputData() { | ||
return; | ||
}, | ||
helpers: { | ||
httpRequest: requestMock, | ||
}, | ||
} as unknown as IExecuteFunctions; | ||
}; | ||
|
||
describe('ToolHttpRequest', () => { | ||
let httpTool: ToolHttpRequest; | ||
let mockRequest: jest.Mock; | ||
const httpTool = new ToolHttpRequest(); | ||
const helpers = mock<IExecuteFunctions['helpers']>(); | ||
const executeFunctions = mock<IExecuteFunctions>({ helpers }); | ||
|
||
describe('Binary response', () => { | ||
beforeEach(() => { | ||
httpTool = new ToolHttpRequest(); | ||
mockRequest = jest.fn(); | ||
jest.resetAllMocks(); | ||
executeFunctions.getNode.mockReturnValue( | ||
mock<INode>({ | ||
type: 'n8n-nodes-base.httpRequest', | ||
name: 'HTTP Request', | ||
typeVersion: 1.1, | ||
}), | ||
); | ||
executeFunctions.addInputData.mockReturnValue({ index: 0 }); | ||
}); | ||
|
||
it('should return the error when receiving a binary response', async () => { | ||
mockRequest.mockResolvedValue({ | ||
helpers.httpRequest.mockResolvedValue({ | ||
body: Buffer.from(''), | ||
headers: { | ||
'content-type': 'image/jpeg', | ||
}, | ||
}); | ||
|
||
const { response } = await httpTool.supplyData.call( | ||
createExecuteFunctionsMock( | ||
{ | ||
method: 'GET', | ||
url: 'https://httpbin.org/image/jpeg', | ||
options: {}, | ||
placeholderDefinitions: { | ||
values: [], | ||
}, | ||
}, | ||
mockRequest, | ||
), | ||
0, | ||
); | ||
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'method': | ||
return 'GET'; | ||
case 'url': | ||
return 'https://httpbin.org/image/jpeg'; | ||
case 'options': | ||
return {}; | ||
case 'placeholderDefinitions.values': | ||
return []; | ||
default: | ||
return undefined; | ||
} | ||
}); | ||
|
||
const res = await (response as N8nTool).invoke(''); | ||
const { response } = await httpTool.supplyData.call(executeFunctions, 0); | ||
|
||
const res = await (response as N8nTool).invoke({}); | ||
expect(helpers.httpRequest).toHaveBeenCalled(); | ||
expect(res).toContain('error'); | ||
expect(res).toContain('Binary data is not supported'); | ||
}); | ||
|
||
it('should return the response text when receiving a text response', async () => { | ||
mockRequest.mockResolvedValue({ | ||
helpers.httpRequest.mockResolvedValue({ | ||
body: 'Hello World', | ||
headers: { | ||
'content-type': 'text/plain', | ||
}, | ||
}); | ||
|
||
const { response } = await httpTool.supplyData.call( | ||
createExecuteFunctionsMock( | ||
{ | ||
method: 'GET', | ||
url: 'https://httpbin.org/text/plain', | ||
options: {}, | ||
placeholderDefinitions: { | ||
values: [], | ||
}, | ||
}, | ||
mockRequest, | ||
), | ||
0, | ||
); | ||
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'method': | ||
return 'GET'; | ||
case 'url': | ||
return 'https://httpbin.org/text/plain'; | ||
case 'options': | ||
return {}; | ||
case 'placeholderDefinitions.values': | ||
return []; | ||
default: | ||
return undefined; | ||
} | ||
}); | ||
|
||
const res = await (response as N8nTool).invoke(''); | ||
const { response } = await httpTool.supplyData.call(executeFunctions, 0); | ||
|
||
const res = await (response as N8nTool).invoke({}); | ||
expect(helpers.httpRequest).toHaveBeenCalled(); | ||
expect(res).toEqual('Hello World'); | ||
}); | ||
|
||
it('should return the response text when receiving a text response with a charset', async () => { | ||
mockRequest.mockResolvedValue({ | ||
helpers.httpRequest.mockResolvedValue({ | ||
body: 'こんにちは世界', | ||
headers: { | ||
'content-type': 'text/plain; charset=iso-2022-jp', | ||
}, | ||
}); | ||
|
||
const { response } = await httpTool.supplyData.call( | ||
createExecuteFunctionsMock( | ||
{ | ||
method: 'GET', | ||
url: 'https://httpbin.org/text/plain', | ||
options: {}, | ||
placeholderDefinitions: { | ||
values: [], | ||
}, | ||
}, | ||
mockRequest, | ||
), | ||
0, | ||
); | ||
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'method': | ||
return 'GET'; | ||
case 'url': | ||
return 'https://httpbin.org/text/plain'; | ||
case 'options': | ||
return {}; | ||
case 'placeholderDefinitions.values': | ||
return []; | ||
default: | ||
return undefined; | ||
} | ||
}); | ||
|
||
const res = await (response as N8nTool).invoke(''); | ||
const { response } = await httpTool.supplyData.call(executeFunctions, 0); | ||
|
||
const res = await (response as N8nTool).invoke({}); | ||
expect(helpers.httpRequest).toHaveBeenCalled(); | ||
expect(res).toEqual('こんにちは世界'); | ||
}); | ||
|
||
it('should return the response object when receiving a JSON response', async () => { | ||
const mockJson = { hello: 'world' }; | ||
|
||
mockRequest.mockResolvedValue({ | ||
body: mockJson, | ||
helpers.httpRequest.mockResolvedValue({ | ||
body: JSON.stringify(mockJson), | ||
headers: { | ||
'content-type': 'application/json', | ||
}, | ||
}); | ||
|
||
const { response } = await httpTool.supplyData.call( | ||
createExecuteFunctionsMock( | ||
{ | ||
method: 'GET', | ||
url: 'https://httpbin.org/json', | ||
options: {}, | ||
placeholderDefinitions: { | ||
values: [], | ||
}, | ||
}, | ||
mockRequest, | ||
), | ||
0, | ||
); | ||
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'method': | ||
return 'GET'; | ||
case 'url': | ||
return 'https://httpbin.org/json'; | ||
case 'options': | ||
return {}; | ||
case 'placeholderDefinitions.values': | ||
return []; | ||
default: | ||
return undefined; | ||
} | ||
}); | ||
|
||
const res = await (response as N8nTool).invoke(''); | ||
const { response } = await httpTool.supplyData.call(executeFunctions, 0); | ||
|
||
const res = await (response as N8nTool).invoke({}); | ||
expect(helpers.httpRequest).toHaveBeenCalled(); | ||
expect(jsonParse(res)).toEqual(mockJson); | ||
}); | ||
|
||
it('should handle authentication with predefined credentials', async () => { | ||
helpers.httpRequestWithAuthentication.mockResolvedValue({ | ||
body: 'Hello World', | ||
headers: { | ||
'content-type': 'text/plain', | ||
}, | ||
}); | ||
|
||
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'method': | ||
return 'GET'; | ||
case 'url': | ||
return 'https://httpbin.org/text/plain'; | ||
case 'authentication': | ||
return 'predefinedCredentialType'; | ||
case 'nodeCredentialType': | ||
return 'linearApi'; | ||
case 'options': | ||
return {}; | ||
case 'placeholderDefinitions.values': | ||
return []; | ||
default: | ||
return undefined; | ||
} | ||
}); | ||
|
||
const { response } = await httpTool.supplyData.call(executeFunctions, 0); | ||
|
||
const res = await (response as N8nTool).invoke({}); | ||
|
||
expect(res).toEqual('Hello World'); | ||
|
||
expect(helpers.httpRequestWithAuthentication).toHaveBeenCalledWith( | ||
'linearApi', | ||
expect.objectContaining({ | ||
returnFullResponse: true, | ||
}), | ||
undefined, | ||
); | ||
}); | ||
|
||
it('should handle authentication with generic credentials', async () => { | ||
helpers.httpRequest.mockResolvedValue({ | ||
body: 'Hello World', | ||
headers: { | ||
'content-type': 'text/plain', | ||
}, | ||
}); | ||
|
||
executeFunctions.getNodeParameter.mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'method': | ||
return 'GET'; | ||
case 'url': | ||
return 'https://httpbin.org/text/plain'; | ||
case 'authentication': | ||
return 'genericCredentialType'; | ||
case 'genericAuthType': | ||
return 'httpBasicAuth'; | ||
case 'options': | ||
return {}; | ||
case 'placeholderDefinitions.values': | ||
return []; | ||
default: | ||
return undefined; | ||
} | ||
}); | ||
|
||
executeFunctions.getCredentials.mockResolvedValue({ | ||
user: 'username', | ||
password: 'password', | ||
}); | ||
|
||
const { response } = await httpTool.supplyData.call(executeFunctions, 0); | ||
|
||
const res = await (response as N8nTool).invoke({}); | ||
|
||
expect(res).toEqual('Hello World'); | ||
|
||
expect(helpers.httpRequest).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
returnFullResponse: true, | ||
auth: expect.objectContaining({ | ||
username: 'username', | ||
password: 'password', | ||
}), | ||
}), | ||
); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters