2026-02-09 15:49:50 +05:30
|
|
|
# ruff: noqa: INP001
|
|
|
|
|
"""Schema validation tests for board and onboarding goal requirements."""
|
|
|
|
|
|
2026-02-06 16:12:04 +05:30
|
|
|
from uuid import uuid4
|
2026-02-05 14:40:48 +05:30
|
|
|
|
2026-02-07 00:21:44 +05:30
|
|
|
import pytest
|
|
|
|
|
|
2026-02-06 11:50:14 +05:30
|
|
|
from app.schemas.board_onboarding import BoardOnboardingConfirm
|
2026-02-11 18:19:29 +05:30
|
|
|
from app.schemas.boards import BoardCreate, BoardUpdate
|
2026-02-05 14:40:48 +05:30
|
|
|
|
|
|
|
|
|
2026-02-09 15:49:50 +05:30
|
|
|
def test_goal_board_requires_objective_and_metrics_when_confirmed() -> None:
|
|
|
|
|
"""Confirmed goal boards should require objective and success metrics."""
|
|
|
|
|
with pytest.raises(
|
|
|
|
|
ValueError,
|
|
|
|
|
match="Confirmed goal boards require objective and success_metrics",
|
|
|
|
|
):
|
2026-02-05 15:00:20 +05:30
|
|
|
BoardCreate(
|
|
|
|
|
name="Goal Board",
|
|
|
|
|
slug="goal",
|
2026-02-11 18:19:29 +05:30
|
|
|
description="Ship onboarding improvements.",
|
2026-02-06 16:12:04 +05:30
|
|
|
gateway_id=uuid4(),
|
2026-02-05 15:00:20 +05:30
|
|
|
board_type="goal",
|
|
|
|
|
goal_confirmed=True,
|
|
|
|
|
)
|
2026-02-05 14:40:48 +05:30
|
|
|
|
|
|
|
|
BoardCreate(
|
|
|
|
|
name="Goal Board",
|
|
|
|
|
slug="goal",
|
2026-02-11 18:19:29 +05:30
|
|
|
description="Ship onboarding improvements.",
|
2026-02-06 16:12:04 +05:30
|
|
|
gateway_id=uuid4(),
|
2026-02-05 14:40:48 +05:30
|
|
|
board_type="goal",
|
2026-02-05 15:00:20 +05:30
|
|
|
goal_confirmed=True,
|
2026-02-05 14:40:48 +05:30
|
|
|
objective="Launch onboarding",
|
|
|
|
|
success_metrics={"emails": 3},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-02-09 15:49:50 +05:30
|
|
|
def test_goal_board_allows_missing_objective_before_confirmation() -> None:
|
|
|
|
|
"""Draft goal boards may omit objective/success_metrics before confirmation."""
|
2026-02-11 18:19:29 +05:30
|
|
|
BoardCreate(
|
|
|
|
|
name="Draft",
|
|
|
|
|
slug="draft",
|
|
|
|
|
description="Iterate on backlog hygiene.",
|
|
|
|
|
gateway_id=uuid4(),
|
|
|
|
|
board_type="goal",
|
|
|
|
|
)
|
2026-02-05 15:00:20 +05:30
|
|
|
|
|
|
|
|
|
2026-02-09 15:49:50 +05:30
|
|
|
def test_general_board_allows_missing_objective() -> None:
|
|
|
|
|
"""General boards should allow missing goal-specific fields."""
|
|
|
|
|
BoardCreate(
|
|
|
|
|
name="General",
|
|
|
|
|
slug="general",
|
2026-02-11 18:19:29 +05:30
|
|
|
description="General coordination board.",
|
2026-02-09 15:49:50 +05:30
|
|
|
gateway_id=uuid4(),
|
|
|
|
|
board_type="general",
|
|
|
|
|
)
|
2026-02-06 11:50:14 +05:30
|
|
|
|
|
|
|
|
|
2026-02-11 18:19:29 +05:30
|
|
|
def test_board_create_requires_description() -> None:
|
|
|
|
|
"""Board creation should reject empty descriptions."""
|
|
|
|
|
with pytest.raises(ValueError, match="description is required"):
|
|
|
|
|
BoardCreate(
|
|
|
|
|
name="Goal Board",
|
|
|
|
|
slug="goal",
|
|
|
|
|
description=" ",
|
|
|
|
|
gateway_id=uuid4(),
|
|
|
|
|
board_type="goal",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_board_update_rejects_empty_description_patch() -> None:
|
|
|
|
|
"""Patch payloads should reject blank descriptions."""
|
|
|
|
|
with pytest.raises(ValueError, match="description is required"):
|
|
|
|
|
BoardUpdate(description=" ")
|
|
|
|
|
|
|
|
|
|
|
2026-02-12 23:05:33 +05:30
|
|
|
def test_board_rule_toggles_have_expected_defaults() -> None:
|
|
|
|
|
"""Boards should default to approval-gated done and optional review gating."""
|
|
|
|
|
created = BoardCreate(
|
|
|
|
|
name="Ops Board",
|
|
|
|
|
slug="ops-board",
|
|
|
|
|
description="Operations workflow board.",
|
|
|
|
|
gateway_id=uuid4(),
|
|
|
|
|
)
|
|
|
|
|
assert created.require_approval_for_done is True
|
|
|
|
|
assert created.require_review_before_done is False
|
2026-02-26 00:31:47 +05:30
|
|
|
assert created.comment_required_for_review is False
|
2026-02-12 23:05:33 +05:30
|
|
|
assert created.block_status_changes_with_pending_approval is False
|
2026-02-13 16:21:54 +05:30
|
|
|
assert created.only_lead_can_change_status is False
|
2026-02-14 19:43:16 +05:30
|
|
|
assert created.max_agents == 1
|
2026-02-12 23:05:33 +05:30
|
|
|
|
|
|
|
|
updated = BoardUpdate(
|
|
|
|
|
require_approval_for_done=False,
|
|
|
|
|
require_review_before_done=True,
|
2026-02-26 00:31:47 +05:30
|
|
|
comment_required_for_review=True,
|
2026-02-12 23:05:33 +05:30
|
|
|
block_status_changes_with_pending_approval=True,
|
2026-02-13 16:21:54 +05:30
|
|
|
only_lead_can_change_status=True,
|
2026-02-14 19:43:16 +05:30
|
|
|
max_agents=3,
|
2026-02-12 23:05:33 +05:30
|
|
|
)
|
|
|
|
|
assert updated.require_approval_for_done is False
|
|
|
|
|
assert updated.require_review_before_done is True
|
2026-02-26 00:31:47 +05:30
|
|
|
assert updated.comment_required_for_review is True
|
2026-02-12 23:05:33 +05:30
|
|
|
assert updated.block_status_changes_with_pending_approval is True
|
2026-02-13 16:21:54 +05:30
|
|
|
assert updated.only_lead_can_change_status is True
|
2026-02-14 19:43:16 +05:30
|
|
|
assert updated.max_agents == 3
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_board_max_agents_must_be_non_negative() -> None:
|
|
|
|
|
"""Board max_agents should reject negative values."""
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
BoardCreate(
|
|
|
|
|
name="Ops Board",
|
|
|
|
|
slug="ops-board",
|
|
|
|
|
description="Operations workflow board.",
|
|
|
|
|
gateway_id=uuid4(),
|
|
|
|
|
max_agents=-1,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
BoardUpdate(max_agents=-1)
|
2026-02-12 23:05:33 +05:30
|
|
|
|
|
|
|
|
|
2026-02-09 15:49:50 +05:30
|
|
|
def test_onboarding_confirm_requires_goal_fields() -> None:
|
|
|
|
|
"""Onboarding confirm should enforce goal fields for goal board types."""
|
|
|
|
|
with pytest.raises(
|
|
|
|
|
ValueError,
|
|
|
|
|
match="Confirmed goal boards require objective and success_metrics",
|
|
|
|
|
):
|
2026-02-06 11:50:14 +05:30
|
|
|
BoardOnboardingConfirm(board_type="goal")
|
|
|
|
|
|
2026-02-09 15:49:50 +05:30
|
|
|
with pytest.raises(
|
|
|
|
|
ValueError,
|
|
|
|
|
match="Confirmed goal boards require objective and success_metrics",
|
|
|
|
|
):
|
2026-02-06 11:50:14 +05:30
|
|
|
BoardOnboardingConfirm(board_type="goal", objective="Ship onboarding")
|
|
|
|
|
|
2026-02-09 15:49:50 +05:30
|
|
|
with pytest.raises(
|
|
|
|
|
ValueError,
|
|
|
|
|
match="Confirmed goal boards require objective and success_metrics",
|
|
|
|
|
):
|
2026-02-06 11:50:14 +05:30
|
|
|
BoardOnboardingConfirm(board_type="goal", success_metrics={"emails": 3})
|
|
|
|
|
|
|
|
|
|
BoardOnboardingConfirm(
|
|
|
|
|
board_type="goal",
|
|
|
|
|
objective="Ship onboarding",
|
|
|
|
|
success_metrics={"emails": 3},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
BoardOnboardingConfirm(board_type="general")
|