v2: подробные комментарии во всех модулях — что, зачем, контракты
This commit is contained in:
+49
-19
@@ -1,14 +1,39 @@
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
// V2 — тестовый эндпоинт для CRUD
|
||||
// Принимает ?action=...&user=... → JSON-ответ
|
||||
// V2 — Тестовый слой
|
||||
//
|
||||
// ЭТО: Express-роутер для тестирования ВСЕХ слоёв через curl.
|
||||
// НЕ: production-код (отключается флагом ENABLE_TEST_API=false).
|
||||
//
|
||||
// ЗАЧЕМ:
|
||||
// 1. Тестировать слои (crud, db, validators) на реальной БД без KC-сессии.
|
||||
// 2. Эмулировать поведение юзера (userFlow).
|
||||
// 3. Нагрузочное тестирование (chaos — 40 параллельных операций).
|
||||
// 4. Очистка тестовых данных (cleanup).
|
||||
//
|
||||
// МОК-ЮЗЕРЫ: u1, u2, imp — эмулируют реальных пользователей IAM.
|
||||
// u1 — обычный юзер, одна компания (WZ10001)
|
||||
// u2 — юзер с двумя компаниями (WZ20001, WZ20002)
|
||||
// imp — админ под имперсонацией (WZ01112 → WZ03709)
|
||||
//
|
||||
// ACTIONS (GET ?action=...):
|
||||
// add/edit/delete/list — CRUD через crud/
|
||||
// audit — аудит через db/queries
|
||||
// limit — лимит компании
|
||||
// switch — переключение компании
|
||||
// cleanup — удаление тестовых данных
|
||||
// userFlow — полная эмуляция юзера с мок-сессией
|
||||
//
|
||||
// CHAOS (GET /chaos):
|
||||
// 4 компании × 10 записей = 40 параллельных Promise.all.
|
||||
// Проверяет: дубликаты, лимиты, аудит.
|
||||
// ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
const express = require('express');
|
||||
const crud = require('../crud');
|
||||
const q = require('../db/queries');
|
||||
const { pool } = require('../db');
|
||||
const crud = require('../crud'); // API-слой БД
|
||||
const q = require('../db/queries'); // прямой доступ для аудита/лимитов
|
||||
const { pool } = require('../db'); // прямой доступ для cleanup
|
||||
|
||||
// Мок-юзеры
|
||||
// Мок-юзеры — эмулируют данные из IAM после KC-входа
|
||||
const USERS = {
|
||||
u1: { email:'u1@t.ru', clientId:'WZ10001', activeClientId:'WZ10001', allClientIds:['WZ10001'], companyName:'Одна', isAdmin:false, profiles:[{client_id:'WZ10001',company_name:'Одна'}] },
|
||||
u2: { email:'u2@t.ru', clientId:'WZ20001', activeClientId:'WZ20002', allClientIds:['WZ20001','WZ20002'], companyName:'Вторая', isAdmin:false, profiles:[{client_id:'WZ20001',company_name:'Первая'},{client_id:'WZ20002',company_name:'Вторая'}] },
|
||||
@@ -18,51 +43,53 @@ const USERS = {
|
||||
function createTestRouter() {
|
||||
const router = express.Router();
|
||||
|
||||
// ── GET / — главный тестовый эндпоинт (?action=...) ─────────────────
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const action = req.query.action;
|
||||
const user = USERS[req.query.user || 'u1'];
|
||||
if (!user) return res.json({ ok: false, error: 'unknown user' });
|
||||
|
||||
// Контекст из мок-юзера (аналог resolveContext)
|
||||
const email = user.isImpersonated ? user.originalUserEmail : user.email;
|
||||
const clientId = user.isImpersonated ? user.impersonatedCompanyId : user.activeClientId;
|
||||
const company = await q.getOrCreateCompany(clientId, user.companyName || clientId);
|
||||
const impBy = user.isImpersonated ? user.email : null;
|
||||
|
||||
switch (action) {
|
||||
// ── LIST ──
|
||||
// ── LIST: список записей компании ─────────────────────────
|
||||
case 'list': {
|
||||
const { entries, used, limit } = await crud.list(clientId, req.query.deleted === '1');
|
||||
return res.json({ ok: true, entries, count: entries.length, used, limit }); }
|
||||
|
||||
// ── ADD ──
|
||||
// ── ADD: добавить CIDR (crud.add → validate → db) ────────
|
||||
case 'add': {
|
||||
const r = await crud.add(clientId, req.query.cidr || '', req.query.comment || '', email, impBy);
|
||||
return res.json({ ok: true, entry: r.entry, wasNormalized: r.wasNormalized });
|
||||
}
|
||||
|
||||
// ── EDIT ──
|
||||
// ── EDIT: изменить существующую запись ────────────────────
|
||||
case 'edit': {
|
||||
const r = await crud.edit(parseInt(req.query.id), clientId, req.query.cidr || '', req.query.comment || '', email, impBy);
|
||||
return res.json({ ok: true, entry: r.entry, wasNormalized: r.wasNormalized });
|
||||
}
|
||||
|
||||
// ── DELETE ──
|
||||
// ── DELETE: soft delete ───────────────────────────────────
|
||||
case 'delete':
|
||||
await crud.remove(parseInt(req.query.id), clientId, email, impBy);
|
||||
return res.json({ ok: true });
|
||||
|
||||
// ── AUDIT ──
|
||||
// ── AUDIT: журнал действий компании ──────────────────────
|
||||
case 'audit': {
|
||||
const audit = await q.getAudit(company.id);
|
||||
return res.json({ ok: true, audit, count: audit.length });
|
||||
}
|
||||
|
||||
// ── LIMIT ──
|
||||
// ── LIMIT: текущий лимит компании ────────────────────────
|
||||
case 'limit':
|
||||
return res.json({ ok: true, limit: await q.getLimit(company) });
|
||||
|
||||
// ── SWITCH ──
|
||||
// ── SWITCH: переключение активной компании ───────────────
|
||||
case 'switch': {
|
||||
const to = req.query.to;
|
||||
if (!to) return res.json({ ok: false, error: 'no to parameter' });
|
||||
@@ -73,7 +100,7 @@ function createTestRouter() {
|
||||
return res.json({ ok: true, switchedTo: to });
|
||||
}
|
||||
|
||||
// ── CLEANUP ──
|
||||
// ── CLEANUP: удалить все данные тестовых компаний ───────
|
||||
case 'cleanup': {
|
||||
const cids = (req.query.cids || '').split(',');
|
||||
for (const c of cids) {
|
||||
@@ -84,7 +111,10 @@ function createTestRouter() {
|
||||
return res.json({ ok: true });
|
||||
}
|
||||
|
||||
// ── USERFLOW — полная эмуляция юзера через все слои ──
|
||||
// ── USERFLOW: полная эмуляция юзера через все слои ──────
|
||||
// Отличие от add/edit/delete: здесь выставляется мок-сессия
|
||||
// как после KC+IAM+resolveContext, и вызывается crud.
|
||||
// Показывает слой 'user→crud→db' в ответе.
|
||||
case 'userFlow': {
|
||||
const sub = req.query.sub; // add | edit | delete | list
|
||||
const cidr = req.query.cidr || '';
|
||||
@@ -138,10 +168,10 @@ function createTestRouter() {
|
||||
}
|
||||
});
|
||||
|
||||
// ── GET /chaos — параллельный хаос-тест внутри приложения ────────────
|
||||
// ── GET /chaos — параллельный хаос-тест (40 операций) ──────────────
|
||||
router.get('/chaos', async (req, res) => {
|
||||
const cids = ['WZ10001','WZ20002','WZ30001','WZ01112'];
|
||||
// Очистка
|
||||
// Очистка перед тестом
|
||||
for (const c of cids) {
|
||||
await pool.query('DELETE FROM v2_entries WHERE company_id IN (SELECT id FROM v2_companies WHERE client_id=$1)',[c]).catch(()=>{});
|
||||
await pool.query('DELETE FROM v2_audit WHERE company_id IN (SELECT id FROM v2_companies WHERE client_id=$1)',[c]).catch(()=>{});
|
||||
@@ -169,7 +199,7 @@ function createTestRouter() {
|
||||
}
|
||||
await Promise.all(tasks);
|
||||
|
||||
// Проверка ВСЕХ компаний
|
||||
// Проверка ВСЕХ компаний после теста
|
||||
const checks = [];
|
||||
let totalEntries = 0, totalAudit = 0, anyDupes = false, anyOverLimit = false;
|
||||
for (const clientId of cids) {
|
||||
@@ -184,7 +214,7 @@ function createTestRouter() {
|
||||
checks.push({ clientId, entries: entries.length, audit: audit.length, dupes, overLimit: entries.length > limit, limit });
|
||||
}
|
||||
|
||||
// Очистка
|
||||
// Очистка после теста
|
||||
for (const c of cids) {
|
||||
await pool.query('DELETE FROM v2_entries WHERE company_id IN (SELECT id FROM v2_companies WHERE client_id=$1)',[c]).catch(()=>{});
|
||||
await pool.query('DELETE FROM v2_audit WHERE company_id IN (SELECT id FROM v2_companies WHERE client_id=$1)',[c]).catch(()=>{});
|
||||
|
||||
Reference in New Issue
Block a user