3.4 KiB
Спецификация: жизненный цикл программы (EnsureProgram, CreateDraft, Activate, Archive)
Сервис
ProgramStore(Server.WatchBody.Core\Programs\); работа — через репозитории m01. Ошибки —ProgramStoreException(message).
1. EnsureProgram(int athleteId, string title) → Program
Создаёт программу атлета при отсутствии (UNIQUE(athlete_id) — одна на
атлета), иначе возвращает существующую. title — только при создании.
| # | Тест | Ожидание |
|---|---|---|
| 1 | первый вызов | программа создана (title как задан) |
| 2 | повторный вызов с другим title | возвращена та же программа (id не меняется, title не перезаписывается) |
2. CreateDraft(int athleteId, string body, string author, string rationale, int? basedOnVersion = null) → ProgramVersion
Новая draft-версия. basedOnVersion = null → явное значение = актуальная
максимальная версия (цепочка истории); число валидируется против
существующих. author ∈ user | admin | rules (llm — v2, иначе
ошибка). body — непустой (не валидируется по схеме, v1). Тело после
создания не меняется (🛡); version = max+1 считает БД.
| # | Тест | Ожидание |
|---|---|---|
| 3 | первая версия атлета без программы | созданы program и версия 1 (draft, based_on = NULL) |
| 4 | вторая версия | version = 2, based_on_version = 1 |
| 5 | author = "llm" | ошибка (v2) |
| 6 | author = "rules" | создана (для m05) |
| 7 | basedOnVersion = 5 при max = 2 | ошибка (цепочка нарушена) |
| 8 | пустой body | ошибка |
| 9 | два draft подряд | version 3 и 4, обе draft (активация — отдельно) |
3. Activate(int athleteId, Guid versionId) → void
draft → active; прежняя активная версия программы (если была) →
archived (БД гарантирует ровно одну). Не-draft версия → ошибка; чужая
версия атлета → ошибка (изоляция).
| # | Тест | Ожидание |
|---|---|---|
| 10 | активация draft при активной v1 | v1 → archived, v2 → active; GetActive = v2 |
| 11 | активация первой версии (активной не было) | единственная active |
| 12 | активация archived-версии | ошибка (🛡 переходы только вперёд) |
| 13 | повторная активация уже активной | ошибка (не draft) |
| 14 | активация чужой версии | ошибка |
4. Archive(int athleteId, Guid versionId) → void
active → archived (например, атлет хочет остаться без активной
программы). Не-active → ошибка.
| # | Тест | Ожидание |
|---|---|---|
| 15 | архивация активной | archived, GetActive → null |
| 16 | архивация draft | ошибка |