Зачем: решения владельца (роли точки/бухгалтера/директора, пакет зарплаты
с подписью директора, неизменяемость после подписи, зарплата по точке
фактической смены, согласие на биометрию) должны стать исполняемыми
критериями приёмки до того, как Codex начнёт этап 4, иначе реализацию
будет нечем проверить.
API ещё нет, поэтому тесты написаны против наиболее вероятного контракта
C1 (access-grants), C2 (payroll/batches, submit/sign/return,
notifications), C3 (biometric-consent, terminal enrollment); контракт и
инварианты описаны в tests/e2e/NOTES-FOR-EXECUTORS.md. Сейчас все OWN
красные (404 Route not found) и внесены в known-defects.json.
В NOTES также: найденные сценариями дефекты с указанием файлов,
конфликт NODE_ENV/standalone для pilot, N6, требования к раннеру docker-host.
Зачем: корневой compose.yaml уже проверяется tests/unit/deployment.test.ts,
а pilot-файл, который реально разворачивается у владельца, — нет. В нём
подстановки вида ${STANDALONE_MANAGER_TOKEN} без :? молча дают пустое
значение, а при пустых токенах standalone выдаёт права анониму (F2);
NODE_ENV=development в pilot отключает production-проверки config.ts.
- PILOT-1: каждая подстановка должна быть ${VAR:?сообщение};
- PILOT-2: ни один сервис не с NODE_ENV=development;
- PILOT-3: у app нет учётных данных мигратора (сейчас зелёный).
Файл в tests/e2e как *.e2e.ts: tests/unit вне разрешённых путей, а
красные пункты (дефекты deploy/, которые правят исполнители) идут через
known-defects.json и не ломают `npm run check`. Сейчас PILOT-1 и PILOT-2
красные: 14 подстановок без :?, app с NODE_ENV=development.
Зачем: прежний CI запускал миграции и тесты под POSTGRES_USER сервиса
(суперпользователь), не проверял откат миграций, не запускал e2e на
реальной БД, держал плавающие теги actions (F13) и выполнял docker build
в контейнере без Docker daemon.
- verify: bootstrap-админ сервиса только создаёт роли (provision-pg.mjs,
пароли случайные); миграции up -> откат всех -> up -> up (no-op) ролью
krona_migrator без SUPERUSER; `npm run check` с DATABASE_TEST_URL
(app-роль) и DATABASE_TEST_MIGRATION_URL (очистка, см. N5); npm audit.
- format: prettier вынесен в отдельную задачу — он красный по 25 файлам
(F14) и не должен маскировать статус verify/e2e.
- e2e: реальный сервер + PostgreSQL 17 через scripts/test/run-e2e.mjs.
Выбран «храповик» known-defects.json, а не отдельная задача с
continue-on-error: так CI зелёный, только если стенд работает и каждый
красный сценарий — известный открытый дефект; новый красный сценарий
(регрессия) или исправленный, но не вычеркнутый дефект делают задачу
красной. continue-on-error скрыл бы и поломку самого стенда.
- docker-build: отдельная задача на метке `docker-host` только для push,
после verify; без такого раннера она ждёт в очереди, не блокируя
остальные.
- actions/checkout v4.4.0 и actions/setup-node v4.4.0 закреплены SHA
(git ls-remote; одинаковы на github.com и code.forgejo.org).
Проверено: actionlint 1.7.12 (метки docker/docker-host объявлены как
self-hosted) — 0 замечаний; scripts/test/ci-local.sh повторяет шаги
verify/format/e2e на локальной PostgreSQL 17.11 (Forgejo-раннер реально
не запускался — не проверено).
Зачем: tests/e2e/evidence не входит в .prettierignore (файл вне разрешённых путей), и минифицированный JSON vitest делал бы npm run format:check красным из-за артефактов, а не из-за кода.
Зачем: бэкенд офлайн-синхронизации (миграция 0011) покрыт только
юнит-тестами без реальной БД. Сценарии на живом сервере показывают, как
терминал реально взаимодействует с сервером, и фиксируют границы
доверия к устройству.
Проверяется правильное поведение: устройство не подтверждает смены и не
утверждает табель сам (SYNC-A/B); начальная загрузка работает (SYNC-C0,
сейчас 500 «column status does not exist» — у staff_org_units нет
status) и не теряет сущности после 100-й (SYNC-C); правки из портала
видны в инкрементальном pull (SYNC-D); отметки уволенных и вне окна
времени отклоняются (SYNC-E/J); точка устройства сохраняется, чужая
отклоняется (SYNC-F); битая команда не роняет пакет (SYNC-G); CSRF и
анонимная регистрация устройств запрещены (SYNC-H/I); ack не принимает
курсор из будущего (SYNC-K). SYNC-L — позитивный контроль отзыва.
Зачем: каждый открытый дефект аудита должен иметь исполняемый сценарий
на реальной БД, который проверяет ПРАВИЛЬНОЕ поведение. Тогда красный
тест = открытый дефект, а исправление видно по смене цвета, без ручной
перепроверки.
- N1 POST /employees -> 201; N2 подтверждение смены, расчёт, утверждение
и сумма начисления 8 ч x синтетическая ставка; N4 пересекающиеся
периоды не начисляют смену дважды (сейчас блокирован N2 — это видно в
сообщении assert как «предусловие, см. N2»);
- N3 SSRF: подписка на https://127.0.0.1:<порт> должна отклоняться, и
локальный слушатель не должен получить соединение;
- N9, N12, N10, N11, F2 (процесс без токенов), F3, F4 (отдельный процесс),
F7, F10.
Сотрудники для сценариев, не относящихся к N1, создаются импортом, чтобы
один дефект не маскировал остальные. Все данные синтетические (E2E-*),
ставка 123,45 ₽/ч заведомо не реальная.
Зачем: 156 зелёных тестов на fakeDatabase не поймали, что ключевые
сценарии (создание сотрудника, подтверждение смены, расчёт зарплаты)
всегда падают на реальной PostgreSQL. Нужен приёмочный стенд, который
проверяет продукт как чёрный ящик: настоящий процесс сервера, настоящая
БД, роли как в pilot, но с минимальными привилегиями.
- tests/e2e/harness/global-setup.ts: создаёт БД и роли через
scripts/test/provision-pg.mjs, применяет миграции ролью krona_migrator
(NOSUPERUSER), поднимает `tsx src/server.ts` под krona_staff_app с
токенами/VAPID из crypto.randomBytes; три процесса: с токенами, без
токенов (как корневой compose.yaml) и отдельный для проверки rate limit;
логи серверов пишутся в tests/e2e/evidence/.
- *.e2e.ts и отдельный vitest.e2e.config.ts: e2e не попадают в `npm test`,
поэтому `npm run check` не требует БД и не краснеет от известных дефектов.
- [HARNESS]/[CTRL] — позитивные контроли (версия PG >= 17, атрибуты ролей,
app-роль не видит schema_migrations, RBAC, уволенный теряет доступ):
если они красные, красные дефектные сценарии ничего не доказывают.
- scripts/test/run-e2e.mjs: «храповик» известных дефектов по
tests/e2e/known-defects.json (новый красный или исправленный-но-не-
вычеркнутый ID = exit 1), таблица e2e-table.md и проверка, что в
evidence нет ничего похожего на секреты стенда.
Логи названы *.txt, т.к. *.log в .gitignore.
Зачем: файл уже до правки N5 не проходил prettier --check (часть F14); форматирование вынесено в отдельный коммит, чтобы смысловая правка N5 читалась отдельно от шума.
Зачем: afterAll удалял строки соединением роли приложения, а у
krona_staff_app намеренно нет DELETE на staff_employees. На реальной
PostgreSQL 17 тест проходил, но файл падал в afterAll ("permission denied
for table staff_employees", exit 1), поэтому любой CI с DATABASE_TEST_URL
был бы красным не из-за дефекта продукта, а из-за самого теста.
Выдавать app-роли DELETE ради теста нельзя: это ослабило бы модель прав.
Что сделано:
- очистка идёт отдельным соединением роли мигратора
(DATABASE_TEST_MIGRATION_URL или DATABASE_MIGRATION_URL, который CI уже
задаёт); так как FORCE RLS действует и на владельца таблиц, в транзакции
выставляется app.tenant_id; удаляются строки обоих тестовых tenant;
- очистка выполняется и в beforeAll, чтобы остатки прошлого упавшего
прогона не ломали уникальные ключи (EMP-PUSH-1, DEV-A);
- без URL мигратора тест падает с понятной ошибкой, а не оставляет мусор.
До: tests/e2e/evidence/n5-before.txt (1 passed, suite FAIL, exit=1).
После: tests/e2e/evidence/n5-after.txt (два прогона подряд, exit=0).
Зачем: аудит показал, что 156 зелёных тестов на fakeDatabase не заметили,
что создание сотрудника, подтверждение смены и расчёт зарплаты всегда
падают на реальной PostgreSQL. Чтобы тесты и CI проверяли реальный SQL,
нужен воспроизводимый стенд с ролями как в pilot, но с минимальными
привилегиями:
- scripts/test/provision-pg.mjs: bootstrap-админ (только как DBA/entrypoint)
создаёт krona_migrator (NOSUPERUSER NOCREATEDB NOCREATEROLE, владелец БД и
схемы public) и krona_staff_app (NOSUPERUSER NOBYPASSRLS NOINHERIT); пароли
crypto.randomBytes, пишутся только в env-файл 0600, в вывод не попадают.
scripts/create-app-role.mjs здесь не используется, т.к. без SUPERUSER он
падает (находка N6, продуктовый код не трогаем).
- scripts/test/migrate-roundtrip.mjs: up -> откат всех миграций -> up ->
повторный up должен быть no-op; ролью мигратора, как требует AGENTS.md.
Проверено на PostgreSQL 17.11: 11 миграций применены, откачены, применены
повторно, третий up пустой.