fix(Telegram Node): Use parameter index instead of 0 for binaryData (#19236)

This commit is contained in:
yehorkardash
2025-09-09 07:29:36 +00:00
committed by GitHub
parent 48df1de8c9
commit 38a6140e79
2 changed files with 335 additions and 5 deletions

View File

@@ -2146,10 +2146,10 @@ export class Telegram implements INodeType {
let responseData; let responseData;
if (binaryData) { if (binaryData) {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', 0); const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
const itemBinaryData = items[i].binary![binaryPropertyName]; const itemBinaryData = items[i].binary![binaryPropertyName];
const propertyName = getPropertyName(operation); const propertyName = getPropertyName(operation);
const fileName = this.getNodeParameter('additionalFields.fileName', 0, '') as string; const fileName = this.getNodeParameter('additionalFields.fileName', i, '') as string;
const filename = fileName || itemBinaryData.fileName?.toString(); const filename = fileName || itemBinaryData.fileName?.toString();
@@ -2214,7 +2214,7 @@ export class Telegram implements INodeType {
); );
const fileName = filePath.split('/').pop() as string; const fileName = filePath.split('/').pop() as string;
const additionalFields = this.getNodeParameter('additionalFields', 0); const additionalFields = this.getNodeParameter('additionalFields', i);
const providedMimeType = additionalFields?.mimeType as string | undefined; const providedMimeType = additionalFields?.mimeType as string | undefined;
const mimeType = providedMimeType ?? (lookup(fileName) || 'application/octet-stream'); const mimeType = providedMimeType ?? (lookup(fileName) || 'application/octet-stream');
@@ -2239,7 +2239,6 @@ export class Telegram implements INodeType {
returnData.push(...executionData); returnData.push(...executionData);
continue; continue;
} }
const executionData = this.helpers.constructExecutionMetaData( const executionData = this.helpers.constructExecutionMetaData(
this.helpers.returnJsonArray(responseData as IDataObject[]), this.helpers.returnJsonArray(responseData as IDataObject[]),
{ itemData: { item: i } }, { itemData: { item: i } },

View File

@@ -1,5 +1,10 @@
import { mockDeep } from 'jest-mock-extended'; import { mockDeep } from 'jest-mock-extended';
import type { IExecuteFunctions, INode } from 'n8n-workflow'; import type {
IExecuteFunctions,
INode,
INodeExecutionData,
NodeExecutionWithMetadata,
} from 'n8n-workflow';
import * as GenericFunctions from '../GenericFunctions'; import * as GenericFunctions from '../GenericFunctions';
import { Telegram } from '../Telegram.node'; import { Telegram } from '../Telegram.node';
@@ -19,6 +24,12 @@ describe('Telegram node', () => {
typeVersion: 1.2, typeVersion: 1.2,
} as INode); } as INode);
executeFunctionsMock.getInputData.mockReturnValue([{ json: {} }]); executeFunctionsMock.getInputData.mockReturnValue([{ json: {} }]);
executeFunctionsMock.helpers.returnJsonArray.mockImplementation(
(input) => input as INodeExecutionData[],
);
executeFunctionsMock.helpers.constructExecutionMetaData.mockImplementation(
(input) => input as NodeExecutionWithMetadata[],
);
}); });
describe('file:get', () => { describe('file:get', () => {
@@ -184,4 +195,324 @@ describe('Telegram node', () => {
); );
}); });
}); });
describe('message:sendPhoto with binary data', () => {
beforeEach(() => {
executeFunctionsMock.getNodeParameter.mockImplementation((paramName, index) => {
switch (paramName) {
case 'resource':
return 'message';
case 'operation':
return 'sendPhoto';
case 'binaryData':
return true;
case 'chatId':
return index === 0 ? 'chat-id-0' : index === 1 ? 'chat-id-1' : 'chat-id-2';
case 'binaryPropertyName':
return index === 0 ? 'data0' : index === 1 ? 'data1' : 'data2';
case 'additionalFields.fileName':
return index === 0 ? 'photo0.jpg' : index === 1 ? 'photo1.png' : 'photo2.gif';
case 'additionalFields':
return {};
default:
return undefined;
}
});
});
it('should use correct index for binaryPropertyName parameter', async () => {
executeFunctionsMock.getInputData.mockReturnValue([
{
json: {},
binary: {
data0: {
data: 'binary-data-0',
mimeType: 'image/jpeg',
fileName: 'original0.jpg',
},
},
},
{
json: {},
binary: {
data1: {
data: 'binary-data-1',
mimeType: 'image/png',
fileName: 'original1.png',
},
},
},
]);
apiRequestSpy.mockResolvedValue([{ result: { message_id: 123 } }]);
await node.execute.call(executeFunctionsMock);
expect(executeFunctionsMock.getNodeParameter).toHaveBeenCalledWith('binaryPropertyName', 0);
expect(executeFunctionsMock.getNodeParameter).toHaveBeenCalledWith('binaryPropertyName', 1);
expect(executeFunctionsMock.getNodeParameter).not.toHaveBeenCalledWith(
'binaryPropertyName',
0,
expect.anything(),
);
expect(executeFunctionsMock.getNodeParameter).not.toHaveBeenCalledWith(
'binaryPropertyName',
1,
expect.anything(),
);
});
it('should use correct index for additionalFields.fileName parameter', async () => {
executeFunctionsMock.getInputData.mockReturnValue([
{
json: {},
binary: {
data0: {
data: 'binary-data-0',
mimeType: 'image/jpeg',
},
},
},
{
json: {},
binary: {
data1: {
data: 'binary-data-1',
mimeType: 'image/png',
},
},
},
]);
apiRequestSpy.mockResolvedValue([{ result: { message_id: 123 } }]);
await node.execute.call(executeFunctionsMock);
expect(executeFunctionsMock.getNodeParameter).toHaveBeenCalledWith(
'additionalFields.fileName',
0,
'',
);
expect(executeFunctionsMock.getNodeParameter).toHaveBeenCalledWith(
'additionalFields.fileName',
1,
'',
);
});
it('should use correct binary data for each item based on binaryPropertyName index', async () => {
executeFunctionsMock.getInputData.mockReturnValue([
{
json: {},
binary: {
data0: {
data: 'binary-data-0',
mimeType: 'image/jpeg',
fileName: 'original0.jpg',
},
wrongData: {
data: 'wrong-binary-data',
mimeType: 'image/gif',
},
},
},
{
json: {},
binary: {
data1: {
data: 'binary-data-1',
mimeType: 'image/png',
fileName: 'original1.png',
},
wrongData: {
data: 'wrong-binary-data',
mimeType: 'image/gif',
},
},
},
]);
apiRequestSpy.mockResolvedValue([{ result: { message_id: 123 } }]);
await node.execute.call(executeFunctionsMock);
expect(apiRequestSpy).toHaveBeenCalledTimes(2);
expect(apiRequestSpy).toHaveBeenNthCalledWith(
1,
'POST',
'sendPhoto',
{},
{},
expect.objectContaining({
formData: expect.objectContaining({
chat_id: 'chat-id-0',
photo: expect.objectContaining({
value: expect.any(Buffer),
options: expect.objectContaining({
filename: 'photo0.jpg',
contentType: 'image/jpeg',
}),
}),
}),
}),
);
expect(apiRequestSpy).toHaveBeenNthCalledWith(
2,
'POST',
'sendPhoto',
{},
{},
expect.objectContaining({
formData: expect.objectContaining({
chat_id: 'chat-id-1',
photo: expect.objectContaining({
value: expect.any(Buffer),
options: expect.objectContaining({
filename: 'photo1.png',
contentType: 'image/png',
}),
}),
}),
}),
);
});
it('should fallback to binary fileName when additionalFields.fileName is empty', async () => {
executeFunctionsMock.getNodeParameter.mockImplementation((paramName, index) => {
switch (paramName) {
case 'resource':
return 'message';
case 'operation':
return 'sendPhoto';
case 'binaryData':
return true;
case 'chatId':
return index === 0 ? 'chat-id-0' : 'chat-id-1';
case 'binaryPropertyName':
return index === 0 ? 'data0' : 'data1';
case 'additionalFields.fileName':
return index === 0 ? '' : 'custom-name.jpg';
case 'additionalFields':
return {};
default:
return undefined;
}
});
executeFunctionsMock.getInputData.mockReturnValue([
{
json: {},
binary: {
data0: {
data: 'binary-data-0',
mimeType: 'image/jpeg',
fileName: 'fallback-name.jpg',
},
},
},
{
json: {},
binary: {
data1: {
data: 'binary-data-1',
mimeType: 'image/png',
fileName: 'original-name.png',
},
},
},
]);
apiRequestSpy.mockResolvedValue([{ result: { message_id: 123 } }]);
await node.execute.call(executeFunctionsMock);
const expectFileName = (index: number, filename: string) => {
expect(apiRequestSpy).toHaveBeenNthCalledWith(
index,
'POST',
'sendPhoto',
{},
{},
{
formData: expect.objectContaining({
photo: expect.objectContaining({
options: expect.objectContaining({
filename,
}),
}),
}),
},
);
};
expectFileName(1, 'fallback-name.jpg');
expectFileName(2, 'custom-name.jpg');
});
it('should process different chat IDs for multiple items correctly', async () => {
executeFunctionsMock.getInputData.mockReturnValue([
{
json: {},
binary: {
data0: {
data: 'binary-data-0',
mimeType: 'image/jpeg',
fileName: 'photo0.jpg',
},
},
},
{
json: {},
binary: {
data1: {
data: 'binary-data-1',
mimeType: 'image/png',
fileName: 'photo1.png',
},
},
},
{
json: {},
binary: {
data2: {
data: 'binary-data-2',
mimeType: 'image/gif',
fileName: 'photo2.gif',
},
},
},
]);
apiRequestSpy.mockResolvedValue([{ result: { message_id: 123 } }]);
await node.execute.call(executeFunctionsMock);
expect(executeFunctionsMock.getNodeParameter).toHaveBeenCalledWith('chatId', 0);
expect(executeFunctionsMock.getNodeParameter).toHaveBeenCalledWith('chatId', 1);
expect(executeFunctionsMock.getNodeParameter).toHaveBeenCalledWith('chatId', 2);
expect(apiRequestSpy).toHaveBeenCalledTimes(3);
const expectChatId = (n: number, chatId: string) => {
expect(apiRequestSpy).toHaveBeenNthCalledWith(
n,
'POST',
'sendPhoto',
{},
{},
{
formData: expect.objectContaining({
chat_id: chatId,
}),
},
);
};
expectChatId(1, 'chat-id-0');
expectChatId(2, 'chat-id-1');
expectChatId(3, 'chat-id-2');
});
});
}); });