ваше сообщение коммита
This commit is contained in:
89
backend/tests/ragService.test.js
Normal file
89
backend/tests/ragService.test.js
Normal file
@@ -0,0 +1,89 @@
|
||||
// Принудительно устанавливаем URL для Docker-сети
|
||||
process.env.VECTOR_SEARCH_URL = 'http://vector-search:8001';
|
||||
|
||||
const vectorSearch = require('../services/vectorSearchClient');
|
||||
|
||||
const TEST_TABLE_ID = 'test_table_rag';
|
||||
|
||||
const rows = [
|
||||
{ row_id: '1', text: 'Что такое RAG?', metadata: { answer: 'Retrieval Augmented Generation', userTags: ['ai', 'ml'], product: 'A' } },
|
||||
{ row_id: '2', text: 'Что такое FAISS?', metadata: { answer: 'Facebook AI Similarity Search', userTags: ['ai', 'search'], product: 'B' } },
|
||||
{ row_id: '3', text: 'Что такое Ollama?', metadata: { answer: 'Локальный inference LLM', userTags: ['llm'], product: 'A' } },
|
||||
];
|
||||
|
||||
describe('vectorSearchClient integration (vector-search)', () => {
|
||||
before(async () => {
|
||||
console.log('Загружаем тестовые данные...');
|
||||
console.log('VECTOR_SEARCH_URL:', process.env.VECTOR_SEARCH_URL);
|
||||
await vectorSearch.rebuild(TEST_TABLE_ID, rows);
|
||||
console.log('Тестовые данные загружены');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
console.log('Очищаем тестовые данные...');
|
||||
await vectorSearch.remove(TEST_TABLE_ID, rows.map(r => r.row_id));
|
||||
console.log('Тестовые данные очищены');
|
||||
});
|
||||
|
||||
it('Поиск без фильтрации', async () => {
|
||||
const results = await vectorSearch.search(TEST_TABLE_ID, 'Что такое RAG?', 1);
|
||||
console.log('Результаты поиска:', results);
|
||||
if (!results || results.length === 0) throw new Error('Нет результатов поиска');
|
||||
if (results[0].metadata.answer !== 'Retrieval Augmented Generation') {
|
||||
throw new Error(`Ответ не совпадает: ${results[0].metadata.answer}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('Поиск с фильтрацией по тегу (должен найти FAISS)', async () => {
|
||||
const results = await vectorSearch.search(TEST_TABLE_ID, 'Что такое FAISS?', 3);
|
||||
console.log('Результаты поиска FAISS:', results);
|
||||
if (!results || results.length === 0) throw new Error('Нет результатов поиска');
|
||||
|
||||
// Фильтруем по тегу 'search'
|
||||
const filtered = results.filter(r => r.metadata.userTags && r.metadata.userTags.includes('search'));
|
||||
if (filtered.length === 0) throw new Error('Нет результатов с тегом search');
|
||||
if (filtered[0].metadata.answer !== 'Facebook AI Similarity Search') {
|
||||
throw new Error(`Ответ не совпадает: ${filtered[0].metadata.answer}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('Поиск с фильтрацией по продукту (должен найти Ollama)', async () => {
|
||||
const results = await vectorSearch.search(TEST_TABLE_ID, 'Что такое Ollama?', 3);
|
||||
console.log('Результаты поиска Ollama:', results);
|
||||
if (!results || results.length === 0) throw new Error('Нет результатов поиска');
|
||||
|
||||
// Фильтруем по продукту 'A'
|
||||
const filtered = results.filter(r => r.metadata.product === 'A');
|
||||
if (filtered.length === 0) throw new Error('Нет результатов с продуктом A');
|
||||
if (filtered[0].metadata.answer !== 'Локальный inference LLM') {
|
||||
throw new Error(`Ответ не совпадает: ${filtered[0].metadata.answer}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('Комбинированная фильтрация (тег+продукт)', async () => {
|
||||
const results = await vectorSearch.search(TEST_TABLE_ID, 'Что такое RAG?', 3);
|
||||
console.log('Результаты поиска RAG:', results);
|
||||
if (!results || results.length === 0) throw new Error('Нет результатов поиска');
|
||||
|
||||
// Фильтруем по тегу 'ai' и продукту 'A'
|
||||
const filtered = results.filter(r =>
|
||||
r.metadata.userTags && r.metadata.userTags.includes('ai') &&
|
||||
r.metadata.product === 'A'
|
||||
);
|
||||
if (filtered.length === 0) throw new Error('Нет результатов с тегом ai и продуктом A');
|
||||
if (filtered[0].metadata.answer !== 'Retrieval Augmented Generation') {
|
||||
throw new Error(`Ответ не совпадает: ${filtered[0].metadata.answer}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('Проверка порога score', async () => {
|
||||
const results = await vectorSearch.search(TEST_TABLE_ID, 'Что такое Ollama?', 3);
|
||||
console.log('Результаты поиска с порогом:', results);
|
||||
if (!results || results.length === 0) throw new Error('Нет результатов поиска');
|
||||
|
||||
// Проверяем, что есть результаты с хорошим score (близкие к 0)
|
||||
const goodScoreResults = results.filter(r => Math.abs(r.score) < 10);
|
||||
if (goodScoreResults.length === 0) throw new Error('Нет результатов с хорошим score');
|
||||
console.log('Результаты с хорошим score:', goodScoreResults.length);
|
||||
});
|
||||
});
|
||||
163
backend/tests/ragServiceFull.test.js
Normal file
163
backend/tests/ragServiceFull.test.js
Normal file
@@ -0,0 +1,163 @@
|
||||
// Временно устанавливаем URL для локального тестирования
|
||||
process.env.VECTOR_SEARCH_URL = 'http://localhost:8001';
|
||||
|
||||
const ragService = require('../services/ragService');
|
||||
const db = require('../db');
|
||||
|
||||
const TEST_TABLE_ID = 999999; // Используем числовой ID
|
||||
|
||||
describe('ragService full integration (DB + vector-search)', () => {
|
||||
before(async () => {
|
||||
console.log('Создаем тестовую таблицу и данные...');
|
||||
|
||||
// Создаем тестовую таблицу
|
||||
await db.getQuery()(`
|
||||
INSERT INTO user_tables (id, name, description)
|
||||
VALUES ($1, 'Test RAG Table', 'Test table for RAG integration')
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
`, [TEST_TABLE_ID]);
|
||||
|
||||
// Создаем колонки
|
||||
const columns = [
|
||||
{ id: 'col_question', name: 'Question', type: 'text', purpose: 'question' },
|
||||
{ id: 'col_answer', name: 'Answer', type: 'text', purpose: 'answer' },
|
||||
{ id: 'col_tags', name: 'Tags', type: 'text', purpose: 'userTags' },
|
||||
{ id: 'col_product', name: 'Product', type: 'text', purpose: 'product' }
|
||||
];
|
||||
|
||||
for (const col of columns) {
|
||||
await db.getQuery()(`
|
||||
INSERT INTO user_columns (id, table_id, name, type, options)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
`, [col.id, TEST_TABLE_ID, col.name, col.type, JSON.stringify({ purpose: col.purpose })]);
|
||||
}
|
||||
|
||||
// Создаем строки
|
||||
const rows = [
|
||||
{ id: 'row_1', question: 'Что такое RAG?', answer: 'Retrieval Augmented Generation', tags: 'ai,ml', product: 'A' },
|
||||
{ id: 'row_2', question: 'Что такое FAISS?', answer: 'Facebook AI Similarity Search', tags: 'ai,search', product: 'B' },
|
||||
{ id: 'row_3', question: 'Что такое Ollama?', answer: 'Локальный inference LLM', tags: 'llm', product: 'A' }
|
||||
];
|
||||
|
||||
for (const row of rows) {
|
||||
// Создаем строку
|
||||
await db.getQuery()(`
|
||||
INSERT INTO user_rows (id, table_id, name)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
`, [row.id, TEST_TABLE_ID, row.question]);
|
||||
|
||||
// Создаем значения ячеек
|
||||
await db.getQuery()(`
|
||||
INSERT INTO user_cell_values (row_id, column_id, value)
|
||||
VALUES ($1, $2, $3), ($1, $4, $5), ($1, $6, $7), ($1, $8, $9)
|
||||
ON CONFLICT (row_id, column_id) DO UPDATE SET value = EXCLUDED.value
|
||||
`, [
|
||||
row.id, 'col_question', row.question,
|
||||
row.id, 'col_answer', row.answer,
|
||||
row.id, 'col_tags', row.tags,
|
||||
row.id, 'col_product', row.product
|
||||
]);
|
||||
}
|
||||
|
||||
console.log('Тестовые данные созданы');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
console.log('Очищаем тестовые данные...');
|
||||
|
||||
// Удаляем значения ячеек
|
||||
await db.getQuery()(`
|
||||
DELETE FROM user_cell_values
|
||||
WHERE row_id IN (SELECT id FROM user_rows WHERE table_id = $1)
|
||||
`, [TEST_TABLE_ID]);
|
||||
|
||||
// Удаляем строки
|
||||
await db.getQuery()(`
|
||||
DELETE FROM user_rows WHERE table_id = $1
|
||||
`, [TEST_TABLE_ID]);
|
||||
|
||||
// Удаляем колонки
|
||||
await db.getQuery()(`
|
||||
DELETE FROM user_columns WHERE table_id = $1
|
||||
`, [TEST_TABLE_ID]);
|
||||
|
||||
// Удаляем таблицу
|
||||
await db.getQuery()(`
|
||||
DELETE FROM user_tables WHERE id = $1
|
||||
`, [TEST_TABLE_ID]);
|
||||
|
||||
console.log('Тестовые данные очищены');
|
||||
});
|
||||
|
||||
it('Полная интеграция: поиск без фильтрации', async () => {
|
||||
const result = await ragService.ragAnswer({
|
||||
tableId: TEST_TABLE_ID,
|
||||
userQuestion: 'Что такое RAG?'
|
||||
});
|
||||
|
||||
console.log('Результат RAG:', result);
|
||||
if (!result) throw new Error('Нет результата');
|
||||
if (result.answer !== 'Retrieval Augmented Generation') {
|
||||
throw new Error(`Ответ не совпадает: ${result.answer}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('Полная интеграция: фильтрация по тегу', async () => {
|
||||
const result = await ragService.ragAnswer({
|
||||
tableId: TEST_TABLE_ID,
|
||||
userQuestion: 'Что такое FAISS?',
|
||||
userTags: ['search']
|
||||
});
|
||||
|
||||
console.log('Результат с фильтром по тегу:', result);
|
||||
if (!result) throw new Error('Нет результата');
|
||||
if (result.answer !== 'Facebook AI Similarity Search') {
|
||||
throw new Error(`Ответ не совпадает: ${result.answer}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('Полная интеграция: фильтрация по продукту', async () => {
|
||||
const result = await ragService.ragAnswer({
|
||||
tableId: TEST_TABLE_ID,
|
||||
userQuestion: 'Что такое Ollama?',
|
||||
product: 'A'
|
||||
});
|
||||
|
||||
console.log('Результат с фильтром по продукту:', result);
|
||||
if (!result) throw new Error('Нет результата');
|
||||
if (result.answer !== 'Локальный inference LLM') {
|
||||
throw new Error(`Ответ не совпадает: ${result.answer}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('Полная интеграция: комбинированная фильтрация', async () => {
|
||||
const result = await ragService.ragAnswer({
|
||||
tableId: TEST_TABLE_ID,
|
||||
userQuestion: 'Что такое RAG?',
|
||||
userTags: ['ai'],
|
||||
product: 'A'
|
||||
});
|
||||
|
||||
console.log('Результат с комбинированной фильтрацией:', result);
|
||||
if (!result) throw new Error('Нет результата');
|
||||
if (result.answer !== 'Retrieval Augmented Generation') {
|
||||
throw new Error(`Ответ не совпадает: ${result.answer}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('Полная интеграция: проверка порога score', async () => {
|
||||
const result = await ragService.ragAnswer({
|
||||
tableId: TEST_TABLE_ID,
|
||||
userQuestion: 'Что такое Ollama?',
|
||||
threshold: 0.95
|
||||
});
|
||||
|
||||
console.log('Результат с высоким порогом:', result);
|
||||
// С высоким порогом может не быть результата, это нормально
|
||||
if (result && result.score < 0.95) {
|
||||
throw new Error(`Score слишком низкий: ${result.score}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
35
backend/tests/vectorSearchClient.test.js
Normal file
35
backend/tests/vectorSearchClient.test.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const vectorSearch = require('../services/vectorSearchClient');
|
||||
|
||||
const TEST_TABLE_ID = 'test_table_1';
|
||||
|
||||
const rows = [
|
||||
{ row_id: '1', text: 'Как тебя зовут?', metadata: { answer: 'Алексей' } },
|
||||
{ row_id: '2', text: 'Где ты живёшь?', metadata: { answer: 'Москва' } }
|
||||
];
|
||||
|
||||
describe('Vector Search Service Integration', () => {
|
||||
afterAll(async () => {
|
||||
// Очистить тестовые данные
|
||||
await vectorSearch.remove(TEST_TABLE_ID, rows.map(r => r.row_id));
|
||||
});
|
||||
|
||||
test('Upsert and search', async () => {
|
||||
await vectorSearch.upsert(TEST_TABLE_ID, rows);
|
||||
const results = await vectorSearch.search(TEST_TABLE_ID, 'Как зовут?', 1);
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
expect(results[0].metadata.answer).toBe('Алексей');
|
||||
});
|
||||
|
||||
test('Delete', async () => {
|
||||
await vectorSearch.remove(TEST_TABLE_ID, ['1']);
|
||||
const results = await vectorSearch.search(TEST_TABLE_ID, 'Как зовут?', 1);
|
||||
expect(results.length === 0 || results[0].metadata.answer !== 'Алексей').toBe(true);
|
||||
});
|
||||
|
||||
test('Rebuild', async () => {
|
||||
await vectorSearch.rebuild(TEST_TABLE_ID, [rows[1]]);
|
||||
const results = await vectorSearch.search(TEST_TABLE_ID, 'Где ты живёшь?', 1);
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
expect(results[0].metadata.answer).toBe('Москва');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user