// Tests/api_tests.mjs // Тесты backend API — запускаются через: node Tests/api_tests.mjs // Требует: запущенный backend (npm run dev в backend/) и PostgreSQL const API = 'http://localhost:3001/api'; let passed = 0; let failed = 0; async function test(name, fn) { try { await fn(); console.log(` ✅ PASS: ${name}`); passed++; } catch (err) { console.log(` ❌ FAIL: ${name}`); console.log(` ${err.message}`); failed++; } } function assert(condition, message) { if (!condition) throw new Error(message || 'Assertion failed'); } async function fetchJSON(url, options = {}) { const res = await fetch(url, { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options, body: options.body ? JSON.stringify(options.body) : undefined, }); const data = await res.json(); return { status: res.status, data }; } // ─── Группа 1: Health Check ──────────────────────────────────────────────── console.log('\n🔍 Группа 1: Health Check'); await test('GET /api/health возвращает 200', async () => { const { status, data } = await fetchJSON(`${API}/health`); assert(status === 200, `Ожидался 200, получен ${status}`); assert(data.success === true, 'success !== true'); assert(data.data.status === 'ok', `status !== 'ok'`); }); // ─── Группа 2: Auth — request-otp ────────────────────────────────────────── console.log('\n🔍 Группа 2: Auth — request-otp'); // Уникальный номер на каждый запуск — обходим rate-limit (5 минут) const testPhone = `+7900${Date.now().toString().slice(-7)}`; await test('POST /api/auth/request-otp с телефоном возвращает success', async () => { const { status, data } = await fetchJSON(`${API}/auth/request-otp`, { method: 'POST', body: { phone: testPhone }, }); assert(status === 200, `Ожидался 200, получен ${status}`); assert(data.success === true, `success=false, errors: ${JSON.stringify(data.errors)}`); }); await test('POST /api/auth/request-otp без телефона возвращает 400', async () => { const { status, data } = await fetchJSON(`${API}/auth/request-otp`, { method: 'POST', body: {}, }); assert(status === 400, `Ожидался 400, получен ${status}`); assert(data.success === false, 'success должен быть false для невалидных данных'); }); // ─── Группа 3: Auth — verify-otp ─────────────────────────────────────────── console.log('\n🔍 Группа 3: Auth — verify-otp'); await test('POST /api/auth/verify-otp с неверным кодом возвращает ошибку', async () => { const { status, data } = await fetchJSON(`${API}/auth/verify-otp`, { method: 'POST', body: { phone: testPhone, otp: '000000' }, }); // Неверный код: сервер должен вернуть 400 или 200 с success=false assert(data.success === false, `Ожидался success=false для неверного OTP`); }); await test('POST /api/auth/verify-otp без otp возвращает 400', async () => { const { status, data } = await fetchJSON(`${API}/auth/verify-otp`, { method: 'POST', body: { phone: testPhone }, }); assert(status === 400, `Ожидался 400, получен ${status}`); }); // ─── Группа 4: Защищённые маршруты ───────────────────────────────────────── console.log('\n🔍 Группа 4: Защищённые маршруты (без токена)'); await test('Запрос к несуществующему API маршруту возвращает 404', async () => { const res = await fetch(`${API}/nonexistent`); assert(res.status === 404, `Ожидался 404, получен ${res.status}`); const data = await res.json(); assert(data.success === false, 'success должен быть false для 404'); }); // ─── Итог ────────────────────────────────────────────────────────────────── console.log(`\n${'─'.repeat(50)}`); console.log(`Итог: ${passed} прошло, ${failed} провалено`); if (failed > 0) { console.log('⚠️ Есть проваленные тесты'); process.exit(1); } else { console.log('✅ Все тесты прошли успешно'); }