test: добавить тесты для matcher.js и aiAdapter.js
This commit is contained in:
parent
f07ae80874
commit
f6ca766531
172
test.js
Normal file
172
test.js
Normal file
@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Тесты для matcher.js и aiAdapter.js
|
||||
* Запуск: node test.js
|
||||
*/
|
||||
|
||||
const aiAdapter = require('./aiAdapter.js');
|
||||
const matcher = require('./matcher.js');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(`✓ ${name}`);
|
||||
passed++;
|
||||
} catch (error) {
|
||||
console.error(`✗ ${name}`);
|
||||
console.error(` ${error.message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) {
|
||||
throw new Error(message || 'Assertion failed');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Тесты aiAdapter.js:\n');
|
||||
|
||||
test('Извлечение требований из текста вакансии', () => {
|
||||
const text = 'Требуется JavaScript разработчик с опытом от 3 лет. Знание React обязательно.';
|
||||
const result = aiAdapter.generate('extractCriteria', { vacancyText: text });
|
||||
assert(result.criteria.length > 0, 'Критерии не извлечены');
|
||||
assert(result.criteria.some(c => c.label.toLowerCase().includes('javascript')), 'Не найдено JavaScript');
|
||||
assert(result.criteria.some(c => c.label.toLowerCase().includes('react')), 'Не найдено React');
|
||||
});
|
||||
|
||||
test('Пустой текст вакансии', () => {
|
||||
const result = aiAdapter.generate('extractCriteria', { vacancyText: '' });
|
||||
assert(result.error, 'Нет ошибки для пустого текста');
|
||||
});
|
||||
|
||||
test('Сопоставление резюме с критериями', () => {
|
||||
const criteria = [
|
||||
{ id: 'js', label: 'JavaScript', required: true, weight: 5, synonyms: ['js'], category: 'frontend' }
|
||||
];
|
||||
const resume = 'Опыт работы с JavaScript 3 года. Владею JS, React.';
|
||||
const result = aiAdapter.generate('matchResume', { criteria, resumeText: resume });
|
||||
assert(result.results.length === 1, 'Неверное количество результатов');
|
||||
assert(result.results[0].status === 'confirmed', 'JavaScript не подтверждён');
|
||||
assert(result.results[0].evidence.length > 0, 'Нет цитаты');
|
||||
});
|
||||
|
||||
test('Отрицание не считается подтверждением', () => {
|
||||
const criteria = [
|
||||
{ id: 'react', label: 'React', required: true, weight: 5, synonyms: [], category: 'frontend' }
|
||||
];
|
||||
const resume = 'Без опыта работы с React. Не владею React.';
|
||||
const result = aiAdapter.generate('matchResume', { criteria, resumeText: resume });
|
||||
assert(result.results[0].status !== 'confirmed', 'Отрицание проигнорировано');
|
||||
});
|
||||
|
||||
test('Синонимы работают', () => {
|
||||
const criteria = [
|
||||
{ id: 'js', label: 'JavaScript', required: true, weight: 5, synonyms: ['js', 'ecmascript'], category: 'frontend' }
|
||||
];
|
||||
const resume = 'Опыт коммерческой разработки на JS 2 года.';
|
||||
const result = aiAdapter.generate('matchResume', { criteria, resumeText: resume });
|
||||
assert(result.results[0].status === 'confirmed', 'Синоним JS не сработал');
|
||||
});
|
||||
|
||||
test('Генерация вопросов для интервью', () => {
|
||||
const unmatched = [
|
||||
{ criterionId: 'nodejs', label: 'Node.js', status: 'clarify', required: true, weight: 3 }
|
||||
];
|
||||
const result = aiAdapter.generate('generateQuestions', { unmatchedCriteria: unmatched });
|
||||
assert(result.questions.length > 0, 'Вопросы не сгенерированы');
|
||||
assert(result.questions[0].question.includes('Node.js'), 'Вопрос не содержит название навыка');
|
||||
});
|
||||
|
||||
console.log('\nТесты matcher.js:\n');
|
||||
|
||||
test('Расчёт покрытия', () => {
|
||||
const results = [
|
||||
{ status: 'confirmed', weight: 5 },
|
||||
{ status: 'confirmed', weight: 3 },
|
||||
{ status: 'notfound', weight: 2 }
|
||||
];
|
||||
const coverage = matcher.calculateCoverage(results);
|
||||
assert(coverage === 80, `Неверное покрытие: ${coverage} (ожидалось 80)`);
|
||||
});
|
||||
|
||||
test('Покрытие при пустом списке', () => {
|
||||
const coverage = matcher.calculateCoverage([]);
|
||||
assert(coverage === 0, `Неверное покрытие пустого списка: ${coverage}`);
|
||||
});
|
||||
|
||||
test('Статусы сопоставления', () => {
|
||||
const results = [
|
||||
{ status: 'confirmed' },
|
||||
{ status: 'clarify' },
|
||||
{ status: 'clarify' },
|
||||
{ status: 'notfound' }
|
||||
];
|
||||
const stats = matcher.getStatusStats(results);
|
||||
assert(stats.confirmed === 1, 'Неверно confirmed');
|
||||
assert(stats.clarify === 2, 'Неверно clarify');
|
||||
assert(stats.notfound === 1, 'Неверно notfound');
|
||||
assert(stats.total === 4, 'Неверно total');
|
||||
});
|
||||
|
||||
test('Добавление критерия', () => {
|
||||
const criteria = [];
|
||||
const newCriteria = matcher.addCustomCriterion(criteria, 'Тест', true, 4, ['синоним1', 'синоним2']);
|
||||
assert(newCriteria.length === 1, 'Критерий не добавлен');
|
||||
assert(newCriteria[0].label === 'Тест', 'Неверная метка');
|
||||
assert(newCriteria[0].weight === 4, 'Неверный вес');
|
||||
assert(newCriteria[0].synonyms.length === 2, 'Неверное количество синонимов');
|
||||
});
|
||||
|
||||
test('Удаление критерия', () => {
|
||||
const criteria = [
|
||||
{ id: '1', label: 'First' },
|
||||
{ id: '2', label: 'Second' }
|
||||
];
|
||||
const result = matcher.removeCriterion(criteria, '1');
|
||||
assert(result.length === 1, 'Критерий не удалён');
|
||||
assert(result[0].id === '2', 'Неверный критерий удалён');
|
||||
});
|
||||
|
||||
test('Обновление статуса', () => {
|
||||
const results = [
|
||||
{ criterionId: '1', status: 'notfound', note: '' }
|
||||
];
|
||||
const updated = matcher.updateCriterionStatus(results, '1', 'clarify', 'Нужно уточнить');
|
||||
assert(updated[0].status === 'clarify', 'Статус не обновлён');
|
||||
assert(updated[0].note === 'Нужно уточнить', 'Примечание не обновлено');
|
||||
});
|
||||
|
||||
test('Экспорт сводки', () => {
|
||||
const summary = matcher.exportSummary(
|
||||
'Текст вакансии',
|
||||
'Текст резюме',
|
||||
[{ id: '1', label: 'Требование' }],
|
||||
{ results: [{ label: 'Требование', required: true, weight: 5, status: 'confirmed', evidence: ['цитата'] }] },
|
||||
{ questions: [{ label: 'Требование', question: 'Вопрос?', followUp: [] }] }
|
||||
);
|
||||
assert(summary.coverage.percentage === 100, 'Неверное покрытие');
|
||||
assert(summary.results.length > 0, 'Нет результатов');
|
||||
assert(summary.interviewQuestions.length > 0, 'Нет вопросов');
|
||||
});
|
||||
|
||||
test('Форматирование экспорта в текст', () => {
|
||||
const summary = {
|
||||
exportedAt: new Date().toISOString(),
|
||||
coverage: { percentage: 80, confirmed: 4, toClarify: 1, notFound: 0, total: 5 },
|
||||
results: [{ requirement: 'JavaScript', type: 'Обязательное', weight: 5, status: 'Подтверждено', evidence: 'Опыт 3 года' }],
|
||||
interviewQuestions: []
|
||||
};
|
||||
const text = matcher.formatExportText(summary);
|
||||
assert(text.includes('HR CANDIDATE MATCHER'), 'Нет заголовка');
|
||||
assert(text.includes('80%'), 'Нет процента покрытия');
|
||||
assert(text.includes('JavaScript'), 'Нет требования');
|
||||
});
|
||||
|
||||
console.log(`\n${passed} passed, ${failed} failed`);
|
||||
|
||||
if (failed > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user