feat(campaigns): add pending_approval state to campaign domain entity
This commit is contained in:
@@ -33,6 +33,8 @@ class CampaignStatus(str, enum.Enum):
|
|||||||
|
|
||||||
# Assign draft = "draft"
|
# Assign draft = "draft"
|
||||||
draft = "draft"
|
draft = "draft"
|
||||||
|
# Assign pending_approval = "pending_approval"
|
||||||
|
pending_approval = "pending_approval"
|
||||||
# Assign active = "active"
|
# Assign active = "active"
|
||||||
active = "active"
|
active = "active"
|
||||||
# Assign completed = "completed"
|
# Assign completed = "completed"
|
||||||
@@ -57,7 +59,8 @@ class CampaignType(str, enum.Enum):
|
|||||||
|
|
||||||
# Assign VALID_TRANSITIONS = {
|
# Assign VALID_TRANSITIONS = {
|
||||||
VALID_TRANSITIONS: dict[CampaignStatus, list[CampaignStatus]] = {
|
VALID_TRANSITIONS: dict[CampaignStatus, list[CampaignStatus]] = {
|
||||||
CampaignStatus.draft: [CampaignStatus.active],
|
CampaignStatus.draft: [CampaignStatus.pending_approval, CampaignStatus.active],
|
||||||
|
CampaignStatus.pending_approval: [CampaignStatus.active, CampaignStatus.draft],
|
||||||
CampaignStatus.active: [CampaignStatus.completed],
|
CampaignStatus.active: [CampaignStatus.completed],
|
||||||
CampaignStatus.completed: [CampaignStatus.archived],
|
CampaignStatus.completed: [CampaignStatus.archived],
|
||||||
CampaignStatus.archived: [],
|
CampaignStatus.archived: [],
|
||||||
@@ -132,6 +135,49 @@ class CampaignEntity:
|
|||||||
# Assign self.status = CampaignStatus.active
|
# Assign self.status = CampaignStatus.active
|
||||||
self.status = CampaignStatus.active
|
self.status = CampaignStatus.active
|
||||||
|
|
||||||
|
def submit_for_approval(self) -> None:
|
||||||
|
"""Transition the campaign from ``draft`` to ``pending_approval``.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
if not self.can_transition_to(CampaignStatus.pending_approval):
|
||||||
|
raise InvalidStateTransition(
|
||||||
|
self.status.value, CampaignStatus.pending_approval.value,
|
||||||
|
[s.value for s in VALID_TRANSITIONS[self.status]],
|
||||||
|
)
|
||||||
|
if self.test_count == 0:
|
||||||
|
raise BusinessRuleViolation(
|
||||||
|
"Campaign must have at least one test to submit for approval"
|
||||||
|
)
|
||||||
|
self.status = CampaignStatus.pending_approval
|
||||||
|
|
||||||
|
def approve(self) -> None:
|
||||||
|
"""Transition the campaign from ``pending_approval`` to ``active``.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
if self.status != CampaignStatus.pending_approval:
|
||||||
|
raise InvalidStateTransition(
|
||||||
|
self.status.value, CampaignStatus.active.value,
|
||||||
|
[s.value for s in VALID_TRANSITIONS[self.status]],
|
||||||
|
)
|
||||||
|
self.status = CampaignStatus.active
|
||||||
|
|
||||||
|
def reject(self) -> None:
|
||||||
|
"""Transition the campaign from ``pending_approval`` back to ``draft``.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
if not self.can_transition_to(CampaignStatus.draft):
|
||||||
|
raise InvalidStateTransition(
|
||||||
|
self.status.value, CampaignStatus.draft.value,
|
||||||
|
[s.value for s in VALID_TRANSITIONS[self.status]],
|
||||||
|
)
|
||||||
|
self.status = CampaignStatus.draft
|
||||||
|
|
||||||
# Define function complete
|
# Define function complete
|
||||||
def complete(self) -> None:
|
def complete(self) -> None:
|
||||||
"""Transition the campaign from ``active`` to ``completed``.
|
"""Transition the campaign from ``active`` to ``completed``.
|
||||||
|
|||||||
@@ -173,3 +173,55 @@ def test_from_orm_handles_none_tags():
|
|||||||
orm.tags = None
|
orm.tags = None
|
||||||
e = CampaignEntity.from_orm(orm)
|
e = CampaignEntity.from_orm(orm)
|
||||||
assert e.tags == []
|
assert e.tags == []
|
||||||
|
|
||||||
|
|
||||||
|
# ── 9. Submit for approval ────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def test_submit_for_approval_from_draft_with_tests_success():
|
||||||
|
e = _entity("draft", test_count=1)
|
||||||
|
e.submit_for_approval()
|
||||||
|
assert e.status == CampaignStatus.pending_approval
|
||||||
|
|
||||||
|
|
||||||
|
def test_submit_for_approval_from_draft_with_zero_tests_raises():
|
||||||
|
e = _entity("draft", test_count=0)
|
||||||
|
with pytest.raises(BusinessRuleViolation, match="at least one test"):
|
||||||
|
e.submit_for_approval()
|
||||||
|
assert e.status == CampaignStatus.draft
|
||||||
|
|
||||||
|
|
||||||
|
def test_submit_for_approval_from_active_raises():
|
||||||
|
e = _entity("active", test_count=1)
|
||||||
|
with pytest.raises(InvalidStateTransition):
|
||||||
|
e.submit_for_approval()
|
||||||
|
|
||||||
|
|
||||||
|
# ── 10. Approve ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def test_approve_from_pending_approval_success():
|
||||||
|
e = _entity("pending_approval", test_count=1)
|
||||||
|
e.approve()
|
||||||
|
assert e.status == CampaignStatus.active
|
||||||
|
|
||||||
|
|
||||||
|
def test_approve_from_draft_raises():
|
||||||
|
e = _entity("draft", test_count=1)
|
||||||
|
with pytest.raises(InvalidStateTransition):
|
||||||
|
e.approve()
|
||||||
|
|
||||||
|
|
||||||
|
# ── 11. Reject ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def test_reject_from_pending_approval_returns_to_draft():
|
||||||
|
e = _entity("pending_approval", test_count=1)
|
||||||
|
e.reject()
|
||||||
|
assert e.status == CampaignStatus.draft
|
||||||
|
|
||||||
|
|
||||||
|
def test_reject_from_draft_raises():
|
||||||
|
e = _entity("draft", test_count=1)
|
||||||
|
with pytest.raises(InvalidStateTransition):
|
||||||
|
e.reject()
|
||||||
|
|||||||
Reference in New Issue
Block a user