mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-27 14:57:21 +02:00
374 lines
11 KiB
TypeScript
374 lines
11 KiB
TypeScript
import type {
|
|
IExecuteSingleFunctions,
|
|
IN8nHttpFullResponse,
|
|
INodeExecutionData,
|
|
} from 'n8n-workflow';
|
|
import { NodeApiError } from 'n8n-workflow';
|
|
|
|
import {
|
|
agentErrorPostReceive,
|
|
embeddingsErrorPostReceive,
|
|
searchErrorPostReceive,
|
|
sendErrorPostReceive,
|
|
} from '../GenericFunctions';
|
|
|
|
// Mock implementation for `this` in `sendErrorPostReceive`
|
|
const mockExecuteSingleFunctions = {
|
|
getNode: () => ({
|
|
name: 'Mock Node',
|
|
type: 'mock-type',
|
|
position: [0, 0],
|
|
}),
|
|
} as unknown as IExecuteSingleFunctions;
|
|
|
|
describe('Generic Functions', () => {
|
|
describe('sendErrorPostReceive', () => {
|
|
let testData: INodeExecutionData[];
|
|
let testResponse: IN8nHttpFullResponse;
|
|
|
|
beforeEach(() => {
|
|
testData = [{ json: {} }];
|
|
testResponse = { statusCode: 200, headers: {}, body: {} };
|
|
});
|
|
|
|
it('should return data if status code is not 4xx or 5xx', async () => {
|
|
const result = await sendErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
testResponse,
|
|
);
|
|
expect(result).toEqual(testData);
|
|
});
|
|
|
|
it('should throw NodeApiError if status code is 4xx', async () => {
|
|
testResponse.statusCode = 400;
|
|
await expect(
|
|
sendErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrow(NodeApiError);
|
|
});
|
|
|
|
it('should throw NodeApiError if status code is 5xx', async () => {
|
|
testResponse.statusCode = 500;
|
|
await expect(
|
|
sendErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrow(NodeApiError);
|
|
});
|
|
|
|
it('should throw NodeApiError with "Invalid model" message if error type is invalid_model', async () => {
|
|
const errorResponse = {
|
|
statusCode: 400,
|
|
body: {
|
|
error: {
|
|
type: 'invalid_model',
|
|
message: 'Invalid model type provided',
|
|
},
|
|
},
|
|
};
|
|
|
|
await expect(
|
|
sendErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
errorResponse as unknown as IN8nHttpFullResponse,
|
|
),
|
|
).rejects.toThrowError(
|
|
new NodeApiError(mockExecuteSingleFunctions.getNode(), errorResponse.body, {
|
|
message: 'Invalid model',
|
|
description:
|
|
'The model is not valid. Permitted models can be found in the documentation at https://docs.perplexity.ai/guides/model-cards.',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('should throw NodeApiError with "Invalid parameter" message if error type is invalid_parameter', async () => {
|
|
const errorResponse = {
|
|
statusCode: 400,
|
|
body: {
|
|
error: {
|
|
type: 'invalid_parameter',
|
|
message: 'Invalid parameter provided',
|
|
},
|
|
},
|
|
};
|
|
|
|
await expect(
|
|
sendErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
errorResponse as unknown as IN8nHttpFullResponse,
|
|
),
|
|
).rejects.toThrowError(
|
|
new NodeApiError(mockExecuteSingleFunctions.getNode(), errorResponse.body, {
|
|
message: 'Invalid parameter provided.',
|
|
description:
|
|
'Please check all input parameters and ensure they are correctly formatted. Valid values can be found in the documentation at https://docs.perplexity.ai/api-reference/chat-completions.',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('should handle "invalid_model" error with itemIndex', async () => {
|
|
const errorResponse = {
|
|
statusCode: 400,
|
|
body: {
|
|
error: {
|
|
type: 'invalid_model',
|
|
message: 'Invalid model',
|
|
itemIndex: 0,
|
|
},
|
|
},
|
|
};
|
|
|
|
await expect(
|
|
sendErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
errorResponse as unknown as IN8nHttpFullResponse,
|
|
),
|
|
).rejects.toThrowError(
|
|
new NodeApiError(mockExecuteSingleFunctions.getNode(), errorResponse.body, {
|
|
message: 'Invalid model',
|
|
description: 'Permitted models documentation...',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('should handle "invalid_parameter" error with non-string message', async () => {
|
|
const errorResponse = {
|
|
statusCode: 400,
|
|
body: {
|
|
error: {
|
|
type: 'invalid_parameter',
|
|
message: { detail: 'Invalid param' },
|
|
},
|
|
},
|
|
};
|
|
|
|
await expect(
|
|
sendErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
errorResponse as unknown as IN8nHttpFullResponse,
|
|
),
|
|
).rejects.toThrowError(
|
|
new NodeApiError(mockExecuteSingleFunctions.getNode(), errorResponse.body, {
|
|
message: 'An unexpected issue occurred.',
|
|
description: 'Please check parameters...',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('should throw generic error for unknown error type', async () => {
|
|
const errorResponse = {
|
|
statusCode: 500,
|
|
body: {
|
|
error: {
|
|
type: 'server_error',
|
|
message: 'Internal server error',
|
|
},
|
|
},
|
|
};
|
|
|
|
await expect(
|
|
sendErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
errorResponse as unknown as IN8nHttpFullResponse,
|
|
),
|
|
).rejects.toThrowError(
|
|
new NodeApiError(mockExecuteSingleFunctions.getNode(), errorResponse.body, {
|
|
message: 'Internal server error.',
|
|
description: 'Refer to API documentation...',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('should include itemIndex in error message when present', async () => {
|
|
const errorResponse = {
|
|
statusCode: 400,
|
|
body: {
|
|
error: {
|
|
type: 'other_error',
|
|
message: 'Error with item',
|
|
itemIndex: 2,
|
|
},
|
|
},
|
|
};
|
|
|
|
await expect(
|
|
sendErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
errorResponse as unknown as IN8nHttpFullResponse,
|
|
),
|
|
).rejects.toThrowError(
|
|
new NodeApiError(mockExecuteSingleFunctions.getNode(), errorResponse.body, {
|
|
message: 'Error with item [Item 2].',
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('agentErrorPostReceive', () => {
|
|
let testData: INodeExecutionData[];
|
|
let testResponse: IN8nHttpFullResponse;
|
|
|
|
beforeEach(() => {
|
|
testData = [{ json: {} }];
|
|
testResponse = { statusCode: 200, headers: {}, body: {} };
|
|
});
|
|
|
|
it('should return data if status code is not 4xx or 5xx', async () => {
|
|
const result = await agentErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
testResponse,
|
|
);
|
|
expect(result).toEqual(testData);
|
|
});
|
|
|
|
it('should throw NodeApiError if status code is 4xx', async () => {
|
|
testResponse.statusCode = 400;
|
|
testResponse.body = { error: { message: 'Bad request' } };
|
|
await expect(
|
|
agentErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrow(NodeApiError);
|
|
});
|
|
|
|
it('should throw NodeApiError if status code is 5xx', async () => {
|
|
testResponse.statusCode = 500;
|
|
testResponse.body = { error: { message: 'Server error' } };
|
|
await expect(
|
|
agentErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrow(NodeApiError);
|
|
});
|
|
|
|
it('should use statusMessage as fallback when error message is not a string', async () => {
|
|
const errorResponse = {
|
|
statusCode: 400,
|
|
statusMessage: 'Bad Request',
|
|
headers: {},
|
|
body: { error: { message: { detail: 'complex error' } } },
|
|
};
|
|
await expect(
|
|
agentErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
errorResponse as unknown as IN8nHttpFullResponse,
|
|
),
|
|
).rejects.toThrow(NodeApiError);
|
|
});
|
|
|
|
it('should include agent API documentation in the description', async () => {
|
|
testResponse.statusCode = 400;
|
|
testResponse.body = { error: { message: 'Invalid param' } };
|
|
await expect(
|
|
agentErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrowError(
|
|
expect.objectContaining({
|
|
description: expect.stringContaining(
|
|
'https://docs.perplexity.ai/api-reference/agent-post',
|
|
),
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('searchErrorPostReceive', () => {
|
|
let testData: INodeExecutionData[];
|
|
let testResponse: IN8nHttpFullResponse;
|
|
|
|
beforeEach(() => {
|
|
testData = [{ json: {} }];
|
|
testResponse = { statusCode: 200, headers: {}, body: {} };
|
|
});
|
|
|
|
it('should return data if status code is not 4xx or 5xx', async () => {
|
|
const result = await searchErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
testResponse,
|
|
);
|
|
expect(result).toEqual(testData);
|
|
});
|
|
|
|
it('should throw NodeApiError if status code is 4xx', async () => {
|
|
testResponse.statusCode = 400;
|
|
testResponse.body = { error: { message: 'Bad request' } };
|
|
await expect(
|
|
searchErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrow(NodeApiError);
|
|
});
|
|
|
|
it('should throw NodeApiError if status code is 5xx', async () => {
|
|
testResponse.statusCode = 500;
|
|
testResponse.body = { error: { message: 'Server error' } };
|
|
await expect(
|
|
searchErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrow(NodeApiError);
|
|
});
|
|
|
|
it('should include search API documentation in the description', async () => {
|
|
testResponse.statusCode = 400;
|
|
testResponse.body = { error: { message: 'Invalid query' } };
|
|
await expect(
|
|
searchErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrowError(
|
|
expect.objectContaining({
|
|
description: expect.stringContaining(
|
|
'https://docs.perplexity.ai/api-reference/search-post',
|
|
),
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('embeddingsErrorPostReceive', () => {
|
|
let testData: INodeExecutionData[];
|
|
let testResponse: IN8nHttpFullResponse;
|
|
|
|
beforeEach(() => {
|
|
testData = [{ json: {} }];
|
|
testResponse = { statusCode: 200, headers: {}, body: {} };
|
|
});
|
|
|
|
it('should return data if status code is not 4xx or 5xx', async () => {
|
|
const result = await embeddingsErrorPostReceive.call(
|
|
mockExecuteSingleFunctions,
|
|
testData,
|
|
testResponse,
|
|
);
|
|
expect(result).toEqual(testData);
|
|
});
|
|
|
|
it('should throw NodeApiError if status code is 4xx', async () => {
|
|
testResponse.statusCode = 400;
|
|
testResponse.body = { error: { message: 'Bad request' } };
|
|
await expect(
|
|
embeddingsErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrow(NodeApiError);
|
|
});
|
|
|
|
it('should throw NodeApiError if status code is 5xx', async () => {
|
|
testResponse.statusCode = 500;
|
|
testResponse.body = { error: { message: 'Server error' } };
|
|
await expect(
|
|
embeddingsErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrow(NodeApiError);
|
|
});
|
|
|
|
it('should include embeddings API documentation in the description', async () => {
|
|
testResponse.statusCode = 400;
|
|
testResponse.body = { error: { message: 'Invalid input' } };
|
|
await expect(
|
|
embeddingsErrorPostReceive.call(mockExecuteSingleFunctions, testData, testResponse),
|
|
).rejects.toThrowError(
|
|
expect.objectContaining({
|
|
description: expect.stringContaining(
|
|
'https://docs.perplexity.ai/api-reference/embeddings-post',
|
|
),
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
});
|