feat: solution for 'Повторный экзамен #2: Сравнительный обзор 3 сущностей (Tavily)'

This commit is contained in:
2026-07-01 11:31:54 +03:00
parent 29e3e8ba1d
commit f1bd10bd09
9 changed files with 321 additions and 247 deletions
+54 -31
View File
@@ -1,48 +1,71 @@
const { compareEntities } = require('../src/index');
const axios = require('axios');
jest.mock('axios');
const nock = require('nock');
const { compareEntities } = require('../src/compare');
require('dotenv').config({ path: '.env.example' });
describe('compareEntities', () => {
const mockResponse = {
status: 200,
data: {
results: [
{ title: 'Apple vs Samsung vs Google', content: 'Apple leads in design...' },
],
},
};
const baseUrl = 'https://api.tavily.com';
const apiKey = 'test-key';
beforeEach(() => {
axios.post.mockResolvedValue(mockResponse);
beforeAll(() => {
process.env.TAVILY_API_KEY = apiKey;
});
afterEach(() => {
jest.clearAllMocks();
nock.cleanAll();
});
it('should return data from Tavily API', async () => {
const data = await compareEntities('Apple', 'Samsung', 'Google');
expect(data).toEqual(mockResponse.data);
expect(axios.post).toHaveBeenCalledWith(
'https://api.tavily.com/search',
expect.objectContaining({
query: expect.stringContaining('Apple'),
}),
expect.any(Object)
test('returns ranked results based on number of results', async () => {
nock(baseUrl)
.post('/search', { query: 'entity1' })
.reply(200, { results: [{}, {}] }); // 2 results
nock(baseUrl)
.post('/search', { query: 'entity2' })
.reply(200, { results: [{}, {}, {}] }); // 3 results
nock(baseUrl)
.post('/search', { query: 'entity3' })
.reply(200, { results: [{}, {}] }); // 2 results
const result = await compareEntities('entity1', 'entity2', 'entity3');
expect(result.ranked[0].entity).toBe('entity2');
expect(result.ranked[1].entity).toBe('entity1');
expect(result.ranked[2].entity).toBe('entity3');
});
test('throws error on empty entity', async () => {
await expect(compareEntities('', 'b', 'c')).rejects.toThrow(
'All entities must be non-empty strings.'
);
});
it('should throw error if any entity is missing', async () => {
await expect(compareEntities('Apple', '', 'Google')).rejects.toThrow(
'All three entity names must be provided.'
test('handles API error', async () => {
nock(baseUrl)
.post('/search', { query: 'entity1' })
.reply(401, { error: 'Invalid API key' });
nock(baseUrl)
.post('/search', { query: 'entity2' })
.reply(200, { results: [] });
nock(baseUrl)
.post('/search', { query: 'entity3' })
.reply(200, { results: [] });
await expect(compareEntities('entity1', 'entity2', 'entity3')).rejects.toThrow(
'Tavily API error: 401 Invalid API key'
);
});
it('should propagate API errors', async () => {
axios.post.mockRejectedValue(new Error('Network error'));
await expect(compareEntities('Apple', 'Samsung', 'Google')).rejects.toThrow(
'Network error'
test('handles network failure', async () => {
nock(baseUrl)
.post('/search', { query: 'entity1' })
.replyWithError('Network down');
nock(baseUrl)
.post('/search', { query: 'entity2' })
.reply(200, { results: [] });
nock(baseUrl)
.post('/search', { query: 'entity3' })
.reply(200, { results: [] });
await expect(compareEntities('entity1', 'entity2', 'entity3')).rejects.toThrow(
'Network error: Network down'
);
});
});