57 lines
3.4 KiB
Markdown
57 lines
3.4 KiB
Markdown
# Спецификация: жизненный цикл программы (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 | ошибка |
|