fix: закрыть P0/P1 расхождения с целевой моделью
- академический отпуск: readOnly, запрет submit/start/finish защиты - черновики лабораторных скрыты от студентов в списке/деталях/по UUID - удалено действие «Убрать из группы» из UI и API - импорт: restoreInactiveStatuses, skipped-отчёт, разблокировка архивного аккаунта - глобальные материалы STUDENTS/TEACHERS с JWT-фильтрацией и сменой аудитории - LDD-AUTH-005 переведён на unit-evidence, clean check проходит по P0 - backup: безопасный парсинг .env, verify-latest, кавычки cron - robots/noindex, комментарии без автора, профиль без черновиков - docs/18-goal-audit.md; baseline 57 claims / 366 тестов / release PASS
This commit is contained in:
@@ -336,10 +336,10 @@ identity V13, **academic V12**, progress V13.
|
||||
- **Этап 6 — выполнен.** Права преподавателя на запись (лабораторные,
|
||||
ведомость, группы, материалы), центральный аудит `identity.audit_events`
|
||||
с `/admin/audit`, CI merge gate и полный hardening. Проверены прогон
|
||||
тестов/линтеров (**348**, 0 failures), upgrade миграций с master-схемы
|
||||
тестов/линтеров (**366**, 0 failures), upgrade миграций с master-схемы
|
||||
(identity `V4–V13`, academic `V6–V12`, progress `V13`), security-матрица,
|
||||
gateway, видимость активов и deploy-runbook. LDD baseline: **51 claims
|
||||
(25 P0 / 26 P1), 51/51 proven**, browser E2E 18/18 (3 браузера). Отчёт:
|
||||
gateway, видимость активов и deploy-runbook. LDD baseline: **57 claims
|
||||
(27 P0 / 30 P1), P0 gaps = 0**, browser E2E 18/18 (3 браузера). Отчёт:
|
||||
[16-stage6-report.md](16-stage6-report.md).
|
||||
|
||||
### 9.1. Известные пробелы после этапа 6
|
||||
|
||||
@@ -71,9 +71,9 @@
|
||||
| web | 108 | 0 | 0 | 0 |
|
||||
| gateway | 2 | 0 | 0 | 0 |
|
||||
| markdown | 5 | 0 | 0 | 0 |
|
||||
| **Итого на момент этапа 6** | **348** | **0** | **0** | **0** |
|
||||
| **Итого на момент этапа 6** | **366** | **0** | **0** | **0** |
|
||||
|
||||
После закрытия рисков актуальный LDD-baseline — **348 тестов, 0 failures**
|
||||
После закрытия рисков актуальный LDD-baseline — **366 тестов, 0 failures**
|
||||
(см. раздел 7). `ktlintCheck` и `detekt` прошли без замечаний. У gateway теперь есть unit-тесты публичных auth-путей и 401; маршрутизация
|
||||
gateway дополнительно проверена конфигурацией и security-матрицей ниже.
|
||||
Тот же набор команд выполняет `.woodpecker/` (Gitea + Woodpecker 3.x); на
|
||||
@@ -133,7 +133,7 @@ gateway дополнительно проверена конфигурацией
|
||||
|
||||
### 2.3. Security-проверки
|
||||
|
||||
Проверено автоматическими тестами (актуальный прогон — 348 тестов, 0 failures)
|
||||
Проверено автоматическими тестами (актуальный прогон — 366 тестов, 0 failures)
|
||||
и разбором конфигурации gateway. Запись преподавателя дополнительно покрыта
|
||||
negative-тестами «свой/чужой курс».
|
||||
|
||||
@@ -347,10 +347,10 @@ web-сессия с отзывом и абсолютным потолком, key
|
||||
## 7. LDD baseline
|
||||
|
||||
После этапа 6 и закрытия рисков текущая функциональность проверена через
|
||||
LDD-контур: реестр `docs/evidence/claims.json` (**51 claims, 25 P0 / 26 P1**),
|
||||
LDD-контур: реестр `docs/evidence/claims.json` (**57 claims, 27 P0 / 30 P1**),
|
||||
gate `./gradlew evidenceCheck`, upgrade-скрипт, runtime-smoke, smoke-workflow,
|
||||
mutation testing и браузерный Playwright E2E. Результат:
|
||||
**51/51 claims proven, P0 gaps = 0, 348 тестов, 0 failures**; mutation strength
|
||||
**57/57 claims proven, P0 gaps = 0, 366 тестов, 0 failures**; mutation strength
|
||||
`CourseAccessService` 75%, `PromotionPlanner` 69%; browser E2E 18/18 (6 сценариев × Chromium/Firefox/WebKit);
|
||||
конкурентные apply/rollback и масштабный нагрузочный smoke (100 групп /
|
||||
1000 студентов, 125 параллельных чтений, 0 ошибок, p95 < 1 с) зелёные.
|
||||
|
||||
@@ -116,8 +116,8 @@
|
||||
### 1.8. LDD и проверки
|
||||
|
||||
- Реестр: `docs/evidence/claims.json`.
|
||||
- **51 claim**: 25 P0 + 26 P1; **51/51 proven, P0 gaps = 0**.
|
||||
- **348 автотестов**, 0 failures/errors/skipped.
|
||||
- **57 claims**: 27 P0 + 30 P1; **57/57 proven, P0 gaps = 0**.
|
||||
- **366 автотестов**, 0 failures/errors/skipped.
|
||||
- Mutation testing:
|
||||
- `CourseAccessService` — 75% test strength;
|
||||
- `PromotionPlanner` — 69%.
|
||||
@@ -194,8 +194,8 @@ cat build/reports/evidence/index.md
|
||||
```
|
||||
|
||||
Последний подтверждённый результат:
|
||||
- `348` тестов, 0 failures;
|
||||
- `51/51` claims;
|
||||
- `366` тестов, 0 failures;
|
||||
- `57/57` claims, P0 gaps = 0;
|
||||
- `ldd-release.sh` — PASS;
|
||||
- smoke — PASS;
|
||||
- browser E2E — 18/18 (Chromium/Firefox/WebKit);
|
||||
@@ -275,8 +275,8 @@ cat build/reports/evidence/index.md
|
||||
`feature/groups-courses`.
|
||||
2. Прочитать этот файл, затем `docs/16-stage6-report.md` §7 и
|
||||
`docs/evidence/baseline-2026-10-27.md`.
|
||||
3. `./gradlew test ktlintCheck detekt evidenceCheck` — должно быть 348 тестов
|
||||
и 51/51 claims.
|
||||
3. `./gradlew test ktlintCheck detekt evidenceCheck` — должно быть 366 тестов
|
||||
и 57 claims, 57/57 proven.
|
||||
4. Для release-проверки: `./scripts/ldd-release.sh`.
|
||||
5. Для быстрой проверки: `./scripts/ldd-smoke.sh` и `./scripts/ci-preflight.sh`.
|
||||
6. Продолжать backlog из §3, начиная с инвентаризации legacy-активов и
|
||||
|
||||
141
docs/18-goal-audit.md
Normal file
141
docs/18-goal-audit.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# 18. Сверка цели и фактического состояния
|
||||
|
||||
**Дата:** 2026-10-29.
|
||||
**Ветка:** `feature/groups-courses`.
|
||||
**Источники цели:** `07-groups-courses.md`, `08-groups-courses-plan.md`,
|
||||
`09-teacher-student-profile.md`, `16-stage6-report.md`, `17-handoff.md`.
|
||||
|
||||
Документ фиксирует, где приложение уже соответствует целевой модели, где
|
||||
остались отличия и какие решения приняты по спорным местам. Это не отчёт
|
||||
«всё готово»: статус `partial` означает реальный незакрытый пункт.
|
||||
|
||||
## 1. Сводка
|
||||
|
||||
| Область | Статус | Что осталось |
|
||||
|---|---|---|
|
||||
| Роли, группы-когорты, статусы, история | partial | Финальные `NOT NULL`/полная уникальность когорты; группа не восстанавливается из архива; история членства не показана в UI |
|
||||
| Академический отпуск | **исправлено** | Вход read-only, сдача/защиты заблокированы на progress-service |
|
||||
| Черновики лабораторных | **исправлено** | Студент не видит черновик в списке/деталях/по UUID |
|
||||
| «Убрать из группы» | **исправлено** | Действие удалено из UI и API; остались перевод/академ/отчисление/архив |
|
||||
| Импорт v2 | partial | Есть флаг восстановления неактивных; row-level отчёт для DB-конфликтов неполный; legacy-группы с NULL-полями |
|
||||
| Курсы, преподаватели, группы курса | implemented | — |
|
||||
| Материалы и активы | implemented | Глобальные `STUDENTS`/`TEACHERS` теперь работают; импорт глобальных глав всё ещё создаёт только `ALL` |
|
||||
| Профиль студента | partial | Браузерный URL использует UUID вместо `courseNo`; часть полей контракта 09 отличается; нет лимита/пагинации попыток; нет SQL-теста «без N+1» |
|
||||
| Повышение | partial | Догон при старте ищет только текущий год, не предыдущий; нет теста apply-конфликта |
|
||||
| Аудит | partial | Добавлены `GROUP_CREATE`, `STUDENT_CREATE`, `GROUP_IMPORT`, `GROUP_ENROLL`; отдельных тестов на эти события пока нет |
|
||||
| Backup/deploy | partial | `deploy.sh` теперь проверяет последний backup; restore на staging не подтверждён; первый деплой без backup — документированное исключение |
|
||||
| CI | partial | Конфигурация Woodpecker и локальные прогоны есть; первый реальный pipeline на VM не подтверждён |
|
||||
| Производительность | partial | Keyset-аудит, пакетные лабы и группы курса есть; списки материалов/групп без пагинации; сдачи в ведомости по выбранной лабе |
|
||||
| LDD | implemented | 57 claims (27 P0 / 30 P1), 57/57 proven после release; `check` доказывает P0/unit, runtime P1 — release |
|
||||
| Legacy-активы | partial | Код и UI готовы; инвентаризация на production/staging не выполнена |
|
||||
| «Контингент» | out of scope | Исключён решением 2026-10-28 |
|
||||
|
||||
## 2. Что исправлено в этой итерации
|
||||
|
||||
1. **Read-only академотпуск.**
|
||||
- `AuthResponse`/`MeResponse` содержат `readOnly`; web-сессия хранит флаг.
|
||||
- `StudentWriteGuard` в progress-service блокирует submit, start и finish защиты.
|
||||
- UI скрывает сдачу/защиту и показывает объяснение.
|
||||
- Тесты: `StudentLifecycleIntegrationTest#academic leave login is read only`,
|
||||
`SubmissionServiceTest#academic leave student cannot submit`,
|
||||
`DefenseServiceAcademicLeaveTest#academic leave student cannot start or finish defense`.
|
||||
|
||||
2. **Черновики лабораторных скрыты от студентов.**
|
||||
- `CourseAccessService.requireLabAccess` отдаёт `404` студенту для черновика.
|
||||
- `GET /api/v1/labs` и `/labs/details` фильтруют черновики для студента.
|
||||
- Тесты: `LabDetailsIntegrationTest#student sees published labs but not drafts in list details or by id`,
|
||||
`CourseAccessServiceTest#student cannot access a draft lab of own course`.
|
||||
|
||||
3. **Удалено действие «Убрать из группы».**
|
||||
- Удалены endpoint, web-маршрут, API-клиент, кнопка и сервисный метод.
|
||||
- Тесты шаблонов проверяют отсутствие действия.
|
||||
|
||||
4. **Импорт больше не воскрешает неактивных студентов без флага.**
|
||||
- `restoreInactiveStatuses` (по умолчанию `false`), `ImportResult.skipped`.
|
||||
- Без флага сохраняются `EXPELLED/GRADUATED/ARCHIVED/ACADEMIC_LEAVE`, строка попадает в отчёт.
|
||||
- С флагом студент активируется, архивный аккаунт разблокируется, пишется `IMPORT`.
|
||||
- Тесты: `GroupServiceIntegrationTest#import does not resurrect...`,
|
||||
`#import restores expelled student...`, `#import preserves academic leave...`,
|
||||
`#import still updates an active student`, шаблонные тесты отчёта.
|
||||
|
||||
5. **Глобальные материалы `STUDENTS`/`TEACHERS`.**
|
||||
- Дерево и статьи фильтруются по роли JWT; аноним — только `ALL`.
|
||||
- Админ выбирает аудиторию при создании и может изменить её.
|
||||
- Тесты: `GlobalAudienceAccessIntegrationTest` (5 сценариев).
|
||||
|
||||
6. **LDD gate на чистом checkout.**
|
||||
- `LDD-AUTH-005` теперь P0 с unit-тестом gateway; полный refresh/handshake вынесен в P1 `LDD-AUTH-008` (runtime).
|
||||
- `evidenceCheck` без `build/evidence` проходит по P0; runtime P1 закрываются в release.
|
||||
|
||||
7. **Backup/env.**
|
||||
- `LMS_REFRESH_TOKEN_CLEANUP_CRON` в примерах закавычен.
|
||||
- `backup.sh` больше не `source`-ит `.env.prod` (не исполняет cron как команду).
|
||||
- `deploy.sh up` после backup вызывает `verify-latest`.
|
||||
|
||||
8. **Robots/noindex.**
|
||||
- `robots.txt`; `X-Robots-Tag: noindex, nofollow` для admin/teacher/student/login/api/курсовых материалов.
|
||||
|
||||
9. **Профиль.**
|
||||
- Старые комментарии без автора показывают «автор не указан».
|
||||
- Студенческий профиль не показывает черновики лабораторных.
|
||||
|
||||
## 3. Остаточные отличия от цели
|
||||
|
||||
### P1
|
||||
|
||||
- **Импорт: ошибки БД.** Ошибки lookup/конфликта всё ещё могут выйти одним
|
||||
flash-сообщением без номера строки; нужен row-level отчёт и для них.
|
||||
- **Догон повышения.** `ensureScheduledDryRun` до 31 августа возвращает `null`
|
||||
и не создаёт план за предыдущий год; после долгого простоя в январе
|
||||
пропущенный август не догоняется.
|
||||
- **Профиль: контракт.** Браузерные маршруты используют UUID курса, а docs/09
|
||||
обещает `courseNo`; часть полей `variant/level/grade` отличается от контракта.
|
||||
Нужно либо привести код к контракту, либо официально обновить контракт.
|
||||
- **Backup restore на staging** не подтверждён артефактом/логом.
|
||||
- **Первый реальный CI pipeline** на Woodpecker VM не подтверждён.
|
||||
|
||||
### P2
|
||||
|
||||
- Когорта: поля всё ещё nullable, уникальность частичная; нужен preflight и
|
||||
финальный `SET NOT NULL`/полный unique index после ручного разбора legacy.
|
||||
- Нет восстановления группы из архива (сейчас архив односторонний).
|
||||
- Нет пагинации списков материалов и групп (аудит и профиль уже лучше).
|
||||
- Попытки сдачи в профиле не ограничены/не пагинированы.
|
||||
- Сдачи в ведомости запрашиваются по выбранной лабораторной, а не пакетно.
|
||||
- Нет визуальной регрессии и полного мобильного набора E2E.
|
||||
- Нет тестов на audit-события `GROUP_CREATE/STUDENT_CREATE/GROUP_IMPORT/GROUP_ENROLL`.
|
||||
- Mutation testing не 100%, нагрузочный smoke не SLA.
|
||||
|
||||
### Операционные
|
||||
|
||||
- Инвентаризация legacy-активов на реальном хранилище.
|
||||
- Установка/проверка daily backup timer на сервере.
|
||||
- Первый push-MR и tag pipeline в Woodpecker.
|
||||
- Restore на staging.
|
||||
|
||||
## 4. Принятые решения
|
||||
|
||||
- `ACADEMIC_LEAVE`: блокировать только сдачу и защиты; чтение и скачивание
|
||||
своих файлов разрешены.
|
||||
- «Убрать из группы»: действие удалено полностью.
|
||||
- Импорт терминальных/академических статусов: только по явному флагу
|
||||
`restoreInactiveStatuses`.
|
||||
- Глобальные материалы: `ALL/STUDENTS/TEACHERS` поддерживаются; `STUDENTS`
|
||||
видит студент, `TEACHERS` — преподаватель, аноним — только `ALL`.
|
||||
- «Контингент» остаётся вне scope.
|
||||
|
||||
## 5. LDD
|
||||
|
||||
- Реестр: `docs/evidence/claims.json`, **57 claims** (27 P0 / 30 P1).
|
||||
- `./gradlew test ktlintCheck detekt evidenceCheck` — зелёный; P0 gaps = 0.
|
||||
- `./scripts/ldd-release.sh` — PASS: **57/57 claims**, 366 тестов, mutation
|
||||
`CourseAccessService` 75%, `PromotionPlanner` 69%, browser E2E 18/18,
|
||||
runtime/load p95 < 1 с.
|
||||
|
||||
## 6. Следующий порядок работ
|
||||
|
||||
1. Закрыть P1: row-level ошибки импорта, догон повышения, контракт профиля,
|
||||
staging restore, первый CI pipeline.
|
||||
2. Закрыть P2: финальная миграция когорты, unarchive, пагинация, audit-тесты,
|
||||
визуальная регрессия.
|
||||
3. Прогнать полный release и обновить `evidence/baseline-*`.
|
||||
@@ -25,8 +25,9 @@ Preview Mermaid Support) и в IntelliJ IDEA (плагин Mermaid).
|
||||
| [15-stage5-course-materials.md](15-stage5-course-materials.md) | Отчёт этапа 5: курсовые материалы, публичные `ALL`-главы, видимость активов и secure-by-default legacy-файлы |
|
||||
| [16-stage6-report.md](16-stage6-report.md) | Отчёт этапа 6: права преподавателя на запись, аудит, CI, upgrade-тест миграций, security-матрица, чек-лист деплоя и остаточные ограничения |
|
||||
| [17-handoff.md](17-handoff.md) | Handoff: что уже внесено, что осталось, как восстановить контекст и прогнать проверки |
|
||||
| [18-goal-audit.md](18-goal-audit.md) | Сверка целевой модели из docs/07–09 с фактическим кодом: статусы, остаточные отличия, решения |
|
||||
| [evidence/README.md](evidence/README.md) | LDD: реестр утверждений, evidence gate, runtime- и migration-проверки |
|
||||
| [evidence/baseline-2026-10-27.md](evidence/baseline-2026-10-27.md) | LDD-baseline: 51 claims, 348 тестов, upgrade, runtime/scale, mutation и browser E2E |
|
||||
| [evidence/baseline-2026-10-27.md](evidence/baseline-2026-10-27.md) | LDD-baseline: 57 claims, 366 тестов, upgrade, runtime/scale, mutation и browser E2E |
|
||||
| [ADR 0001](adr/0001-groups-courses-foundation.md) | Ключевые архитектурные решения: курс C, миграции, доступ, статусы, повышение |
|
||||
|
||||
## Кратко о системе
|
||||
|
||||
@@ -87,8 +87,8 @@ Platform 6 из Spring Boot 4.
|
||||
LDD-циклы выполнены 2026-10-27/28; последний — после закрытия продуктовых
|
||||
рисков:
|
||||
|
||||
- **348 автоматических тестов**, 0 failures/errors;
|
||||
- **51 claims**: 25 P0 + 26 P1, **51/51 proven, P0 gaps = 0**. P0 — доступы,
|
||||
- **366 автоматических тестов**, 0 failures/errors;
|
||||
- **57 claims**: 27 P0 + 30 P1, **57/57 proven, P0 gaps = 0**. P0 — доступы,
|
||||
повышение, жизненный цикл, профиль/скачивание, материалы/активы
|
||||
(включая secure-by-default legacy-файлы), миграции V11–V13, аудит,
|
||||
refresh-токены и их отзыв; P1 — группы, авторство, UI, контракты, импорт,
|
||||
|
||||
@@ -40,7 +40,7 @@ Playwright. В ходе проверки найден и исправлен ре
|
||||
./gradlew test --rerun-tasks
|
||||
```
|
||||
|
||||
Результат: **348 тестов, 0 failures, 0 errors, 0 skipped**.
|
||||
Результат: **366 тестов, 0 failures, 0 errors, 0 skipped**.
|
||||
`ktlintCheck` и `detekt` — зелёные.
|
||||
|
||||
### 2.2. LDD gate
|
||||
@@ -49,7 +49,7 @@ Playwright. В ходе проверки найден и исправлен ре
|
||||
./gradlew evidenceCheck
|
||||
```
|
||||
|
||||
Результат: **51/51 claims proven, P0 gaps = 0**.
|
||||
Результат: **57/57 claims proven, P0 gaps = 0**.
|
||||
Отчёт: `build/reports/evidence/index.md`.
|
||||
|
||||
Gate устроен так:
|
||||
@@ -306,8 +306,8 @@ optional json fields`.
|
||||
|
||||
## 5. Вывод
|
||||
|
||||
Текущая функциональность прошла LDD-контур: **51/51 claims proven, P0 gaps = 0**,
|
||||
**348 тестов**, upgrade, mutation baseline, 18/18 browser E2E, smoke и
|
||||
Текущая функциональность прошла LDD-контур: **57/57 claims proven, P0 gaps = 0**,
|
||||
**366 тестов**, upgrade, mutation baseline, 18/18 browser E2E, smoke и
|
||||
масштабный load зелёные. Это достаточное основание продолжать доработки, но не
|
||||
«формальное доказательство всей системы».
|
||||
|
||||
|
||||
@@ -43,11 +43,11 @@
|
||||
"oracle": "401/404 для приватного; 200 только для ALL; состав публичного дерева",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.academic.content.CourseContentIntegrationTest#anonymous cannot read course content()",
|
||||
"ru.ysu.lms.academic.content.CourseContentIntegrationTest#public tree contains only global chapters with ALL audience()",
|
||||
"ru.ysu.lms.academic.content.CourseContentIntegrationTest#anonymous tree contains only global chapters with ALL audience()",
|
||||
"ru.ysu.lms.academic.content.ContentAssetAccessIntegrationTest#course asset requires authentication()",
|
||||
"ru.ysu.lms.academic.content.ContentAssetAccessIntegrationTest#published global ALL asset stays public()"
|
||||
],
|
||||
"limitations": "Legacy-активы без article_assets остаются публичными и вынесены в отдельный риск."
|
||||
"limitations": "Legacy-активы без article_assets закрыты отдельными claims LDD-ASSET-002/003."
|
||||
},
|
||||
{
|
||||
"id": "LDD-ACCESS-004",
|
||||
@@ -659,16 +659,18 @@
|
||||
},
|
||||
{
|
||||
"id": "LDD-AUTH-005",
|
||||
"title": "Refresh/logout работают через реальный gateway",
|
||||
"title": "Gateway пропускает refresh/logout без access-JWT",
|
||||
"criticality": "P0",
|
||||
"status": "active",
|
||||
"requirement": "docs/16-stage6-report.md, §4",
|
||||
"claim": "Через gateway refresh без access-токена выдаёт новую пару, новый access работает, logout отзывает refresh, повторный refresh — 401.",
|
||||
"oracle": "runtime.auth_refresh",
|
||||
"runtime": [
|
||||
"runtime.auth_refresh"
|
||||
],
|
||||
"limitations": "Проверено на одноразовом стеке; TTL не истекает в рамках smoke."
|
||||
"claim": "Gateway не требует access-JWT для /api/v1/auth/refresh и /api/v1/auth/logout; защищённый маршрут без токена получает 401. Полный цикл refresh/logout через реальный gateway проверяется отдельным P1 runtime-claim.",
|
||||
"oracle": "JwtAuthenticationFilterTest: public paths пропущены, protected path без токена — 401",
|
||||
"runtime": [],
|
||||
"limitations": "Unit-тест проверяет маршрутизацию gateway; полный обмен пары и отзыв refresh — LDD-AUTH-008.",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.gateway.security.JwtAuthenticationFilterTest#refresh and logout are public()",
|
||||
"ru.ysu.lms.gateway.security.JwtAuthenticationFilterTest#protected route without bearer token is rejected()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "LDD-AUTH-006",
|
||||
@@ -747,6 +749,97 @@
|
||||
"ru.ysu.lms.web.admin.LegacyAssetsTemplateTest#renders empty legacy assets state()"
|
||||
],
|
||||
"limitations": "Удаление файлов остаётся ручной операцией на стороне academic-service; UI только инвентаризирует."
|
||||
},
|
||||
{
|
||||
"id": "LDD-AUTH-008",
|
||||
"title": "Refresh/logout через реальный gateway",
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/16-stage6-report.md, §2.3",
|
||||
"claim": "Через gateway refresh без access-токена выдаёт новую пару, новый access работает, logout отзывает refresh, повторный refresh — 401.",
|
||||
"oracle": "runtime.auth_refresh",
|
||||
"runtime": [
|
||||
"runtime.auth_refresh"
|
||||
],
|
||||
"limitations": "Проверено на одноразовом стеке; TTL не истекает в рамках smoke."
|
||||
},
|
||||
{
|
||||
"id": "LDD-ACCESS-010",
|
||||
"title": "Черновики лабораторных скрыты от студентов",
|
||||
"criticality": "P0",
|
||||
"status": "active",
|
||||
"requirement": "docs/07-groups-courses.md, §5",
|
||||
"claim": "Студент не видит черновик лабораторной ни в списке, ни в пакетных деталях, ни по прямому UUID; преподаватель и админ видят черновик.",
|
||||
"oracle": "HTTP 200/404 и фильтрация списков",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.academic.lab.LabDetailsIntegrationTest#student sees published labs but not drafts in list details or by id()",
|
||||
"ru.ysu.lms.academic.course.CourseAccessServiceTest#student cannot access a draft lab of own course()"
|
||||
],
|
||||
"limitations": "Проверены academic API и список; отдельный тест прогресса на сдачу черновика не добавлялся, так как academic отдаёт 404."
|
||||
},
|
||||
{
|
||||
"id": "LDD-LIFE-004",
|
||||
"title": "Академический отпуск: вход только для чтения",
|
||||
"criticality": "P0",
|
||||
"status": "active",
|
||||
"requirement": "docs/07-groups-courses.md, §0.3",
|
||||
"claim": "Студент в ACADEMIC_LEAVE может войти и читать, но не может сдавать лабораторные и начинать/завершать защиту.",
|
||||
"oracle": "readOnly в login/me, ForbiddenException на submit/start/finish",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.identity.roster.StudentLifecycleIntegrationTest#academic leave login is read only()",
|
||||
"ru.ysu.lms.progress.SubmissionServiceTest#academic leave student cannot submit()",
|
||||
"ru.ysu.lms.progress.DefenseServiceAcademicLeaveTest#academic leave student cannot start or finish defense()"
|
||||
],
|
||||
"limitations": "Запрет проверяется на progress-service; UI скрывает кнопки, но не является единственной защитой."
|
||||
},
|
||||
{
|
||||
"id": "LDD-LIFE-005",
|
||||
"title": "Действие «Убрать из группы» удалено",
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/14-stage4-report.md, §3",
|
||||
"claim": "В админском UI и API больше нет отдельного действия unenroll; доступны только перевод, академ, отчисление, архив и восстановление.",
|
||||
"oracle": "Отсутствие маршрута/кнопки/метода и отрицательные проверки шаблона",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.web.admin.AdminStage4TemplateTest#renders group lifecycle buttons by student status()",
|
||||
"ru.ysu.lms.web.admin.TeacherGradebookTemplateTest#teacher gradebook has no admin-only controls()"
|
||||
],
|
||||
"limitations": "Удалённый API-маршрут не проверяется отдельным HTTP-тестом; отсутствие метода компилятор проверяет по коду."
|
||||
},
|
||||
{
|
||||
"id": "LDD-IMPORT-002",
|
||||
"title": "Импорт не воскрешает неактивных студентов без флага",
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/07-groups-courses.md, §9",
|
||||
"claim": "Повторный импорт сохраняет EXPELLED/GRADUATED/ARCHIVED/ACADEMIC_LEAVE без явного флага restoreInactiveStatuses и сообщает строку в skipped; с флагом активирует и зачисляет.",
|
||||
"oracle": "Статус/зачисление/ImportResult.skipped",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.identity.roster.GroupServiceIntegrationTest#import does not resurrect expelled student without explicit flag()",
|
||||
"ru.ysu.lms.identity.roster.GroupServiceIntegrationTest#import restores expelled student with explicit flag and records import history()",
|
||||
"ru.ysu.lms.identity.roster.GroupServiceIntegrationTest#import preserves academic leave without explicit flag()",
|
||||
"ru.ysu.lms.identity.roster.GroupServiceIntegrationTest#import still updates an active student()",
|
||||
"ru.ysu.lms.web.admin.AdminStage4TemplateTest#group import form requires explicit restoration flag()",
|
||||
"ru.ysu.lms.web.admin.AdminStage4TemplateTest#group import result shows skipped inactive rows()"
|
||||
],
|
||||
"limitations": "Флаг восстанавливает статус студента; архивный аккаунт может потребовать отдельного lifecycle-восстановления."
|
||||
},
|
||||
{
|
||||
"id": "LDD-CONTENT-003",
|
||||
"title": "Глобальные материалы STUDENTS/TEACHERS",
|
||||
"criticality": "P1",
|
||||
"status": "active",
|
||||
"requirement": "docs/07-groups-courses.md, §6",
|
||||
"claim": "Аноним видит только ALL; студент — ALL+STUDENTS; преподаватель — ALL+TEACHERS; прямой доступ к чужой аудитории даёт 404; админ может создать/сменить аудиторию.",
|
||||
"oracle": "Фильтрация дерева/статей по JWT-роли и authoring audience",
|
||||
"evidence": [
|
||||
"ru.ysu.lms.academic.content.GlobalAudienceAccessIntegrationTest#global tree is filtered by role audience()",
|
||||
"ru.ysu.lms.academic.content.GlobalAudienceAccessIntegrationTest#global article access follows audience and role()",
|
||||
"ru.ysu.lms.academic.content.GlobalAudienceAccessIntegrationTest#authoring creates requested audience and rejects invalid()",
|
||||
"ru.ysu.lms.academic.content.GlobalAudienceAccessIntegrationTest#changing audience immediately changes access()",
|
||||
"ru.ysu.lms.academic.content.GlobalAudienceAccessIntegrationTest#course chapter never gets audience and cannot change it()"
|
||||
],
|
||||
"limitations": "Админ видит все аудитории через админское дерево; публичное дерево с админским JWT также не скрывает аудитории."
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user