ty-produktovyy-dizayner-i-se/test.js

173 lines
7.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Тесты для 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);
}