Files
server-core/tests/test_task_claim.py
T
Pine b9b5e2e2ff feat(task): 任务中心扫码接单(多端) —— 服务端任务状态机/端口/迁移
- Task 加 task_code/tags/display_priority/claimed_by/claimed_at/doing_at;新增 TaskClaim 流水表。
- TaskService 加 claimed/doing/completed 状态机(claim/start_doing/complete),grab 并入 claim。
- TaskRepository 加 list_published/get_by_code/update/claim/set_doing + TaskClaimRepository;挂 Database。
- 端口:
  · opc  /tasks/grab-by-code、/tasks/{id}/doing、/tasks/{id}/complete
  · operator POST /tasks(auto task_code) + PATCH /tasks/:id
  · park  /park/api/tasks(大屏展示,含 scan_payload 二维码载荷)
  · training /api/tasks/claim-by-code、/api/tasks/my(小程序 C 端账号→OPC 身份 find-or-create 领单)
- 迁移 0008(tasks 加列 + task_claims)已应用+stamp;seed 补 task_code/tags/grab demo。
- tests/test_task_claim.py(自包含内存库, 状态机+流水+list_published), 直接 async 校验通过。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-25 18:40:35 +08:00

93 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""任务中心·扫码接单:TaskService 状态机 + Repository 访问(自包含内存库)。"""
from __future__ import annotations
from contextlib import asynccontextmanager
import pytest
from fastapi import HTTPException
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from app.infrastructure.models import Base # noqa: F401 (注册表到 Base)
from app.infrastructure.repositories import Database
from app.services.task_service import TaskService
@asynccontextmanager
async def _db():
engine = create_async_engine("sqlite+aiosqlite:///:memory:")
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
Session = async_sessionmaker(engine, expire_on_commit=False)
async with Session() as session:
yield Database(session=session)
await engine.dispose()
def _grab_published(**kw):
return dict(
task_code="TK-004-GRAB", title="短视频剪辑", mode="grab",
status="published", category="技术开发", budget_min=1500, budget_max=3000,
display_priority=30, **kw,
)
async def test_claim_marks_claimed_and_records_claim():
"""published + grab → claimedclaimed_by/claimed_at),并写 task_claims 流水。"""
async with _db() as db:
t = await db.tasks.create(_grab_published())
actor = {"id": "u_opc_1", "username": "opc01", "nickname": "阿健"}
res = await TaskService(db).claim(t["task_code"], actor, source="scan")
assert res["status"] == "claimed"
assert res["claimed_by"] == "u_opc_1"
assert res["claimed_at"]
claims = await db.task_claims.list_by_task(t["id"])
assert claims and claims[0]["claim_source"] == "scan"
async def test_claim_disallowed_non_grab_mode():
"""bid 模式不可扫码接单。"""
async with _db() as db:
t = await db.tasks.create(_grab_published(title="UI", mode="bid"))
with pytest.raises(HTTPException):
await TaskService(db).claim(t["task_code"], {"id": "u1"}, "scan")
async def test_claim_unknown_code_404():
async with _db() as db:
with pytest.raises(HTTPException):
await TaskService(db).claim("TK-NOT-EXIST", {"id": "u1"}, "scan")
async def test_claim_already_claimed_rejected():
"""已 claim 的任务再 claim 应报错。"""
async with _db() as db:
t = await db.tasks.create(_grab_published())
await TaskService(db).claim(t["task_code"], {"id": "u1"}, "scan")
with pytest.raises(HTTPException):
await TaskService(db).claim(t["task_code"], {"id": "u2"}, "scan")
async def test_doing_then_complete():
"""claimed → doing → completed。"""
async with _db() as db:
t = await db.tasks.create(_grab_published())
actor = {"id": "u_opc_1", "username": "opc01", "nickname": "阿健"}
await TaskService(db).claim(t["task_code"], actor, source="scan")
doing = await TaskService(db).start_doing(t["id"], actor)
assert doing["status"] == "doing" and doing["doing_at"]
done = await TaskService(db).complete(t["id"], actor)
assert done["status"] == "completed"
async def test_list_published_returns_display_fields():
"""大屏取数:list_published 返回新字段且按 display_priority 降序。"""
async with _db() as db:
await db.tasks.create(_grab_published(task_code="TK-LOW", display_priority=1))
await db.tasks.create(_grab_published(task_code="TK-HIGH", display_priority=99))
items = await db.tasks.list_published()
codes = [t["task_code"] for t in items]
assert "TK-LOW" in codes and "TK-HIGH" in codes
assert codes.index("TK-HIGH") < codes.index("TK-LOW")
assert all("task_code" in t and "claimed_by" in t for t in items)