Files
roboco/tests/unit/api/routes/test_x_project_fields.py
T
7804e0fafa [f8480831] Batch B: extract route helpers in remaining smaller-offender route files (#760)
* [f8480831] refactor(api): extract route-layer helpers into services/schemas/utils (batch B)

Moves 28 non-@router-decorated helper functions out of 15 route files
(optimal, project, release, dashboard, pitch, x, docs, git, playbooks,
product, provider, research, secretary, system, work_session) into
their paired services module (DB/service-calling helpers), the route's
schemas module as a converter (pure response/request shaping, mirroring
the existing project_to_response/assignment_to_response pattern), or
roboco/utils/converters.py (pure generic helpers). Adds two small
shared role-check helpers to api/deps.py (require_auditor_or_ceo,
require_role_in) for endpoint-specific role gates that had no existing
home. Placement-only: no route paths, schemas, or observable behavior
changed. Fixes the handful of tests that imported the old private
helper names directly.

* [f8480831] docs(map): document Batch B route-helper relocation in api-routes-schemas.md

* [f8480831] docs(map): add Key Symbols rows for require_auditor_or_ceo/require_role_in

---------

Co-authored-by: Backend Developer 2 <be-dev-2@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
2026-07-31 20:23:10 +00:00

80 lines
2.7 KiB
Python

"""``roboco/api/schemas/x.py`` response-builder wiring for project_slug/
project_name. The sa_inspect(task).unloaded guard branches themselves are
covered once on the shared helper in tests/unit/api/schemas/test_project_fields.py
— this only asserts task_to_post_response/task_to_post_history_response
actually populate the response from it (loaded case; a real ORM task always
resolves the "loaded" branch since ``project`` is lazy="joined")."""
from __future__ import annotations
from types import SimpleNamespace
from typing import Any
from unittest.mock import MagicMock, patch
from roboco.api.schemas.x import task_to_post_history_response, task_to_post_response
def _stub_task(*, with_project: bool = False) -> Any:
"""A TaskTable stand-in matching the response builders' reads."""
return SimpleNamespace(
id="task-1",
source="x_post",
title="X post: release v1.0.0",
status="pending",
description="",
orchestration_markers=None,
project=(
SimpleNamespace(slug="acme-robotics", name="Acme Robotics")
if with_project
else None
),
updated_at=None,
created_at="2026-07-18T00:00:00+00:00",
)
def _loaded_inspector() -> MagicMock:
inspector = MagicMock()
inspector.unloaded = set()
return inspector
def test_to_response_includes_project_fields_when_loaded() -> None:
with patch(
"roboco.api.schemas.project_fields.sa_inspect",
return_value=_loaded_inspector(),
):
resp = task_to_post_response(_stub_task(with_project=True))
assert resp.project_slug == "acme-robotics"
assert resp.project_name == "Acme Robotics"
def test_to_response_omits_project_fields_when_project_unset() -> None:
with patch(
"roboco.api.schemas.project_fields.sa_inspect",
return_value=_loaded_inspector(),
):
resp = task_to_post_response(_stub_task(with_project=False))
assert resp.project_slug is None
assert resp.project_name is None
def test_to_history_response_includes_project_fields_when_loaded() -> None:
with patch(
"roboco.api.schemas.project_fields.sa_inspect",
return_value=_loaded_inspector(),
):
resp = task_to_post_history_response(_stub_task(with_project=True))
assert resp.project_slug == "acme-robotics"
assert resp.project_name == "Acme Robotics"
def test_to_history_response_omits_project_fields_when_project_unset() -> None:
with patch(
"roboco.api.schemas.project_fields.sa_inspect",
return_value=_loaded_inspector(),
):
resp = task_to_post_history_response(_stub_task(with_project=False))
assert resp.project_slug is None
assert resp.project_name is None