"""新建流程申请 must be visible in 我的申请, not only in the register. The submit button and the tracking surface are two different promises. The backend has had ``GET /me/approval-flows/requests`` since the feature shipped, but nothing read it and the cross-domain ``/me/requests`` aggregate — the thing 我的申请 actually renders — had no provider for this source. So an employee could file a request and then have nowhere at all to see what happened to it. """ from __future__ import annotations import uuid import pytest from app.domains.approval_registry import constants as const from app.domains.approval_registry import requests as reqs from app.domains.me_universe.my_requests import ( _APPROVED_IS_TERMINAL_SOURCES, SRC_APPROVAL_FLOW, build_my_requests, resolve_normalized_status, ) from app.identity.person_models import IdentityPerson pytestmark = pytest.mark.asyncio async def _person(db, name="Req"): p = IdentityPerson( id=uuid.uuid4(), work_email=f"{name.lower()}-{uuid.uuid4().hex[:6]}@test.com", display_name=name, employment_status="active", source_system="successfactors", hris_person_id=f"sf-{uuid.uuid4().hex[:8]}", ) db.add(p) await db.flush() await db.commit() return p async def _submit(db, person, name="设备借用申请"): row = await reqs.submit_request( db, requester=person, proposed_name=name, purpose="临时借用测试机、投影仪等设备。", proposed_category="行政", ) await db.commit() return row async def _mine(db, person): res = await build_my_requests(db, person_id=person.id, page_size=100) return [i for i in res.items if i.source == SRC_APPROVAL_FLOW] async def test_a_filed_request_shows_up_in_my_requests(db_session): """The gap the user hit: filed, then invisible.""" person = await _person(db_session) row = await _submit(db_session, person) mine = await _mine(db_session, person) assert len(mine) == 1 item = mine[0] assert item.id == f"{SRC_APPROVAL_FLOW}:{row.id}" assert item.title == "设备借用申请" assert item.category == "general" assert item.is_open is True # Pending is the one state the requester can still take back. assert item.can_cancel is True assert item.cancel_path is not None async def test_approved_is_not_done_here(db_session): """Every other 通用 source pins ``approved`` as terminal. This one must not: approved means IT agreed and has **not built it yet**, so the requester is still waiting. Showing 「已完成」 to somebody who still has no flow to submit is the #1108 bug class inverted. """ assert SRC_APPROVAL_FLOW not in _APPROVED_IS_TERMINAL_SOURCES person = await _person(db_session) row = await _submit(db_session, person) await reqs.decide_request(db_session, row, approve=True, actor=None) await db_session.commit() item = (await _mine(db_session, person))[0] assert item.raw_status == const.FLOW_REQ_APPROVED assert item.is_open is True, "approved 仍在等 IT 把流程建出来" # And it can no longer be withdrawn — IT already acted on it. assert item.can_cancel is False async def test_registered_is_the_only_done_state(db_session): """``registered`` was not in ``_STATUS_MAP``, so it fell through as the literal string — the unknown-status shape that renders an open row as closed on one surface and unknown on the other.""" assert resolve_normalized_status( SRC_APPROVAL_FLOW, "approval_flow_request", const.FLOW_REQ_REGISTERED, ) == "completed" person = await _person(db_session) row = await _submit(db_session, person) await reqs.decide_request(db_session, row, approve=True, actor=None) await db_session.commit() row = await reqs.get_request(db_session, row.id) await reqs.register_built_flow( db_session, row, approval_code=str(uuid.uuid4()).upper(), actor=None, ) await db_session.commit() item = (await _mine(db_session, person))[0] assert item.raw_status == const.FLOW_REQ_REGISTERED assert item.status == "completed" assert item.is_open is False async def test_clicking_it_does_not_fail_to_load(db_session): """A source listed in the tracker with no ``_DETAIL_BUILDERS`` entry hands its own requester a red 「加载失败」 on 查看. Provider and detail builder ship together or not at all.""" from app.domains.me_universe.request_detail import _DETAIL_BUILDERS assert SRC_APPROVAL_FLOW in _DETAIL_BUILDERS person = await _person(db_session) row = await _submit(db_session, person) detail = await _DETAIL_BUILDERS[SRC_APPROVAL_FLOW]( db_session, str(row.id), [str(person.id)], ) assert detail is not None assert detail.title == "设备借用申请" # Somebody else's request is not readable through it. other = await _person(db_session, "Other") assert await _DETAIL_BUILDERS[SRC_APPROVAL_FLOW]( db_session, str(row.id), [str(other.id)], ) is None async def test_only_my_own_requests_are_listed(db_session): """The aggregate is self-scoped; a second person's request must not appear in mine.""" me = await _person(db_session, "Me") them = await _person(db_session, "Them") await _submit(db_session, me, "我的申请") await _submit(db_session, them, "别人的申请") titles = [i.title for i in await _mine(db_session, me)] assert titles == ["我的申请"]