302 lines
11 KiB
Python
302 lines
11 KiB
Python
"""P0 单元测试:状态机转换 VALID_TRANSITIONS 全覆盖。"""
|
||
|
||
import pytest
|
||
|
||
pytestmark = pytest.mark.unit
|
||
|
||
from src.blackboard.db import VALID_TRANSITIONS
|
||
|
||
ALL_STATES = set(VALID_TRANSITIONS.keys())
|
||
|
||
|
||
class TestCompleteness:
|
||
"""完整性校验:测试覆盖的状态集合 == VALID_TRANSITIONS 的 key 集合。"""
|
||
|
||
def test_all_states_are_tested(self):
|
||
tested = {
|
||
"pending", "claimed", "working", "paused", "review",
|
||
"blocked", "failed", "escalated", "waiting_human",
|
||
"done", "reviewing", "cancelled",
|
||
}
|
||
assert tested == ALL_STATES
|
||
|
||
|
||
class TestPendingTransitions:
|
||
"""pending 状态的合法/非法转换。"""
|
||
|
||
@pytest.mark.parametrize("target", ["claimed", "paused", "blocked", "cancelled"])
|
||
def test_valid_from_pending(self, target):
|
||
assert target in VALID_TRANSITIONS["pending"]
|
||
|
||
def test_invalid_from_pending_to_working(self):
|
||
assert "working" not in VALID_TRANSITIONS["pending"]
|
||
|
||
def test_invalid_from_pending_to_done(self):
|
||
assert "done" not in VALID_TRANSITIONS["pending"]
|
||
|
||
def test_invalid_from_pending_to_review(self):
|
||
assert "review" not in VALID_TRANSITIONS["pending"]
|
||
|
||
def test_invalid_from_pending_to_failed(self):
|
||
assert "failed" not in VALID_TRANSITIONS["pending"]
|
||
|
||
def test_invalid_from_pending_to_escalated(self):
|
||
assert "escalated" not in VALID_TRANSITIONS["pending"]
|
||
|
||
def test_invalid_from_pending_to_waiting_human(self):
|
||
assert "waiting_human" not in VALID_TRANSITIONS["pending"]
|
||
|
||
|
||
class TestClaimedTransitions:
|
||
"""claimed 状态的合法/非法转换。"""
|
||
|
||
@pytest.mark.parametrize("target", ["working", "paused", "pending", "cancelled"])
|
||
def test_valid_from_claimed(self, target):
|
||
assert target in VALID_TRANSITIONS["claimed"]
|
||
|
||
def test_invalid_from_claimed_to_done(self):
|
||
assert "done" not in VALID_TRANSITIONS["claimed"]
|
||
|
||
def test_invalid_from_claimed_to_review(self):
|
||
assert "review" not in VALID_TRANSITIONS["claimed"]
|
||
|
||
def test_invalid_from_claimed_to_failed(self):
|
||
assert "failed" not in VALID_TRANSITIONS["claimed"]
|
||
|
||
def test_invalid_from_claimed_to_blocked(self):
|
||
assert "blocked" not in VALID_TRANSITIONS["claimed"]
|
||
|
||
def test_invalid_from_claimed_to_escalated(self):
|
||
assert "escalated" not in VALID_TRANSITIONS["claimed"]
|
||
|
||
|
||
class TestWorkingTransitions:
|
||
"""working 状态的合法/非法转换。"""
|
||
|
||
@pytest.mark.parametrize(
|
||
"target",
|
||
["review", "done", "blocked", "failed", "paused", "escalated", "waiting_human", "cancelled", "pending"],
|
||
)
|
||
def test_valid_from_working(self, target):
|
||
assert target in VALID_TRANSITIONS["working"]
|
||
|
||
def test_working_to_pending_is_valid(self):
|
||
"""重点:working → pending,Mail spawn 失败回退。"""
|
||
assert "pending" in VALID_TRANSITIONS["working"]
|
||
|
||
def test_invalid_from_working_to_claimed(self):
|
||
assert "claimed" not in VALID_TRANSITIONS["working"]
|
||
|
||
|
||
class TestPausedTransitions:
|
||
"""paused 状态的合法/非法转换——可恢复到多个状态。"""
|
||
|
||
@pytest.mark.parametrize("target", ["working", "claimed", "review", "escalated", "waiting_human", "cancelled"])
|
||
def test_valid_from_paused(self, target):
|
||
assert target in VALID_TRANSITIONS["paused"]
|
||
|
||
def test_paused_can_resume_to_multiple_states(self):
|
||
"""重点:paused 可以恢复到 working / claimed / review。"""
|
||
resume_targets = {"working", "claimed", "review"}
|
||
assert resume_targets.issubset(VALID_TRANSITIONS["paused"])
|
||
|
||
def test_invalid_from_paused_to_done(self):
|
||
assert "done" not in VALID_TRANSITIONS["paused"]
|
||
|
||
def test_invalid_from_paused_to_pending(self):
|
||
assert "pending" not in VALID_TRANSITIONS["paused"]
|
||
|
||
def test_invalid_from_paused_to_blocked(self):
|
||
assert "blocked" not in VALID_TRANSITIONS["paused"]
|
||
|
||
def test_invalid_from_paused_to_failed(self):
|
||
assert "failed" not in VALID_TRANSITIONS["paused"]
|
||
|
||
|
||
class TestReviewTransitions:
|
||
"""review 状态的合法/非法转换。"""
|
||
|
||
@pytest.mark.parametrize("target", ["done", "pending", "failed", "paused", "escalated", "waiting_human", "cancelled"])
|
||
def test_valid_from_review(self, target):
|
||
assert target in VALID_TRANSITIONS["review"]
|
||
|
||
def test_invalid_from_review_to_working(self):
|
||
assert "working" not in VALID_TRANSITIONS["review"]
|
||
|
||
def test_invalid_from_review_to_claimed(self):
|
||
assert "claimed" not in VALID_TRANSITIONS["review"]
|
||
|
||
def test_invalid_from_review_to_blocked(self):
|
||
assert "blocked" not in VALID_TRANSITIONS["review"]
|
||
|
||
|
||
class TestBlockedTransitions:
|
||
"""blocked 状态的合法/非法转换。"""
|
||
|
||
@pytest.mark.parametrize("target", ["pending", "escalated", "cancelled"])
|
||
def test_valid_from_blocked(self, target):
|
||
assert target in VALID_TRANSITIONS["blocked"]
|
||
|
||
def test_invalid_from_blocked_to_working(self):
|
||
assert "working" not in VALID_TRANSITIONS["blocked"]
|
||
|
||
def test_invalid_from_blocked_to_done(self):
|
||
assert "done" not in VALID_TRANSITIONS["blocked"]
|
||
|
||
def test_invalid_from_blocked_to_review(self):
|
||
assert "review" not in VALID_TRANSITIONS["blocked"]
|
||
|
||
def test_invalid_from_blocked_to_paused(self):
|
||
assert "paused" not in VALID_TRANSITIONS["blocked"]
|
||
|
||
def test_invalid_from_blocked_to_failed(self):
|
||
assert "failed" not in VALID_TRANSITIONS["blocked"]
|
||
|
||
|
||
class TestFailedTransitions:
|
||
"""failed 状态的合法/非法转换。"""
|
||
|
||
@pytest.mark.parametrize("target", ["pending", "escalated", "cancelled"])
|
||
def test_valid_from_failed(self, target):
|
||
assert target in VALID_TRANSITIONS["failed"]
|
||
|
||
def test_invalid_from_failed_to_working(self):
|
||
assert "working" not in VALID_TRANSITIONS["failed"]
|
||
|
||
def test_invalid_from_failed_to_done(self):
|
||
assert "done" not in VALID_TRANSITIONS["failed"]
|
||
|
||
def test_invalid_from_failed_to_review(self):
|
||
assert "review" not in VALID_TRANSITIONS["failed"]
|
||
|
||
def test_invalid_from_failed_to_paused(self):
|
||
assert "paused" not in VALID_TRANSITIONS["failed"]
|
||
|
||
def test_invalid_from_failed_to_claimed(self):
|
||
assert "claimed" not in VALID_TRANSITIONS["failed"]
|
||
|
||
|
||
class TestEscalatedTransitions:
|
||
"""escalated 状态的合法/非法转换。"""
|
||
|
||
@pytest.mark.parametrize("target", ["working", "pending", "paused", "cancelled"])
|
||
def test_valid_from_escalated(self, target):
|
||
assert target in VALID_TRANSITIONS["escalated"]
|
||
|
||
def test_invalid_from_escalated_to_done(self):
|
||
assert "done" not in VALID_TRANSITIONS["escalated"]
|
||
|
||
def test_invalid_from_escalated_to_review(self):
|
||
assert "review" not in VALID_TRANSITIONS["escalated"]
|
||
|
||
def test_invalid_from_escalated_to_blocked(self):
|
||
assert "blocked" not in VALID_TRANSITIONS["escalated"]
|
||
|
||
def test_invalid_from_escalated_to_failed(self):
|
||
assert "failed" not in VALID_TRANSITIONS["escalated"]
|
||
|
||
def test_invalid_from_escalated_to_claimed(self):
|
||
assert "claimed" not in VALID_TRANSITIONS["escalated"]
|
||
|
||
|
||
class TestWaitingHumanTransitions:
|
||
"""waiting_human 状态的合法/非法转换。"""
|
||
|
||
@pytest.mark.parametrize("target", ["working", "done", "paused", "cancelled"])
|
||
def test_valid_from_waiting_human(self, target):
|
||
assert target in VALID_TRANSITIONS["waiting_human"]
|
||
|
||
def test_invalid_from_waiting_human_to_pending(self):
|
||
assert "pending" not in VALID_TRANSITIONS["waiting_human"]
|
||
|
||
def test_invalid_from_waiting_human_to_review(self):
|
||
assert "review" not in VALID_TRANSITIONS["waiting_human"]
|
||
|
||
def test_invalid_from_waiting_human_to_claimed(self):
|
||
assert "claimed" not in VALID_TRANSITIONS["waiting_human"]
|
||
|
||
def test_invalid_from_waiting_human_to_blocked(self):
|
||
assert "blocked" not in VALID_TRANSITIONS["waiting_human"]
|
||
|
||
def test_invalid_from_waiting_human_to_failed(self):
|
||
assert "failed" not in VALID_TRANSITIONS["waiting_human"]
|
||
|
||
|
||
class TestDoneTransitions:
|
||
"""done 状态的合法/非法转换——只有 cancelled 和 reviewing。"""
|
||
|
||
@pytest.mark.parametrize("target", ["cancelled", "reviewing"])
|
||
def test_valid_from_done(self, target):
|
||
assert target in VALID_TRANSITIONS["done"]
|
||
|
||
def test_done_to_reviewing_is_valid(self):
|
||
"""重点:done → reviewing,审查回溯。"""
|
||
assert "reviewing" in VALID_TRANSITIONS["done"]
|
||
|
||
def test_invalid_from_done_to_working(self):
|
||
assert "working" not in VALID_TRANSITIONS["done"]
|
||
|
||
def test_invalid_from_done_to_pending(self):
|
||
assert "pending" not in VALID_TRANSITIONS["done"]
|
||
|
||
def test_invalid_from_done_to_review(self):
|
||
assert "review" not in VALID_TRANSITIONS["done"]
|
||
|
||
def test_invalid_from_done_to_paused(self):
|
||
assert "paused" not in VALID_TRANSITIONS["done"]
|
||
|
||
def test_invalid_from_done_to_blocked(self):
|
||
assert "blocked" not in VALID_TRANSITIONS["done"]
|
||
|
||
|
||
class TestReviewingTransitions:
|
||
"""reviewing 状态的合法/非法转换。"""
|
||
|
||
@pytest.mark.parametrize("target", ["done", "working", "cancelled"])
|
||
def test_valid_from_reviewing(self, target):
|
||
assert target in VALID_TRANSITIONS["reviewing"]
|
||
|
||
def test_invalid_from_reviewing_to_pending(self):
|
||
assert "pending" not in VALID_TRANSITIONS["reviewing"]
|
||
|
||
def test_invalid_from_reviewing_to_review(self):
|
||
assert "review" not in VALID_TRANSITIONS["reviewing"]
|
||
|
||
def test_invalid_from_reviewing_to_paused(self):
|
||
assert "paused" not in VALID_TRANSITIONS["reviewing"]
|
||
|
||
def test_invalid_from_reviewing_to_blocked(self):
|
||
assert "blocked" not in VALID_TRANSITIONS["reviewing"]
|
||
|
||
def test_invalid_from_reviewing_to_failed(self):
|
||
assert "failed" not in VALID_TRANSITIONS["reviewing"]
|
||
|
||
|
||
class TestCancelledTransitions:
|
||
"""cancelled 状态的合法/非法转换——唯一出路是回到 pending。"""
|
||
|
||
def test_valid_from_cancelled_to_pending(self):
|
||
"""重点:cancelled → pending,唯一能从 cancelled 出来的路径。"""
|
||
assert "pending" in VALID_TRANSITIONS["cancelled"]
|
||
|
||
def test_cancelled_only_has_pending(self):
|
||
assert VALID_TRANSITIONS["cancelled"] == {"pending"}
|
||
|
||
def test_invalid_from_cancelled_to_working(self):
|
||
assert "working" not in VALID_TRANSITIONS["cancelled"]
|
||
|
||
def test_invalid_from_cancelled_to_done(self):
|
||
assert "done" not in VALID_TRANSITIONS["cancelled"]
|
||
|
||
def test_invalid_from_cancelled_to_review(self):
|
||
assert "review" not in VALID_TRANSITIONS["cancelled"]
|
||
|
||
def test_invalid_from_cancelled_to_failed(self):
|
||
assert "failed" not in VALID_TRANSITIONS["cancelled"]
|
||
|
||
def test_invalid_from_cancelled_to_paused(self):
|
||
assert "paused" not in VALID_TRANSITIONS["cancelled"]
|
||
|
||
def test_invalid_from_cancelled_to_claimed(self):
|
||
assert "claimed" not in VALID_TRANSITIONS["cancelled"]
|