first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,629 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import type { IExecuteFunctions, ILoadOptionsFunctions } from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import { AwsLambda } from '../AwsLambda.node';
|
||||
import * as GenericFunctions from '../GenericFunctions';
|
||||
|
||||
describe('AwsLambda', () => {
|
||||
let node: AwsLambda;
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockLoadOptionsFunctions: jest.Mocked<ILoadOptionsFunctions>;
|
||||
const awsApiRequestRESTSpy = jest.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
|
||||
beforeEach(() => {
|
||||
node = new AwsLambda();
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
mockLoadOptionsFunctions = mockDeep<ILoadOptionsFunctions>();
|
||||
jest.clearAllMocks();
|
||||
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([{ json: {} }]);
|
||||
mockExecuteFunctions.getNode.mockReturnValue({
|
||||
id: 'test-node',
|
||||
name: 'AWS Lambda',
|
||||
type: 'n8n-nodes-base.awsLambda',
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
});
|
||||
(mockExecuteFunctions.helpers.constructExecutionMetaData as jest.Mock).mockImplementation(
|
||||
(data: any, meta: any) => {
|
||||
return [
|
||||
{
|
||||
...data[0],
|
||||
pairedItem: meta?.itemData?.item ?? 0,
|
||||
},
|
||||
];
|
||||
},
|
||||
);
|
||||
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation((data: any) => [
|
||||
{ json: data },
|
||||
]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('Load Options Methods', () => {
|
||||
describe('getFunctions', () => {
|
||||
it('should load functions without pagination', async () => {
|
||||
const mockFunctions = {
|
||||
Functions: [
|
||||
{
|
||||
FunctionName: 'test-function-1',
|
||||
FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:test-function-1',
|
||||
},
|
||||
{
|
||||
FunctionName: 'test-function-2',
|
||||
FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:test-function-2',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
awsApiRequestRESTSpy.mockResolvedValue(mockFunctions);
|
||||
|
||||
const result = await node.methods.loadOptions.getFunctions.call(mockLoadOptionsFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
name: 'test-function-1',
|
||||
value: 'arn:aws:lambda:us-east-1:123456789012:function:test-function-1',
|
||||
},
|
||||
{
|
||||
name: 'test-function-2',
|
||||
value: 'arn:aws:lambda:us-east-1:123456789012:function:test-function-2',
|
||||
},
|
||||
]);
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledWith(
|
||||
'lambda',
|
||||
'GET',
|
||||
'/2015-03-31/functions/',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle paginated function list', async () => {
|
||||
const mockFirstPage = {
|
||||
Functions: [
|
||||
{
|
||||
FunctionName: 'test-function-1',
|
||||
FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:test-function-1',
|
||||
},
|
||||
],
|
||||
NextMarker: 'marker123',
|
||||
};
|
||||
|
||||
const mockSecondPage = {
|
||||
Functions: [
|
||||
{
|
||||
FunctionName: 'test-function-2',
|
||||
FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:test-function-2',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
awsApiRequestRESTSpy
|
||||
.mockResolvedValueOnce(mockFirstPage)
|
||||
.mockResolvedValueOnce(mockSecondPage);
|
||||
|
||||
const result = await node.methods.loadOptions.getFunctions.call(mockLoadOptionsFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
name: 'test-function-1',
|
||||
value: 'arn:aws:lambda:us-east-1:123456789012:function:test-function-1',
|
||||
},
|
||||
{
|
||||
name: 'test-function-2',
|
||||
value: 'arn:aws:lambda:us-east-1:123456789012:function:test-function-2',
|
||||
},
|
||||
]);
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledTimes(2);
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'lambda',
|
||||
'GET',
|
||||
'/2015-03-31/functions/?MaxItems=50&Marker=marker123',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle multiple pages with NextMarker', async () => {
|
||||
const mockFirstPage = {
|
||||
Functions: [
|
||||
{
|
||||
FunctionName: 'function-1',
|
||||
FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:function-1',
|
||||
},
|
||||
],
|
||||
NextMarker: 'marker1',
|
||||
};
|
||||
|
||||
const mockSecondPage = {
|
||||
Functions: [
|
||||
{
|
||||
FunctionName: 'function-2',
|
||||
FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:function-2',
|
||||
},
|
||||
],
|
||||
NextMarker: 'marker2',
|
||||
};
|
||||
|
||||
const mockThirdPage = {
|
||||
Functions: [
|
||||
{
|
||||
FunctionName: 'function-3',
|
||||
FunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:function-3',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
awsApiRequestRESTSpy
|
||||
.mockResolvedValueOnce(mockFirstPage)
|
||||
.mockResolvedValueOnce(mockSecondPage)
|
||||
.mockResolvedValueOnce(mockThirdPage);
|
||||
|
||||
const result = await node.methods.loadOptions.getFunctions.call(mockLoadOptionsFunctions);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledTimes(3);
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
'lambda',
|
||||
'GET',
|
||||
'/2015-03-31/functions/?MaxItems=50&Marker=marker2',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty function list', async () => {
|
||||
const mockEmptyResponse = {
|
||||
Functions: [],
|
||||
};
|
||||
|
||||
awsApiRequestRESTSpy.mockResolvedValue(mockEmptyResponse);
|
||||
|
||||
const result = await node.methods.loadOptions.getFunctions.call(mockLoadOptionsFunctions);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledWith(
|
||||
'lambda',
|
||||
'GET',
|
||||
'/2015-03-31/functions/',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Execute Method', () => {
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName) => {
|
||||
const mockParams = {
|
||||
operation: 'invoke',
|
||||
function: 'arn:aws:lambda:us-east-1:123456789012:function:test-function',
|
||||
qualifier: '$LATEST',
|
||||
invocationType: 'RequestResponse',
|
||||
payload: JSON.stringify({ test: 'data' }),
|
||||
};
|
||||
return mockParams[paramName as keyof typeof mockParams];
|
||||
});
|
||||
});
|
||||
|
||||
describe('Successful Lambda Invocation', () => {
|
||||
it('should invoke lambda function with RequestResponse type', async () => {
|
||||
const mockResponse = {
|
||||
StatusCode: 200,
|
||||
result: { success: true, data: 'processed' },
|
||||
};
|
||||
|
||||
awsApiRequestRESTSpy.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
[
|
||||
{
|
||||
json: [
|
||||
{
|
||||
json: { result: mockResponse },
|
||||
pairedItem: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledWith(
|
||||
'lambda',
|
||||
'POST',
|
||||
'/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:test-function/invocations?Qualifier=$LATEST',
|
||||
JSON.stringify({ test: 'data' }),
|
||||
{
|
||||
'X-Amz-Invocation-Type': 'RequestResponse',
|
||||
'Content-Type': 'application/x-amz-json-1.0',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should invoke lambda function with Event type', async () => {
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName) => {
|
||||
const mockParams = {
|
||||
operation: 'invoke',
|
||||
function: 'test-function',
|
||||
qualifier: 'PROD',
|
||||
invocationType: 'Event',
|
||||
payload: JSON.stringify({ async: true }),
|
||||
};
|
||||
return mockParams[paramName as keyof typeof mockParams];
|
||||
});
|
||||
|
||||
const mockResponse = {
|
||||
StatusCode: 202,
|
||||
};
|
||||
|
||||
awsApiRequestRESTSpy.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
[
|
||||
{
|
||||
json: [
|
||||
{
|
||||
json: { result: mockResponse },
|
||||
pairedItem: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledWith(
|
||||
'lambda',
|
||||
'POST',
|
||||
'/2015-03-31/functions/test-function/invocations?Qualifier=PROD',
|
||||
JSON.stringify({ async: true }),
|
||||
{
|
||||
'X-Amz-Invocation-Type': 'Event',
|
||||
'Content-Type': 'application/x-amz-json-1.0',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty payload', async () => {
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName) => {
|
||||
const mockParams = {
|
||||
operation: 'invoke',
|
||||
function: 'test-function',
|
||||
qualifier: '$LATEST',
|
||||
invocationType: 'RequestResponse',
|
||||
payload: '',
|
||||
};
|
||||
return mockParams[paramName as keyof typeof mockParams];
|
||||
});
|
||||
|
||||
const mockResponse = { result: 'success' };
|
||||
awsApiRequestRESTSpy.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
[
|
||||
{
|
||||
json: [
|
||||
{
|
||||
json: { result: mockResponse },
|
||||
pairedItem: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledWith(
|
||||
'lambda',
|
||||
'POST',
|
||||
'/2015-03-31/functions/test-function/invocations?Qualifier=$LATEST',
|
||||
'',
|
||||
{
|
||||
'X-Amz-Invocation-Type': 'RequestResponse',
|
||||
'Content-Type': 'application/x-amz-json-1.0',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should process multiple input items', async () => {
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([
|
||||
{ json: { item: 1 } },
|
||||
{ json: { item: 2 } },
|
||||
]);
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName, itemIndex) => {
|
||||
const mockParams = {
|
||||
operation: 'invoke',
|
||||
function: 'test-function',
|
||||
qualifier: '$LATEST',
|
||||
invocationType: 'RequestResponse',
|
||||
payload: JSON.stringify({ item: itemIndex + 1 }),
|
||||
};
|
||||
return mockParams[paramName as keyof typeof mockParams];
|
||||
});
|
||||
|
||||
const mockResponse1 = { result: 'item1-processed' };
|
||||
const mockResponse2 = { result: 'item2-processed' };
|
||||
awsApiRequestRESTSpy
|
||||
.mockResolvedValueOnce(mockResponse1)
|
||||
.mockResolvedValueOnce(mockResponse2);
|
||||
|
||||
const result = await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(result[0]).toHaveLength(1);
|
||||
expect(result[0][0].json).toHaveLength(2);
|
||||
expect(result[0][0].json[0]).toEqual({ json: { result: mockResponse1 }, pairedItem: 0 });
|
||||
expect(result[0][0].json[1]).toEqual({ json: { result: mockResponse2 }, pairedItem: 1 });
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Lambda Function Errors', () => {
|
||||
it('should handle lambda function execution errors', async () => {
|
||||
const mockErrorResponse = {
|
||||
errorMessage: 'Function execution failed',
|
||||
errorType: 'Runtime.HandlerNotFound',
|
||||
stackTrace: ['at /var/task/index.js:10:5'],
|
||||
};
|
||||
|
||||
awsApiRequestRESTSpy.mockResolvedValue(mockErrorResponse);
|
||||
|
||||
await expect(node.execute.call(mockExecuteFunctions)).rejects.toThrow(NodeApiError);
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledWith(
|
||||
'lambda',
|
||||
'POST',
|
||||
'/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:test-function/invocations?Qualifier=$LATEST',
|
||||
JSON.stringify({ test: 'data' }),
|
||||
{
|
||||
'X-Amz-Invocation-Type': 'RequestResponse',
|
||||
'Content-Type': 'application/x-amz-json-1.0',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle API errors when continueOnFail is true', async () => {
|
||||
const apiError = new Error('Function not found');
|
||||
mockExecuteFunctions.continueOnFail.mockReturnValue(true);
|
||||
awsApiRequestRESTSpy.mockRejectedValue(apiError);
|
||||
|
||||
const result = await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
[
|
||||
{
|
||||
json: [
|
||||
{
|
||||
json: { error: 'Function not found' },
|
||||
pairedItem: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should throw API errors when continueOnFail is false', async () => {
|
||||
const apiError = new Error('Invalid function name');
|
||||
mockExecuteFunctions.continueOnFail.mockReturnValue(false);
|
||||
awsApiRequestRESTSpy.mockRejectedValue(apiError);
|
||||
|
||||
await expect(node.execute.call(mockExecuteFunctions)).rejects.toThrow(
|
||||
'Invalid function name',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle mixed success and error scenarios with continueOnFail', async () => {
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([
|
||||
{ json: { item: 1 } },
|
||||
{ json: { item: 2 } },
|
||||
{ json: { item: 3 } },
|
||||
]);
|
||||
mockExecuteFunctions.continueOnFail.mockReturnValue(true);
|
||||
|
||||
const mockSuccessResponse = { result: 'success' };
|
||||
const apiError = new Error('Function error');
|
||||
|
||||
awsApiRequestRESTSpy
|
||||
.mockResolvedValueOnce(mockSuccessResponse)
|
||||
.mockRejectedValueOnce(apiError)
|
||||
.mockResolvedValueOnce(mockSuccessResponse);
|
||||
|
||||
const result = await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(result[0]).toHaveLength(1);
|
||||
expect(result[0][0].json).toHaveLength(3);
|
||||
expect(result[0][0].json[0]).toEqual({
|
||||
json: { result: mockSuccessResponse },
|
||||
pairedItem: 0,
|
||||
});
|
||||
expect(result[0][0].json[1]).toEqual({ json: { error: 'Function error' }, pairedItem: 1 });
|
||||
expect(result[0][0].json[2]).toEqual({
|
||||
json: { result: mockSuccessResponse },
|
||||
pairedItem: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Different Qualifier Values', () => {
|
||||
it('should handle custom version qualifier', async () => {
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName) => {
|
||||
const mockParams = {
|
||||
operation: 'invoke',
|
||||
function: 'test-function',
|
||||
qualifier: '1',
|
||||
invocationType: 'RequestResponse',
|
||||
payload: JSON.stringify({ version: 1 }),
|
||||
};
|
||||
return mockParams[paramName as keyof typeof mockParams];
|
||||
});
|
||||
|
||||
const mockResponse = { result: 'version-1-response' };
|
||||
awsApiRequestRESTSpy.mockResolvedValue(mockResponse);
|
||||
|
||||
await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledWith(
|
||||
'lambda',
|
||||
'POST',
|
||||
'/2015-03-31/functions/test-function/invocations?Qualifier=1',
|
||||
JSON.stringify({ version: 1 }),
|
||||
{
|
||||
'X-Amz-Invocation-Type': 'RequestResponse',
|
||||
'Content-Type': 'application/x-amz-json-1.0',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle alias qualifier', async () => {
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName) => {
|
||||
const mockParams = {
|
||||
operation: 'invoke',
|
||||
function: 'test-function',
|
||||
qualifier: 'PROD',
|
||||
invocationType: 'RequestResponse',
|
||||
payload: JSON.stringify({ environment: 'production' }),
|
||||
};
|
||||
return mockParams[paramName as keyof typeof mockParams];
|
||||
});
|
||||
|
||||
const mockResponse = { result: 'prod-response' };
|
||||
awsApiRequestRESTSpy.mockResolvedValue(mockResponse);
|
||||
|
||||
await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledWith(
|
||||
'lambda',
|
||||
'POST',
|
||||
'/2015-03-31/functions/test-function/invocations?Qualifier=PROD',
|
||||
JSON.stringify({ environment: 'production' }),
|
||||
{
|
||||
'X-Amz-Invocation-Type': 'RequestResponse',
|
||||
'Content-Type': 'application/x-amz-json-1.0',
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Payload Handling', () => {
|
||||
it('should handle complex JSON payload', async () => {
|
||||
const complexPayload = {
|
||||
user: { id: 123, name: 'John Doe' },
|
||||
data: [1, 2, 3],
|
||||
metadata: { timestamp: '2023-01-01T00:00:00Z' },
|
||||
};
|
||||
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName) => {
|
||||
const mockParams = {
|
||||
operation: 'invoke',
|
||||
function: 'test-function',
|
||||
qualifier: '$LATEST',
|
||||
invocationType: 'RequestResponse',
|
||||
payload: JSON.stringify(complexPayload),
|
||||
};
|
||||
return mockParams[paramName as keyof typeof mockParams];
|
||||
});
|
||||
|
||||
const mockResponse = { result: 'complex-processed' };
|
||||
awsApiRequestRESTSpy.mockResolvedValue(mockResponse);
|
||||
|
||||
await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledWith(
|
||||
'lambda',
|
||||
'POST',
|
||||
'/2015-03-31/functions/test-function/invocations?Qualifier=$LATEST',
|
||||
JSON.stringify(complexPayload),
|
||||
{
|
||||
'X-Amz-Invocation-Type': 'RequestResponse',
|
||||
'Content-Type': 'application/x-amz-json-1.0',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle string payload', async () => {
|
||||
mockExecuteFunctions.getNodeParameter.mockImplementation((paramName) => {
|
||||
const mockParams = {
|
||||
operation: 'invoke',
|
||||
function: 'test-function',
|
||||
qualifier: '$LATEST',
|
||||
invocationType: 'RequestResponse',
|
||||
payload: 'plain string payload',
|
||||
};
|
||||
return mockParams[paramName as keyof typeof mockParams];
|
||||
});
|
||||
|
||||
const mockResponse = { result: 'string-processed' };
|
||||
awsApiRequestRESTSpy.mockResolvedValue(mockResponse);
|
||||
|
||||
await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(awsApiRequestRESTSpy).toHaveBeenCalledWith(
|
||||
'lambda',
|
||||
'POST',
|
||||
'/2015-03-31/functions/test-function/invocations?Qualifier=$LATEST',
|
||||
'plain string payload',
|
||||
{
|
||||
'X-Amz-Invocation-Type': 'RequestResponse',
|
||||
'Content-Type': 'application/x-amz-json-1.0',
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle null response from Lambda', async () => {
|
||||
awsApiRequestRESTSpy.mockResolvedValue(null);
|
||||
|
||||
const result = await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
[
|
||||
{
|
||||
json: [
|
||||
{
|
||||
json: { result: null },
|
||||
pairedItem: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle undefined response from Lambda', async () => {
|
||||
awsApiRequestRESTSpy.mockResolvedValue(undefined);
|
||||
|
||||
const result = await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
[
|
||||
{
|
||||
json: [
|
||||
{
|
||||
json: { result: undefined },
|
||||
pairedItem: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle empty input data', async () => {
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([]);
|
||||
|
||||
const result = await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(result).toEqual([
|
||||
[
|
||||
{
|
||||
json: [],
|
||||
},
|
||||
],
|
||||
]);
|
||||
expect(awsApiRequestRESTSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,643 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import type { IExecuteFunctions, ILoadOptionsFunctions, IWebhookFunctions } from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import { awsApiRequest, awsApiRequestREST, awsApiRequestSOAP } from '../GenericFunctions';
|
||||
|
||||
describe('AWS GenericFunctions', () => {
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockLoadOptionsFunctions: jest.Mocked<ILoadOptionsFunctions>;
|
||||
let mockWebhookFunctions: jest.Mocked<IWebhookFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
mockLoadOptionsFunctions = mockDeep<ILoadOptionsFunctions>();
|
||||
mockWebhookFunctions = mockDeep<IWebhookFunctions>();
|
||||
jest.clearAllMocks();
|
||||
|
||||
mockExecuteFunctions.getNode.mockReturnValue({
|
||||
id: 'test-node',
|
||||
name: 'Test Node',
|
||||
type: 'n8n-nodes-base.aws',
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('awsApiRequest', () => {
|
||||
describe('Successful Requests', () => {
|
||||
it('should make successful API request with basic parameters', async () => {
|
||||
const mockCredentials = {
|
||||
accessKeyId: 'test-access-key',
|
||||
secretAccessKey: 'test-secret-key',
|
||||
region: 'us-east-1',
|
||||
};
|
||||
const mockResponse = { success: true, data: 'test response' };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequest.call(
|
||||
mockExecuteFunctions,
|
||||
's3',
|
||||
'GET',
|
||||
'/test-path',
|
||||
undefined,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
);
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
expect(mockExecuteFunctions.getCredentials).toHaveBeenCalledWith('aws');
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith('aws', {
|
||||
qs: {
|
||||
service: 's3',
|
||||
path: '/test-path',
|
||||
},
|
||||
method: 'GET',
|
||||
body: undefined,
|
||||
url: '',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
region: 'us-east-1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle lambda service with raw body', async () => {
|
||||
const mockCredentials = {
|
||||
region: 'us-west-2',
|
||||
};
|
||||
const testBody = '{"test": "data"}';
|
||||
const mockResponse = { result: 'lambda response' };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequest.call(
|
||||
mockExecuteFunctions,
|
||||
'lambda',
|
||||
'POST',
|
||||
'/2015-03-31/functions/test/invocations',
|
||||
testBody,
|
||||
);
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith('aws', {
|
||||
qs: {
|
||||
service: 'lambda',
|
||||
path: '/2015-03-31/functions/test/invocations',
|
||||
},
|
||||
method: 'POST',
|
||||
body: testBody,
|
||||
url: '',
|
||||
headers: undefined,
|
||||
region: 'us-west-2',
|
||||
});
|
||||
});
|
||||
|
||||
it('should stringify body for non-lambda services', async () => {
|
||||
const mockCredentials = { region: 'eu-central-1' };
|
||||
const testBody = { name: 'Test', value: 123 };
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
await awsApiRequest.call(
|
||||
mockExecuteFunctions,
|
||||
's3',
|
||||
'PUT',
|
||||
'/bucket/object',
|
||||
testBody as any,
|
||||
);
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'aws',
|
||||
expect.objectContaining({
|
||||
body: JSON.stringify(testBody),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should work with ILoadOptionsFunctions context', async () => {
|
||||
const mockCredentials = { region: 'ca-central-1' };
|
||||
const mockResponse = ['option1', 'option2'];
|
||||
|
||||
mockLoadOptionsFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
mockLoadOptionsFunctions.getNode.mockReturnValue({
|
||||
id: 'load-node',
|
||||
name: 'Load Node',
|
||||
type: 'n8n-nodes-base.aws',
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const result = await awsApiRequest.call(
|
||||
mockLoadOptionsFunctions,
|
||||
'ec2',
|
||||
'GET',
|
||||
'/instances',
|
||||
);
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
it('should work with IWebhookFunctions context', async () => {
|
||||
const mockCredentials = { region: 'sa-east-1' };
|
||||
const mockResponse = { webhook: 'processed' };
|
||||
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockWebhookFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
mockWebhookFunctions.getNode.mockReturnValue({
|
||||
id: 'webhook-node',
|
||||
name: 'Webhook Node',
|
||||
type: 'n8n-nodes-base.aws',
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const result = await awsApiRequest.call(
|
||||
mockWebhookFunctions,
|
||||
'apigateway',
|
||||
'GET',
|
||||
'/webhooks',
|
||||
);
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
it('should handle undefined body parameter', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
await awsApiRequest.call(mockExecuteFunctions, 's3', 'GET', '/list-buckets');
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'aws',
|
||||
expect.objectContaining({
|
||||
body: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle undefined headers parameter', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
await awsApiRequest.call(mockExecuteFunctions, 's3', 'GET', '/list-buckets');
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'aws',
|
||||
expect.objectContaining({
|
||||
headers: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Handling', () => {
|
||||
it('should handle credential retrieval errors', async () => {
|
||||
const credentialError = new Error('Failed to get AWS credentials');
|
||||
mockExecuteFunctions.getCredentials.mockRejectedValue(credentialError);
|
||||
|
||||
await expect(
|
||||
awsApiRequest.call(mockExecuteFunctions, 's3', 'GET', '/test-path'),
|
||||
).rejects.toThrow('Failed to get AWS credentials');
|
||||
|
||||
expect(mockExecuteFunctions.getCredentials).toHaveBeenCalledWith('aws');
|
||||
});
|
||||
|
||||
it('should wrap API errors in NodeApiError', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const apiError = new Error('AWS API Error');
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockRejectedValue(
|
||||
apiError,
|
||||
);
|
||||
|
||||
await expect(
|
||||
awsApiRequest.call(mockExecuteFunctions, 's3', 'GET', '/test-path'),
|
||||
).rejects.toThrow(NodeApiError);
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle authentication errors', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const authError = {
|
||||
message: 'Invalid AWS credentials',
|
||||
statusCode: 403,
|
||||
response: { body: 'Forbidden' },
|
||||
};
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockRejectedValue(
|
||||
authError,
|
||||
);
|
||||
|
||||
await expect(
|
||||
awsApiRequest.call(mockExecuteFunctions, 's3', 'GET', '/test-path'),
|
||||
).rejects.toThrow(NodeApiError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle empty service name', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
await awsApiRequest.call(mockExecuteFunctions, '', 'GET', '/test');
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'aws',
|
||||
expect.objectContaining({
|
||||
qs: {
|
||||
service: '',
|
||||
path: '/test',
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle null credentials region', async () => {
|
||||
const mockCredentials = { region: null };
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
await awsApiRequest.call(mockExecuteFunctions, 's3', 'GET', '/test');
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'aws',
|
||||
expect.objectContaining({
|
||||
region: null,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle various HTTP methods', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] as const;
|
||||
|
||||
for (const method of methods) {
|
||||
await awsApiRequest.call(mockExecuteFunctions, 's3', method, '/test');
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'aws',
|
||||
expect.objectContaining({
|
||||
method,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalledTimes(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('awsApiRequestREST', () => {
|
||||
describe('JSON Response Handling', () => {
|
||||
it('should parse valid JSON response', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const jsonResponse = '{"result": "success", "data": [1, 2, 3]}';
|
||||
const expectedParsed = { result: 'success', data: [1, 2, 3] };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
jsonResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestREST.call(
|
||||
mockExecuteFunctions,
|
||||
'lambda',
|
||||
'POST',
|
||||
'/2015-03-31/functions/test/invocations',
|
||||
'{"test": "data"}',
|
||||
);
|
||||
|
||||
expect(result).toEqual(expectedParsed);
|
||||
expect(mockExecuteFunctions.helpers.requestWithAuthentication).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return raw response when JSON parsing fails', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const invalidJsonResponse = 'Not valid JSON content';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
invalidJsonResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestREST.call(
|
||||
mockExecuteFunctions,
|
||||
's3',
|
||||
'GET',
|
||||
'/bucket/object.txt',
|
||||
);
|
||||
|
||||
expect(result).toBe(invalidJsonResponse);
|
||||
});
|
||||
|
||||
it('should handle empty string response', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const emptyResponse = '';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
emptyResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestREST.call(
|
||||
mockExecuteFunctions,
|
||||
'lambda',
|
||||
'POST',
|
||||
'/test',
|
||||
);
|
||||
|
||||
expect(result).toBe(emptyResponse);
|
||||
});
|
||||
|
||||
it('should handle null response', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const nullResponse = null;
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
nullResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestREST.call(
|
||||
mockExecuteFunctions,
|
||||
's3',
|
||||
'DELETE',
|
||||
'/bucket',
|
||||
);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle number response', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const numberResponse = 42;
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
numberResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestREST.call(
|
||||
mockExecuteFunctions,
|
||||
'cloudwatch',
|
||||
'GET',
|
||||
'/metrics',
|
||||
);
|
||||
|
||||
expect(result).toBe(numberResponse);
|
||||
});
|
||||
|
||||
it('should handle boolean response', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const booleanResponse = true;
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
booleanResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestREST.call(
|
||||
mockExecuteFunctions,
|
||||
's3',
|
||||
'HEAD',
|
||||
'/bucket/exists',
|
||||
);
|
||||
|
||||
expect(result).toBe(booleanResponse);
|
||||
});
|
||||
|
||||
it('should work with ILoadOptionsFunctions context', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const jsonResponse = '["option1", "option2", "option3"]';
|
||||
|
||||
mockLoadOptionsFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
jsonResponse,
|
||||
);
|
||||
mockLoadOptionsFunctions.getNode.mockReturnValue({
|
||||
id: 'load-node',
|
||||
name: 'Load Node',
|
||||
type: 'n8n-nodes-base.aws',
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const result = await awsApiRequestREST.call(
|
||||
mockLoadOptionsFunctions,
|
||||
'ec2',
|
||||
'GET',
|
||||
'/instances',
|
||||
);
|
||||
|
||||
expect(result).toEqual(['option1', 'option2', 'option3']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Propagation', () => {
|
||||
it('should propagate errors from awsApiRequest', async () => {
|
||||
const apiError = new Error('API request failed');
|
||||
mockExecuteFunctions.getCredentials.mockRejectedValue(apiError);
|
||||
|
||||
await expect(
|
||||
awsApiRequestREST.call(mockExecuteFunctions, 's3', 'GET', '/test'),
|
||||
).rejects.toThrow('API request failed');
|
||||
});
|
||||
|
||||
it('should handle malformed JSON gracefully', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const malformedJson = '{"incomplete": json';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
malformedJson,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestREST.call(
|
||||
mockExecuteFunctions,
|
||||
'lambda',
|
||||
'POST',
|
||||
'/test',
|
||||
);
|
||||
|
||||
expect(result).toBe(malformedJson);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('awsApiRequestSOAP', () => {
|
||||
describe('XML Response Handling', () => {
|
||||
it('should parse valid XML response', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const xmlResponse = '<response><status>success</status><data>test</data></response>';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
xmlResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestSOAP.call(
|
||||
mockExecuteFunctions,
|
||||
'ses',
|
||||
'POST',
|
||||
'/send-email',
|
||||
'<email>test</email>',
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
response: {
|
||||
status: 'success',
|
||||
data: 'test',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return raw response when XML parsing fails', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const invalidXmlResponse = 'Not valid XML content';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
invalidXmlResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestSOAP.call(
|
||||
mockExecuteFunctions,
|
||||
'ses',
|
||||
'POST',
|
||||
'/send-email',
|
||||
);
|
||||
|
||||
expect(result).toBe(invalidXmlResponse);
|
||||
});
|
||||
|
||||
it('should handle empty XML response', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const emptyResponse = '';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
emptyResponse,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestSOAP.call(mockExecuteFunctions, 'ses', 'GET', '/status');
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle complex nested XML with actual parsing', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const complexXml =
|
||||
'<ListQueuesResult><QueueUrl>url1</QueueUrl><QueueUrl>url2</QueueUrl></ListQueuesResult>';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
complexXml,
|
||||
);
|
||||
|
||||
const result = await awsApiRequestSOAP.call(
|
||||
mockExecuteFunctions,
|
||||
'sqs',
|
||||
'GET',
|
||||
'/list-queues',
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
ListQueuesResult: {
|
||||
QueueUrl: ['url1', 'url2'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with different function contexts', async () => {
|
||||
const mockCredentials = { region: 'us-east-1' };
|
||||
const xmlResponse = '<options><item>opt1</item><item>opt2</item></options>';
|
||||
|
||||
mockLoadOptionsFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
xmlResponse,
|
||||
);
|
||||
mockLoadOptionsFunctions.getNode.mockReturnValue({
|
||||
id: 'load-node',
|
||||
name: 'Load Node',
|
||||
type: 'n8n-nodes-base.aws',
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const result = await awsApiRequestSOAP.call(
|
||||
mockLoadOptionsFunctions,
|
||||
'ses',
|
||||
'GET',
|
||||
'/options',
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
options: {
|
||||
item: ['opt1', 'opt2'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should propagate errors from awsApiRequest', async () => {
|
||||
const apiError = new Error('SOAP API request failed');
|
||||
mockExecuteFunctions.getCredentials.mockRejectedValue(apiError);
|
||||
|
||||
await expect(
|
||||
awsApiRequestSOAP.call(mockExecuteFunctions, 'ses', 'POST', '/test'),
|
||||
).rejects.toThrow('SOAP API request failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
export const credentials = {
|
||||
aws: {
|
||||
region: 'eu-central-1',
|
||||
accessKeyId: 'key',
|
||||
secretAccessKey: 'secret',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user