VEU.WathBody/1-docs/4-modules/m03-program-store/api_specs/01-lifecycle.md

3.4 KiB
Raw Blame History

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