fix(Mandrill Node): Fix a typo in subaccount in options (#18103)

This commit is contained in:
Shireen Missi
2025-08-08 14:40:04 +01:00
committed by GitHub
parent 6f4c76c78c
commit 833bcdde00
6 changed files with 461 additions and 2 deletions

View File

@@ -766,8 +766,8 @@ export class Mandrill implements INodeType {
message.from_name = options.fromName;
}
if (options.subaccount) {
message.subaccount = options.subaccount;
if (options.subAccount) {
message.subaccount = options.subAccount.toString();
}
const body: Body = {

View File

@@ -0,0 +1,143 @@
import {
getGoogleAnalyticsDomainsArray,
getTags,
getToEmailArray,
validateJSON,
} from '../GenericFunctions';
describe('Mandrill GenericFunctions', () => {
describe('getToEmailArray', () => {
it('should convert single email to array', () => {
const result = getToEmailArray('test@example.com');
expect(result).toEqual([
{
email: 'test@example.com',
type: 'to',
},
]);
});
it('should convert comma-separated emails to array', () => {
const result = getToEmailArray('test1@example.com,test2@example.com,test3@example.com');
expect(result).toEqual([
{
email: 'test1@example.com',
type: 'to',
},
{
email: 'test2@example.com',
type: 'to',
},
{
email: 'test3@example.com',
type: 'to',
},
]);
});
it('should handle emails with spaces after commas', () => {
const result = getToEmailArray('test1@example.com, test2@example.com, test3@example.com');
expect(result).toEqual([
{
email: 'test1@example.com',
type: 'to',
},
{
email: ' test2@example.com',
type: 'to',
},
{
email: ' test3@example.com',
type: 'to',
},
]);
});
});
describe('getGoogleAnalyticsDomainsArray', () => {
it('should convert single domain to array', () => {
const result = getGoogleAnalyticsDomainsArray('example.com');
expect(result).toEqual(['example.com']);
});
it('should convert comma-separated domains to array', () => {
const result = getGoogleAnalyticsDomainsArray('example.com,test.com,demo.org');
expect(result).toEqual(['example.com', 'test.com', 'demo.org']);
});
it('should handle domains with spaces after commas', () => {
const result = getGoogleAnalyticsDomainsArray('example.com, test.com, demo.org');
expect(result).toEqual(['example.com', ' test.com', ' demo.org']);
});
it('should handle empty string', () => {
const result = getGoogleAnalyticsDomainsArray('');
expect(result).toEqual(['']);
});
});
describe('getTags', () => {
it('should convert single tag to array', () => {
const result = getTags('newsletter');
expect(result).toEqual(['newsletter']);
});
it('should convert comma-separated tags to array', () => {
const result = getTags('newsletter,marketing,promotion');
expect(result).toEqual(['newsletter', 'marketing', 'promotion']);
});
it('should handle tags with spaces after commas', () => {
const result = getTags('newsletter, marketing, promotion');
expect(result).toEqual(['newsletter', ' marketing', ' promotion']);
});
it('should handle empty string', () => {
const result = getTags('');
expect(result).toEqual(['']);
});
});
describe('validateJSON', () => {
it('should parse valid JSON object', () => {
const result = validateJSON('{"Test": "value", "number": 123}');
expect(result).toEqual({ Test: 'value', number: 123 });
});
it('should parse valid JSON array', () => {
const result = validateJSON('[{"name": "Test", "value": "data"}]');
expect(result).toEqual([{ name: 'Test', value: 'data' }]);
});
it('should return empty array for invalid JSON', () => {
const result = validateJSON('invalid json');
expect(result).toEqual([]);
});
it('should return empty array for undefined input', () => {
const result = validateJSON(undefined);
expect(result).toEqual([]);
});
it('should return null for null JSON string', () => {
const result = validateJSON('null');
expect(result).toEqual(null);
});
it('should parse nested JSON correctly', () => {
const result = validateJSON('{"metadata": {"key": "value"}, "array": [1, 2, 3]}');
expect(result).toEqual({
metadata: { key: 'value' },
array: [1, 2, 3],
});
});
it('should handle JSON with special characters', () => {
const result = validateJSON('{"message": "Hello\\nWorld\\t!", "emoji": "🎉"}');
expect(result).toEqual({
message: 'Hello\nWorld\t!',
emoji: '🎉',
});
});
});
});

View File

@@ -0,0 +1,101 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import { mock, mockDeep } from 'jest-mock-extended';
import type { ILoadOptionsFunctions, INode } from 'n8n-workflow';
import nock from 'nock';
import { Mandrill } from '../Mandrill.node';
describe('Test Mandrill Node', () => {
describe('Messages', () => {
const mandrillNock = nock('https://mandrillapp.com/api/1.0');
beforeAll(() => {
// Mock sendTemplate API call with subaccount verification
mandrillNock
.post('/messages/send-template.json', (body) => {
// Verify that subaccount is properly passed to the API
return (
body.message &&
body.message.subaccount === 'test-subaccount' &&
body.template_name === 'test-template'
);
})
.reply(200, [
{
email: 'recipient@example.com',
status: 'sent',
reject_reason: null,
_id: 'test-message-id-456',
},
]);
// Mock sendTemplate API call specifically for subaccount regression test
mandrillNock
.post('/messages/send-template.json', (body) => {
// Verify regression test scenario: subaccount is properly passed
return (
body.message &&
body.message.subaccount === 'regression-test-subaccount' &&
body.template_name === 'test-template-subaccount'
);
})
.reply(200, [
{
email: 'recipient@example.com',
status: 'sent',
reject_reason: null,
_id: 'test-subaccount-message-id',
},
]);
// Mock sendHtml API call
mandrillNock.post('/messages/send.json').reply(200, [
{
email: 'recipient@example.com',
status: 'rejected',
_id: 'test-message-id-123',
reject_reason: 'global-block',
queued_reason: null,
},
]);
});
afterAll(() => mandrillNock.done());
new NodeTestHarness().setupTests({
workflowFiles: [
'sendTemplate.workflow.json',
'sendTemplateWithSubaccount.workflow.json',
'sendHtml.workflow.json',
],
});
});
describe('loadOptions', () => {
describe('getTemplates', () => {
it('should return a list of Mandrill templates', async () => {
const mandrill = new Mandrill();
const loadOptionsFunctions = mockDeep<ILoadOptionsFunctions>();
loadOptionsFunctions.getNode.mockReturnValue(mock<INode>());
loadOptionsFunctions.getCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
loadOptionsFunctions.helpers.request.mockResolvedValue([
{
slug: 'template-1',
name: 'Test Template 1',
},
{
slug: 'template-2',
name: 'Test Template 2',
},
]);
const result = await mandrill.methods.loadOptions.getTemplates.call(loadOptionsFunctions);
expect(result).toEqual([
{ name: 'Test Template 1', value: 'template-1' },
{ name: 'Test Template 2', value: 'template-2' },
]);
});
});
});
});

View File

@@ -0,0 +1,72 @@
{
"name": "sendHtml",
"nodes": [
{
"parameters": {},
"id": "test-trigger-node-id",
"name": "When clicking 'Execute workflow'",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0]
},
{
"parameters": {
"operation": "sendHtml",
"fromEmail": "sender@example.com",
"toEmail": "recipient@example.com",
"options": {
"html": "<h1>Test HTML Content</h1>",
"subject": "Test HTML Subject"
}
},
"id": "test-mandrill-node-id",
"name": "Mandrill",
"type": "n8n-nodes-base.mandrill",
"typeVersion": 1,
"position": [220, 0],
"credentials": {
"mandrillApi": {
"id": "test-credentials-id",
"name": "Test Mandrill API"
}
}
}
],
"connections": {
"When clicking 'Execute workflow'": {
"main": [
[
{
"node": "Mandrill",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {
"Mandrill": [
{
"json": {
"email": "recipient@example.com",
"status": "rejected",
"_id": "test-message-id-123",
"reject_reason": "global-block",
"queued_reason": null
}
}
]
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "test-version-id",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "test-instance-id"
},
"id": "test-workflow-id-2",
"tags": []
}

View File

@@ -0,0 +1,71 @@
{
"name": "sendTemplate",
"nodes": [
{
"parameters": {},
"id": "test-trigger-node-id",
"name": "When clicking 'Execute workflow'",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0]
},
{
"parameters": {
"resource": "message",
"operation": "sendTemplate",
"template": "test-template",
"fromEmail": "sender@example.com",
"toEmail": "recipient@example.com",
"options": {
"subAccount": "test-subaccount"
}
},
"id": "test-mandrill-node-id",
"name": "Mandrill",
"type": "n8n-nodes-base.mandrill",
"typeVersion": 1,
"position": [220, 0],
"credentials": {
"mandrillApi": {
"id": "test-credentials-id",
"name": "Test Mandrill API"
}
}
}
],
"connections": {
"When clicking 'Execute workflow'": {
"main": [
[
{
"node": "Mandrill",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {
"Mandrill": [
{
"json": {
"email": "recipient@example.com",
"status": "sent",
"reject_reason": null,
"_id": "test-message-id-456"
}
}
]
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "test-version-id",
"meta": {
"instanceId": "test-instance"
},
"id": "test-workflow-id",
"tags": []
}

View File

@@ -0,0 +1,72 @@
{
"name": "sendTemplateWithSubaccount",
"nodes": [
{
"parameters": {},
"id": "test-trigger-node-id",
"name": "When clicking 'Execute workflow'",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0]
},
{
"parameters": {
"resource": "message",
"operation": "sendTemplate",
"template": "test-template-subaccount",
"fromEmail": "sender@example.com",
"toEmail": "recipient@example.com",
"options": {
"subAccount": "regression-test-subaccount",
"subject": "Testing subaccount functionality"
}
},
"id": "test-mandrill-node-id",
"name": "Mandrill",
"type": "n8n-nodes-base.mandrill",
"typeVersion": 1,
"position": [220, 0],
"credentials": {
"mandrillApi": {
"id": "test-credentials-id",
"name": "Test Mandrill API"
}
}
}
],
"connections": {
"When clicking 'Execute workflow'": {
"main": [
[
{
"node": "Mandrill",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {
"Mandrill": [
{
"json": {
"email": "recipient@example.com",
"status": "sent",
"reject_reason": null,
"_id": "test-subaccount-message-id"
}
}
]
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "test-version-id",
"meta": {
"instanceId": "test-instance"
},
"id": "test-workflow-subaccount-id",
"tags": []
}