feat: закрыть остаточный backlog
- keyset-пагинация аудита с курсором (occurred_at, id) и UI «Показать ещё» - пакетный /api/v1/labs/details и переход progress на один запрос - абсолютный потолок сессии 30 дней (identity V12 + web), очистка refresh - админская инвентаризация legacy-активов (/admin/legacy-assets, скрипт) - E2E в Chromium/Firefox/WebKit (18/18), CI preflight - LDD: 51 claim, 346 тестов, docs и handoff обновлены
This commit is contained in:
@@ -7,13 +7,13 @@ Playwright. В ходе проверки найден и исправлен ре
|
||||
|
||||
## 1. Что проверялось
|
||||
|
||||
Реестр: `docs/evidence/claims.json`, **44 утверждения**.
|
||||
Реестр: `docs/evidence/claims.json`, **51 утверждение**.
|
||||
|
||||
| Критичность | Claims | Proven | Gaps |
|
||||
|---|---:|---:|---:|
|
||||
| P0 | 25 | 25 | 0 |
|
||||
| P1 | 21 | 21 | 0 |
|
||||
| **Итого** | **46** | **46** | **0** |
|
||||
| P1 | 26 | 26 | 0 |
|
||||
| **Итого** | **51** | **51** | **0** |
|
||||
|
||||
Домены:
|
||||
|
||||
@@ -23,12 +23,13 @@ Playwright. В ходе проверки найден и исправлен ре
|
||||
- жизненный цикл: академ, возврат, отчисление, архив, восстановление, вход;
|
||||
- профиль студента и скачивание сдач;
|
||||
- материалы и активы, включая secure-by-default legacy-файлы;
|
||||
- миграции V11 и V13, полный upgrade с master-схемы;
|
||||
- миграции V11–V13, полный upgrade с master-схемы;
|
||||
- аудит: токен, admin-only, сохранность после удаления, representative-хуки,
|
||||
пагинация;
|
||||
- аутентификация: короткий access-токен, ротация/отзыв refresh, web-сессия;
|
||||
- группы курса без N+1, авторство материалов, ведомость, UI/контракты, импорт,
|
||||
копирование лабораторных;
|
||||
keyset-пагинация;
|
||||
- аутентификация: короткий access-токен, ротация/отзыв refresh, web-сессия,
|
||||
абсолютный потолок 30 дней и очистка истёкших токенов;
|
||||
- группы курса и детали лабораторных без N+1, авторство материалов, ведомость,
|
||||
UI/контракты, импорт, копирование лабораторных;
|
||||
- runtime-smoke, масштабный load, mutation testing, браузерный E2E и CI smoke.
|
||||
|
||||
## 2. Команды и результаты
|
||||
@@ -39,7 +40,7 @@ Playwright. В ходе проверки найден и исправлен ре
|
||||
./gradlew test --rerun-tasks
|
||||
```
|
||||
|
||||
Результат: **319 тестов, 0 failures, 0 errors, 0 skipped**.
|
||||
Результат: **346 тестов, 0 failures, 0 errors, 0 skipped**.
|
||||
`ktlintCheck` и `detekt` — зелёные.
|
||||
|
||||
### 2.2. LDD gate
|
||||
@@ -48,7 +49,7 @@ Playwright. В ходе проверки найден и исправлен ре
|
||||
./gradlew evidenceCheck
|
||||
```
|
||||
|
||||
Результат: **46/46 claims proven, P0 gaps = 0**.
|
||||
Результат: **51/51 claims proven, P0 gaps = 0**.
|
||||
Отчёт: `build/reports/evidence/index.md`.
|
||||
|
||||
Gate устроен так:
|
||||
@@ -67,11 +68,13 @@ Gate устроен так:
|
||||
|
||||
Сценарий: одноразовая PostgreSQL 18, master identity `V1–V3`, academic
|
||||
`V1–V5`, progress `V1–V9,V11,V12`, синтетические данные, затем ветка:
|
||||
identity `V4–V11`, academic `V6–V12`, progress `V13`.
|
||||
identity `V4–V13`, academic `V6–V12`, progress `V13`.
|
||||
|
||||
Проверено:
|
||||
|
||||
- `identity.audit_events` (`V10`) и `identity.refresh_tokens` (`V11`) созданы;
|
||||
- `identity.audit_events` (`V10`), `identity.refresh_tokens` (`V11`),
|
||||
`absolute_expires_at` (`V12`) и keyset-индекс `audit_events_occurred_id_idx`
|
||||
(`V13`) созданы;
|
||||
- `students`, `groups`, `membership_history` сохранились;
|
||||
- `labs.course_id`, `chapters.course_id`, `article_assets` созданы;
|
||||
- `grade_comments.author_account_id`, `lab_grades.updated_by_account_id` созданы;
|
||||
@@ -117,7 +120,8 @@ identity `V4–V11`, academic `V6–V12`, progress `V13`.
|
||||
преподавателю (существование не раскрывается);
|
||||
- админ может скачать такой файл;
|
||||
- `GET /api/v1/content/legacy-assets` защищён `ROLE_ADMIN` и перечисляет
|
||||
несвязанные файлы для привязки или удаления;
|
||||
несвязанные файлы; админская страница `/admin/legacy-assets` и
|
||||
`scripts/legacy-assets.sh` дают тот же список для привязки или удаления;
|
||||
- связанный актив по-прежнему наследует видимость статьи → раздела → главы:
|
||||
публичная `ALL` — без входа, курсовая — только участникам курса, скрытая
|
||||
для студента — `404`.
|
||||
@@ -138,30 +142,44 @@ identity `V4–V11`, academic `V6–V12`, progress `V13`.
|
||||
токена отзывает все сессии аккаунта;
|
||||
- logout и смена пароля отзывают refresh; отчисленный, выпущенный, архивный
|
||||
студент, архивный аккаунт и закрытая группа не могут обновить сессию;
|
||||
- web-сессия — 7 дней sliding (`LMS_SESSION_TIMEOUT`), cookie `LMS_SESSION`
|
||||
(`HttpOnly`, `SameSite=Lax`, `Max-Age` перевыпускается на каждом ответе);
|
||||
- `V12__refresh_absolute.sql` задаёт `absolute_expires_at`; ротация не
|
||||
продлевает абсолютный срок, после 30 дней refresh отклоняется;
|
||||
- web-сессия — 7 дней sliding (`LMS_SESSION_TIMEOUT`), но не дольше 30 дней
|
||||
(`LMS_SESSION_ABSOLUTE_TIMEOUT`); cookie `LMS_SESSION`
|
||||
(`HttpOnly`, `SameSite=Lax`, `Max-Age` перевыпускается на каждом ответе и
|
||||
ограничен остатком абсолютного срока);
|
||||
- access обновляется автоматически за 2 минуты до истечения, поэтому активная
|
||||
вкладка не требует входа каждые 15 минут; logout отзывает refresh.
|
||||
вкладка не требует входа каждые 15 минут; logout отзывает refresh;
|
||||
- истёкшие refresh-токены старше retention (30 дней) удаляет ночная задача
|
||||
(`LMS_REFRESH_TOKEN_CLEANUP_CRON`), свежие отозванные токены сохраняются для
|
||||
detection reuse.
|
||||
|
||||
Прямой API-клиент должен сам использовать `POST /api/v1/auth/refresh`;
|
||||
автоматического обновления вне web BFF нет.
|
||||
|
||||
Результат: **PASS**, claims `LDD-AUTH-001`, `LDD-AUTH-002`, `LDD-AUTH-003`.
|
||||
Результат: **PASS**, claims `LDD-AUTH-001`, `LDD-AUTH-002`, `LDD-AUTH-003`,
|
||||
`LDD-AUTH-006`, `LDD-AUTH-007`.
|
||||
|
||||
### 2.7. Пагинация, N+1 и масштаб
|
||||
|
||||
- `GET /api/v1/audit` принимает `page` и `limit` (максимум 500); в UI есть
|
||||
«Показать ещё». Это offset-пагинация, а не keyset.
|
||||
- `GET /api/v1/audit` принимает keyset-курсор `cursor` (opaque `occurredAt|id`),
|
||||
`limit` (максимум 500) и `action`; offset `page` остался для совместимости.
|
||||
Курсорные страницы не пересекаются, битый курсор — `400`; в UI «Показать ещё»
|
||||
идёт по курсору, «В начало» возвращает к первой странице.
|
||||
- `GET /api/v1/labs/details` отдаёт все лабораторные курса с уровнями и
|
||||
вариантами одним запросом; progress делает ровно один HTTP-вызов вместо
|
||||
запроса на каждую лабораторную.
|
||||
- `GET /api/v1/courses/{id}/groups` возвращает привязанные группы с именами
|
||||
одним вызовом; страница преподавателя не делает N+1 по именам групп.
|
||||
- Полный load использует seed на **100 групп / 1000 студентов** (SQL, только
|
||||
release-контур) и **125 параллельных чтений** курса, лаб, профиля, групп и
|
||||
аудита: **0 ошибок, p95 < 1 с** (порог p95 < 2 с).
|
||||
|
||||
Остаются offset-пагинация аудита и N+1 в отдельных лабораторных
|
||||
списках/ведомости — это лабораторные детали, не продуктовые риски доступа.
|
||||
Остаётся запрос сдач по выбранной лабораторной в ведомости — это не N+1 по
|
||||
студентам и не продуктовый риск доступа.
|
||||
|
||||
Результат: **PASS**, claims `LDD-AUDIT-004`, `LDD-PERF-001`, `LDD-RUNTIME-003`.
|
||||
Результат: **PASS**, claims `LDD-AUDIT-004`, `LDD-AUDIT-005`, `LDD-PERF-001`,
|
||||
`LDD-PERF-002`, `LDD-RUNTIME-003`.
|
||||
|
||||
### 2.8. Mutation testing
|
||||
|
||||
@@ -187,16 +205,20 @@ PIT запускается через CLI с JUnit 4 harness: JUnit5-плаги
|
||||
./scripts/ldd-browser.sh
|
||||
```
|
||||
|
||||
Playwright/Chromium на полном стеке, одноразовая БД:
|
||||
Playwright в Chromium, Firefox и WebKit на полном стеке, одноразовая БД:
|
||||
|
||||
- admin: вход, `/admin/courses` с курсом, `/admin/audit` с событием;
|
||||
- teacher: вход, курс, ведомость (группа/студент, без админских кнопок),
|
||||
профиль студента, создание главы курса;
|
||||
- anonymous: `/admin/audit` → `/login`;
|
||||
- student: вход, `/admin/audit` → `/student`, `/learn/course/{no}`.
|
||||
- student: вход, `/admin/audit` → `/student`, `/learn/course/{no}`;
|
||||
- admin: cursor-пагинация аудита («Показать ещё» → «Продолжение» → «В начало»);
|
||||
- admin: страница `/admin/legacy-assets` со списком или пустым состоянием.
|
||||
|
||||
Результат: **4/4 passed**, evidence: `build/evidence/browser.json`.
|
||||
Дополнительно проверены сдача файла студентом и mobile-размер teacher-страницы.
|
||||
Результат: **18/18 passed** (6 сценариев × 3 браузера), evidence:
|
||||
`build/evidence/browser.json`. Дополнительно проверены сдача файла студентом и
|
||||
mobile-размер teacher-страницы. На Fedora WebKit запускается через Docker-образ
|
||||
Playwright, потому что системных Ubuntu-библиотек нет.
|
||||
|
||||
### 2.10. Конкурентные apply/rollback
|
||||
|
||||
@@ -215,8 +237,12 @@ mutation, runtime + scale и browser E2E, затем финальный `evidenc
|
||||
- `check` — `test ktlintCheck detekt evidenceCheck` на push/MR;
|
||||
- `smoke` — `ldd-smoke.sh` (полный стек, health, вход админа, один курс)
|
||||
на каждом push/MR;
|
||||
- `release-evidence` — полный `ldd-release.sh` с Chromium/Node: обязателен для
|
||||
тегов, для веток запускается вручную.
|
||||
- `release-evidence` — полный `ldd-release.sh` с Chromium/Firefox/WebKit и
|
||||
Node: обязателен для тегов, для веток запускается вручную.
|
||||
- smoke/release начинаются с `scripts/ci-preflight.sh`, который проверяет
|
||||
Java 21, Docker Compose v2, Python 3, Node и Playwright.
|
||||
- Первый реальный прогон на GitLab shell-runners ещё не подтверждён; локальные
|
||||
скрипты и конфигурация проверены.
|
||||
|
||||
### 2.12. Найденный дефект
|
||||
|
||||
@@ -240,9 +266,12 @@ optional json fields`.
|
||||
- Legacy-актив без привязки не утекает обычным пользователям, а админ может
|
||||
провести инвентаризацию.
|
||||
- Refresh-токены ротируются и отзываются; закрытый статус блокирует продление
|
||||
сессии, а web-сессия переживает 15-минутный access-токен.
|
||||
- Аудит листается по страницам, а группы курса отдаются одним вызовом без N+1.
|
||||
- Аудит пишется и читается; upgrade не теряет синтетические данные.
|
||||
сессии, абсолютный потолок 30 дней ограничивает сессию, а web-сессия
|
||||
переживает 15-минутный access-токен.
|
||||
- Аудит листается keyset-курсором, детали лабораторных и группы курса
|
||||
отдаются пакетно без N+1.
|
||||
- Аудит пишется и читается; upgrade не теряет синтетические данные;
|
||||
истёкшие refresh-токены очищаются.
|
||||
- Мутационная прочность критичных компонентов измерена, а не заявлена.
|
||||
- Масштабный load на 100 группах / 1000 студентов проходит без ошибок.
|
||||
- Реальный дефект DTO/Jackson найден именно E2E-контуром и закрыт тестом.
|
||||
@@ -254,35 +283,36 @@ optional json fields`.
|
||||
2. **Property-based тесты покрывают не все ветки.** Есть генераторы для
|
||||
матрицы доступа, планировщика и 10 транзакционных apply/rollback-кейсов,
|
||||
но не для всех сочетаний полей и распределённой нагрузки.
|
||||
3. **Браузерный E2E — один Chromium и 4 сценария.** Нет визуальной регрессии,
|
||||
Safari/Firefox и полного мобильного набора страниц.
|
||||
3. **Браузерный E2E — 3 браузера, 6 сценариев.** Нет визуальной регрессии и
|
||||
полного мобильного набора страниц; WebKit на Fedora требует Docker-образа.
|
||||
4. **Нагрузочный smoke — не benchmark.** Одна машина, синтетические данные,
|
||||
без SLA production; 125 чтений и p95 < 1 с — ориентир, а не гарантия.
|
||||
5. **Offset-пагинация аудита.** Вставки между страницами могут сдвигать
|
||||
выдачу; keyset-курсор не реализован.
|
||||
6. **N+1 в отдельных лабораторных списках/ведомости.** Имена групп курса и
|
||||
аудит исправлены, но не все большие списки собраны пакетно.
|
||||
7. **Нет абсолютного лимита web-сессии.** Sliding-сессия может продлеваться
|
||||
бесконечно при непрерывной активности; отзыв по статусу при этом работает.
|
||||
5. **Сдачи по выбранной лабораторной в ведомости** запрашиваются отдельно;
|
||||
это не N+1 по студентам, но и не пакетная выборка.
|
||||
6. **Инвентаризация legacy-активов на production не выполнена.** UI, API и
|
||||
скрипт готовы, но реальные файлы нужно привязать или удалить вручную.
|
||||
7. **Первый CI-прогон не подтверждён.** Конфигурация и preflight готовы, но
|
||||
наличие shell-runners `lms-shell`/`lms-release` проверяется в GitLab.
|
||||
8. **Полный release-evidence не на каждом коммите.** На push/MR идёт smoke,
|
||||
а mutation/browser/scale — для тегов или вручную.
|
||||
9. **Внешняя интеграция «Контингент» не проверялась** — она остаётся заделом.
|
||||
9. **Интеграция «Контингент» исключена из плана** — без нового ТЗ и доступа к
|
||||
API она не проверялась и не реализуется.
|
||||
10. **Номер группы числовой.** Отдельного требования к ведущим нулям нет;
|
||||
`01` и `1` — одно значение, это не открытый риск.
|
||||
|
||||
## 5. Вывод
|
||||
|
||||
Текущая функциональность прошла LDD-контур: **46/46 claims proven, P0 gaps = 0**,
|
||||
**319 тестов**, upgrade, mutation baseline, browser E2E, smoke и масштабный load
|
||||
зелёные. Это достаточное основание продолжать доработки, но не «формальное
|
||||
доказательство всей системы».
|
||||
Текущая функциональность прошла LDD-контур: **51/51 claims proven, P0 gaps = 0**,
|
||||
**346 тестов**, upgrade, mutation baseline, 18/18 browser E2E, smoke и
|
||||
масштабный load зелёные. Это достаточное основание продолжать доработки, но не
|
||||
«формальное доказательство всей системы».
|
||||
|
||||
Следующий цикл LDD:
|
||||
|
||||
1. почистить инвентарь legacy-активов через `GET /api/v1/content/legacy-assets`
|
||||
(файлы уже не публичны, это гигиена хранилища);
|
||||
2. добавить keyset-пагинацию аудита и убрать оставшийся N+1 в лабораторных
|
||||
списках/ведомости;
|
||||
3. при необходимости ввести абсолютный лимит web-сессии;
|
||||
4. расширить browser E2E на публикацию оценки и остальные мобильные страницы;
|
||||
5. подготовить внешнюю интеграцию «Контингент»/`next.uniyar.ac.ru`.
|
||||
1. прогнать инвентаризацию legacy-активов на production/staging через
|
||||
`/admin/legacy-assets` или `scripts/legacy-assets.sh` и привязать/удалить
|
||||
найденные файлы;
|
||||
2. подтвердить первый реальный CI-прогон на shell-runners и tag-job;
|
||||
3. при необходимости расширить E2E на визуальную регрессию и остальные
|
||||
мобильные страницы;
|
||||
4. пакетно отдавать сдачи в ведомости, если появятся медленные курсы.
|
||||
|
||||
Reference in New Issue
Block a user