v0.5.11: зачистка мёртвого кода + новые интеграционные тесты

Удалено:
- src/routes/*.js (5 файлов) — старая монолитная архитектура
- views/dev-login.ejs — старый dev-логин
- tests/debug-session.js — тесты старой архитектуры

Добавлено:
- safeReturn в src/auth.js
- Новые интеграционные тесты tests/integration.js (67 тестов, UI+API)

Обновлено:
- tests/run-tests.js — убраны старые require, добавлены новые модули
- tests/tz-compliance.js — /exp → /api/v1/entries/export
- server.js — убран require('./src/routes/exp')
This commit is contained in:
2026-06-02 18:04:33 +03:00
parent 4694b7e8a6
commit 15b458eb35
12 changed files with 357 additions and 1767 deletions
+9 -62
View File
@@ -45,10 +45,14 @@ const modules = [
'src/validators.js',
'src/middleware/rateLimit.js',
'src/middleware/csrf.js',
'src/routes/export.js',
'src/routes/auth.js',
'src/routes/entries.js',
'src/routes/admin.js',
'src/api/index.js',
'src/api/routes/entries.js',
'src/api/routes/admin.js',
'ui/index.js',
'ui/routes/auth.js',
'ui/routes/entries.js',
'ui/routes/admin.js',
'ui/routes/export.js',
'server.js',
];
@@ -133,8 +137,7 @@ test('no overlap public', overlaps('8.8.8.0/24', '9.9.9.0/24'), false);
process.env.DEV_MODE = 'true';
process.env.ADMIN_CLIENT_ID = 'WZ01112';
const { initAuth, requireAdmin } = require('../src/auth');
const { safeReturn } = require('../src/routes/auth');
const { initAuth, requireAdmin, safeReturn } = require('../src/auth');
const csrfMiddleware = require('../src/middleware/csrf');
(async () => {
@@ -219,63 +222,7 @@ const { doubleCsrfProtection: dp, generateCsrfToken: gct } = csrfMiddleware.init
test('initCsrf returns doubleCsrfProtection fn', typeof dp, 'function');
test('initCsrf returns generateCsrfToken fn', typeof gct, 'function');
// ────────────────────────────────────────────────────────────────
// 7. Export auth isolation — req.user влияет на фильтр компании
// ────────────────────────────────────────────────────────────────
section('Export auth isolation');
// Тестируем логику без реального HTTP — проверяем что createRouter не бросает при require
const exportModule = require('../src/routes/export');
test('export module exports createRouter', typeof exportModule.createRouter, 'function');
// Логика изоляции: обычный пользователь получает companyId из q.getOrCreateCompany,
// admin без ?company → companyId = null (все компании).
// Проверяем через минимальный stub — без реальной БД.
let exportCompanyIdCaptured = null;
const stubQ = {
getOrCreateCompany: async (clientId) => ({ id: 42, clientId }),
getExportCIDRs: async (cid) => { exportCompanyIdCaptured = cid; return []; },
};
const exportRouter = exportModule.createRouter({
q: stubQ,
exportLimiter: (req, res, next) => next(),
aggregateCIDRs: (x) => x,
});
// Симулируем GET /export для обычного пользователя
const mockReqUser = {
user: { isAdmin: false, clientId: 'WZ01325', companyName: 'TestCo' },
query: {},
method: 'GET',
path: '/export',
};
const mockResUser = {
setHeader: () => {},
send: () => {},
};
// Найдём обработчик /export в router.stack
const exportLayer = exportRouter.stack.find(l => l.route && l.route.path === '/export');
test('export route registered', !!exportLayer, true);
if (exportLayer) {
// Запускаем handler напрямую
const handlers = exportLayer.route.stack.map(l => l.handle);
// exportLimiter (stub пропускает) + async handler
exportCompanyIdCaptured = 'NOT_CALLED';
await handlers[handlers.length - 1](mockReqUser, mockResUser, () => {});
test('user export: companyId filtered (42)', exportCompanyIdCaptured, 42);
// Симулируем GET /export для admin без ?company
exportCompanyIdCaptured = 'NOT_CALLED';
const mockReqAdmin = {
user: { isAdmin: true, clientId: 'WZ01112', companyName: 'Admin' },
query: {},
method: 'GET',
path: '/export',
};
await handlers[handlers.length - 1](mockReqAdmin, mockResUser, () => {});
test('admin export: no filter (null)', exportCompanyIdCaptured, null);
}
// ────────────────────────────────────────────────────────────────
// Итог