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:
@@ -52,14 +52,14 @@
|
||||
# 6. Mutation testing критичных P0-компонентов
|
||||
./scripts/ldd-mutation.sh
|
||||
|
||||
# 7. Браузерный E2E (Playwright + Chromium)
|
||||
# 7. Браузерный E2E (Playwright: Chromium/Firefox/WebKit)
|
||||
./scripts/ldd-browser.sh
|
||||
|
||||
# 8. Полный release gate
|
||||
./scripts/ldd-release.sh
|
||||
```
|
||||
|
||||
Для E2E один раз установите зависимости и браузер: `cd e2e && npm install && npx playwright install chromium`.
|
||||
Для E2E один раз установите зависимости и браузеры: `cd e2e && npm install && npx playwright install chromium firefox webkit`.
|
||||
Mutation harness использует JUnit 4: JUnit5-плагин PIT несовместим с JUnit
|
||||
Platform 6 из Spring Boot 4.
|
||||
|
||||
@@ -87,15 +87,17 @@ Platform 6 из Spring Boot 4.
|
||||
LDD-циклы выполнены 2026-10-27/28; последний — после закрытия продуктовых
|
||||
рисков:
|
||||
|
||||
- **319 автоматических тестов**, 0 failures/errors;
|
||||
- **46 claims**: 25 P0 + 21 P1, **46/46 proven, P0 gaps = 0**. P0 — доступы,
|
||||
- **346 автоматических тестов**, 0 failures/errors;
|
||||
- **51 claims**: 25 P0 + 26 P1, **51/51 proven, P0 gaps = 0**. P0 — доступы,
|
||||
повышение, жизненный цикл, профиль/скачивание, материалы/активы
|
||||
(включая secure-by-default legacy-файлы), миграция V13, аудит, refresh-токены
|
||||
и их отзыв; P1 — группы, авторство, UI, контракты, импорт, копирование лаб,
|
||||
пагинация аудита, bulk-группы курса, runtime, mutation и browser E2E;
|
||||
(включая secure-by-default legacy-файлы), миграции V11–V13, аудит,
|
||||
refresh-токены и их отзыв; P1 — группы, авторство, UI, контракты, импорт,
|
||||
копирование лаб, keyset-пагинация, пакетные детали лабораторных,
|
||||
абсолютный потолок сессии, runtime, mutation и browser E2E;
|
||||
- mutation testing: `CourseAccessService` 75% test strength,
|
||||
`PromotionPlanner` 69%;
|
||||
- Playwright: 4 сценария (admin, teacher, student/anonymous, сдача+mobile),
|
||||
- Playwright: 6 сценариев (admin, teacher, student/anonymous, сдача+mobile,
|
||||
cursor-аудит, инвентаризация активов) × Chromium/Firefox/WebKit = **18/18**,
|
||||
все зелёные;
|
||||
- масштабный load: 100 групп / 1000 студентов, 125 параллельных чтений,
|
||||
0 ошибок, p95 < 1 с (порог p95 < 2 с);
|
||||
@@ -107,7 +109,9 @@ LDD-циклы выполнены 2026-10-27/28; последний — посл
|
||||
Kotlin-модуль, поэтому DTO с default-значениями требовали все поля JSON
|
||||
(например, создание студента без `firstName`/`basis` давало 400).
|
||||
|
||||
Остаточные ограничения: offset-пагинация аудита, N+1 в отдельных лабораторных
|
||||
списках/ведомости, отсутствие абсолютного лимита web-сессии. Legacy-активы,
|
||||
срок жизни JWT и «ведущие нули» больше не являются открытыми рисками.
|
||||
Остаточные ограничения: ручная инвентаризация legacy-активов на production,
|
||||
сдачи по выбранной лабораторной в ведомости, непроверенный первый CI-прогон на
|
||||
реальных runner'ах и отсутствие визуальной регрессии. Legacy-активы, срок
|
||||
access-токена, пагинация, N+1 деталей лабораторных, абсолютный лимит сессии и
|
||||
«ведущие нули» больше не являются открытыми рисками.
|
||||
Результаты — в `baseline-2026-10-27.md`.
|
||||
|
||||
@@ -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. пакетно отдавать сдачи в ведомости, если появятся медленные курсы.
|
||||
|
||||
@@ -435,7 +435,7 @@
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/16-stage6-report.md, §2.2",
|
||||
"claim": "Схема master + синтетические данные → миграции identity V4–V11, academic V6–V12, progress V13; V10 audit и V11 refresh создаются, V13 идемпотентна.",
|
||||
"claim": "Схема master + синтетические данные → миграции identity V4–V13, academic V6–V12, progress V13; V10 audit, V11 refresh, V12 absolute-expiry и V13 keyset-индексы создаются, V13 идемпотентна.",
|
||||
"oracle": "Успешное применение и postflight-наличие таблиц/колонок/индексов",
|
||||
"runtime": [
|
||||
"migration.master_upgrade"
|
||||
@@ -488,16 +488,16 @@
|
||||
},
|
||||
{
|
||||
"id": "LDD-BROWSER-001",
|
||||
"title": "Браузерный E2E админа, преподавателя и студента, включая сдачу и mobile",
|
||||
"title": "Браузерный E2E в Chromium, Firefox и WebKit",
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/evidence/README.md",
|
||||
"claim": "Playwright на реальном стеке: admin видит курс и аудит; teacher открывает курс/ведомость/профиль, создаёт главу и видит mobile-вид; student сдаёт файл; аноним и студент не попадают в админку.",
|
||||
"claim": "6 сценариев (админ, преподаватель, студент/аноним, сдача+mobile, cursor-аудит, инвентаризация активов) проходят в Chromium, Firefox и WebKit: 18/18 без unexpected.",
|
||||
"oracle": "Playwright assertions на DOM/URL",
|
||||
"runtime": [
|
||||
"browser.e2e"
|
||||
],
|
||||
"limitations": "4 сценария, один Chromium; нет визуальной регрессии и Safari/Firefox."
|
||||
"limitations": "Нет визуальной регрессии и полного набора мобильных страниц; WebKit на Fedora запускается через Docker-образ Playwright."
|
||||
},
|
||||
{
|
||||
"id": "LDD-RUNTIME-002",
|
||||
@@ -613,7 +613,7 @@
|
||||
"ru.ysu.lms.web.session.SessionCookieRefreshFilterTest#refreshes cookie max age on every existing session()",
|
||||
"ru.ysu.lms.web.auth.AuthPageControllerTest#logout revokes refresh token and invalidates session()"
|
||||
],
|
||||
"limitations": "Cookie и серверный timeout оба 7 дней; абсолютного верхнего предела сессии нет."
|
||||
"limitations": "Cookie и серверный timeout 7 дней; поверх них действует абсолютный потолок 30 дней (LDD-AUTH-006)."
|
||||
},
|
||||
{
|
||||
"id": "LDD-AUDIT-004",
|
||||
@@ -621,13 +621,13 @@
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/evidence/README.md",
|
||||
"claim": "Audit API отдаёт страницы limit/page без пересечений и с устойчивым порядком; UI показывает «Показать ещё».",
|
||||
"claim": "Audit API поддерживает offset-страницы `page`/`limit` для совместимости; UI по умолчанию продолжает журнал keyset-курсором.",
|
||||
"oracle": "Страницы AuditIntegrationTest и ссылки шаблона",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.identity.audit.AuditIntegrationTest#pagination returns disjoint pages newest first()",
|
||||
"ru.ysu.lms.web.admin.AdminAuditTemplateTest#renders audit events with labels and filter()"
|
||||
],
|
||||
"limitations": "Offset-пагинация, не keyset; при частых вставках возможны сдвиги."
|
||||
"limitations": "Offset-режим остаётся только для старых ссылок; основной путь — курсор (LDD-AUDIT-005)."
|
||||
},
|
||||
{
|
||||
"id": "LDD-PERF-001",
|
||||
@@ -641,7 +641,7 @@
|
||||
"ru.ysu.lms.academic.course.CourseGroupTeacherAccessTest#teacher gets attached groups with names in one call()",
|
||||
"ru.ysu.lms.web.teacher.TeacherControllerCourseGroupsTest#loads group names with a single call instead of per-group requests()"
|
||||
],
|
||||
"limitations": "Детали лабораторных профиля всё ещё подтягиваются по одной; это следующий шаг."
|
||||
"limitations": "Детали лабораторных теперь пакетные (LDD-PERF-002); отдельные запросы на лабораторную в ведомости ещё остаются."
|
||||
},
|
||||
{
|
||||
"id": "LDD-AUTH-004",
|
||||
@@ -669,6 +669,84 @@
|
||||
"runtime.auth_refresh"
|
||||
],
|
||||
"limitations": "Проверено на одноразовом стеке; TTL не истекает в рамках smoke."
|
||||
},
|
||||
{
|
||||
"id": "LDD-AUTH-006",
|
||||
"title": "Абсолютный потолок сессии 30 дней",
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/16-stage6-report.md, §4",
|
||||
"claim": "Ротация refresh не продлевает absolute_expires_at; после 30 дней refresh отклоняется, а web-сессия принудительно завершается и cookie не живёт дольше остатка.",
|
||||
"oracle": "absolute_expires_at в цепочке ротаций, 401 на refresh, redirect /login?expired=absolute, Max-Age cookie",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.identity.RefreshTokenIntegrationTest#refresh past the absolute session expiry is rejected with unauthorized()",
|
||||
"ru.ysu.lms.identity.RefreshTokenIntegrationTest#rotation keeps the absolute session expiry unchanged()",
|
||||
"ru.ysu.lms.web.session.AuthInterceptorTest#absolute expired session redirects to absolute login without identity call()",
|
||||
"ru.ysu.lms.web.session.SessionCookieRefreshFilterTest#cookie max age is capped by remaining absolute lifetime()",
|
||||
"ru.ysu.lms.web.session.SessionCookieRefreshFilterTest#absolute expired session receives zero max age cookie()",
|
||||
"ru.ysu.lms.web.auth.AuthPageControllerTest#login stores authenticated at timestamp()"
|
||||
],
|
||||
"limitations": "Срок настраивается LMS_SESSION_ABSOLUTE_TIMEOUT; абсолютный отсчёт начинается с логина, а не с создания web-сессии."
|
||||
},
|
||||
{
|
||||
"id": "LDD-AUTH-007",
|
||||
"title": "Очистка истёкших refresh-токенов",
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/16-stage6-report.md, §4",
|
||||
"claim": "Ночная очистка удаляет токены, у которых sliding- или absolute-срок истёк раньше retention (30 дней), и сохраняет свежие/недавно отозванные токены для detection reuse.",
|
||||
"oracle": "cleanupExpired(now) и количество удалённых записей",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.identity.RefreshTokenIntegrationTest#cleanup deletes only tokens expired beyond retention()"
|
||||
],
|
||||
"limitations": "Расписание задаётся LMS_REFRESH_TOKEN_CLEANUP_CRON; в тесте вызывается метод напрямую, без ожидания cron."
|
||||
},
|
||||
{
|
||||
"id": "LDD-AUDIT-005",
|
||||
"title": "Keyset-пагинация аудита",
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/16-stage6-report.md, §2.7",
|
||||
"claim": "Audit API принимает непрозрачный cursor (occurredAt|id), отдаёт строго следующие записи без пересечений и сохраняет фильтр по action; битый курсор — 400; UI ведёт «Показать ещё» по курсору.",
|
||||
"oracle": "Страницы AuditIntegrationTest и cursor-ссылка шаблона",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.identity.audit.AuditIntegrationTest#cursor pagination walks all events newest first()",
|
||||
"ru.ysu.lms.identity.audit.AuditIntegrationTest#cursor pagination respects the action filter()",
|
||||
"ru.ysu.lms.identity.audit.AuditIntegrationTest#malformed audit cursor is rejected with bad request()",
|
||||
"ru.ysu.lms.identity.audit.AuditIntegrationTest#cursor from the newest event returns an empty page()",
|
||||
"ru.ysu.lms.web.admin.AdminAuditTemplateTest#cursor mode links next page with cursor and resets to start()"
|
||||
],
|
||||
"limitations": "Offset page остаётся для обратной совместимости; keyset-курсор не подписан, но не содержит секретов."
|
||||
},
|
||||
{
|
||||
"id": "LDD-PERF-002",
|
||||
"title": "Пакетные детали лабораторных без N+1",
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/16-stage6-report.md, §2.7",
|
||||
"claim": "Academic отдаёт все лабораторные курса с уровнями и вариантами одним запросом /api/v1/labs/details; progress делает ровно один HTTP-вызов вместо запроса на каждую лабораторную.",
|
||||
"oracle": "LabDetailsIntegrationTest и счётчик запросов AcademicClientBatchLabsTest",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.academic.lab.LabDetailsIntegrationTest#teacher gets all course labs with levels and variants in one call()",
|
||||
"ru.ysu.lms.academic.lab.LabDetailsIntegrationTest#student of the course receives lab details()",
|
||||
"ru.ysu.lms.academic.lab.LabDetailsIntegrationTest#foreign teacher is forbidden for lab details()",
|
||||
"ru.ysu.lms.progress.AcademicClientBatchLabsTest#getCourseLabs loads all labs with levels and variants in one request()"
|
||||
],
|
||||
"limitations": "Ведомость всё ещё запрашивает сдачи по выбранной лабораторной; это не N+1 по студентам."
|
||||
},
|
||||
{
|
||||
"id": "LDD-ASSET-003",
|
||||
"title": "Админская инвентаризация legacy-активов",
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/15-stage5-course-materials.md",
|
||||
"claim": "Админ открывает /admin/legacy-assets, видит список файлов без статьи, ссылку на файл и пустое состояние; страница не показывает ошибку при пустом списке.",
|
||||
"oracle": "LegacyAssetsTemplateTest и HTTP-страница",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.web.admin.LegacyAssetsTemplateTest#renders orphan assets with links and admin-only explanation()",
|
||||
"ru.ysu.lms.web.admin.LegacyAssetsTemplateTest#renders empty legacy assets state()"
|
||||
],
|
||||
"limitations": "Удаление файлов остаётся ручной операцией на стороне academic-service; UI только инвентаризирует."
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user