feat: complete leave approval MVP
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app, get_progress_gateway
|
||||
from app.models import LeaveProgressAnswerRequest
|
||||
|
||||
|
||||
class FakeProgressGateway:
|
||||
async def answer_progress(self, request: LeaveProgressAnswerRequest) -> str:
|
||||
assert request.context.requestId == "leave-1"
|
||||
assert request.context.activeTaskNames == ["主管审批"]
|
||||
return "当前状态为审批中,正在等待主管审批。"
|
||||
|
||||
|
||||
def test_answers_only_from_authorized_structured_context() -> None:
|
||||
app.dependency_overrides[get_progress_gateway] = lambda: FakeProgressGateway()
|
||||
try:
|
||||
response = TestClient(app).post(
|
||||
"/v1/leave-progress/answer",
|
||||
json={
|
||||
"question": "我的请假到哪一步了?",
|
||||
"timezone": "Asia/Shanghai",
|
||||
"context": {
|
||||
"requestId": "leave-1",
|
||||
"type": "ANNUAL",
|
||||
"status": "PENDING",
|
||||
"startsAt": "2026-07-20T01:00:00Z",
|
||||
"endsAt": "2026-07-20T09:00:00Z",
|
||||
"activeTaskNames": ["主管审批"],
|
||||
"completedTaskNames": [],
|
||||
"processEnded": False,
|
||||
"timelineEventTypes": ["LEAVE_REQUEST_SUBMITTED"],
|
||||
},
|
||||
},
|
||||
)
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"answer": "当前状态为审批中,正在等待主管审批。", "model": "qwen-plus"}
|
||||
assert "processVariables" not in response.text
|
||||
|
||||
|
||||
def test_rejects_unknown_context_fields() -> None:
|
||||
response = TestClient(app).post(
|
||||
"/v1/leave-progress/answer",
|
||||
json={
|
||||
"question": "进度?",
|
||||
"timezone": "Asia/Shanghai",
|
||||
"context": {
|
||||
"requestId": "leave-1", "type": "ANNUAL", "status": "PENDING",
|
||||
"startsAt": "2026-07-20T01:00:00Z", "endsAt": "2026-07-20T09:00:00Z",
|
||||
"activeTaskNames": [], "completedTaskNames": [], "processEnded": False,
|
||||
"timelineEventTypes": [], "processVariables": {"approverId": "secret"},
|
||||
},
|
||||
},
|
||||
)
|
||||
assert response.status_code == 422
|
||||
@@ -0,0 +1,46 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app, get_gateway
|
||||
from app.models import LeaveDraftSuggestion, LeaveDraftSuggestionRequest, LeaveType
|
||||
|
||||
|
||||
class FakeGateway:
|
||||
async def suggest(self, request: LeaveDraftSuggestionRequest) -> LeaveDraftSuggestion:
|
||||
assert request.text == "明天下午请事假四小时"
|
||||
return LeaveDraftSuggestion(
|
||||
type=LeaveType.PERSONAL,
|
||||
startsAt=datetime.fromisoformat("2026-07-19T13:30:00+08:00"),
|
||||
endsAt=datetime.fromisoformat("2026-07-19T17:30:00+08:00"),
|
||||
reason="办理个人事务",
|
||||
assumptions=["下午按 13:30 开始计算"],
|
||||
)
|
||||
|
||||
|
||||
def test_returns_structured_suggestion_without_executing_business_action() -> None:
|
||||
app.dependency_overrides[get_gateway] = lambda: FakeGateway()
|
||||
try:
|
||||
response = TestClient(app).post(
|
||||
"/v1/leave-drafts/suggest",
|
||||
json={"text": "明天下午请事假四小时", "timezone": "Asia/Shanghai"},
|
||||
)
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
assert body["suggestion"]["type"] == "PERSONAL"
|
||||
assert body["requiresUserConfirmation"] is True
|
||||
assert "applicantId" not in body["suggestion"]
|
||||
|
||||
|
||||
def test_rejects_invalid_time_range_from_model() -> None:
|
||||
try:
|
||||
LeaveDraftSuggestion(
|
||||
startsAt=datetime.fromisoformat("2026-07-19T17:30:00+08:00"),
|
||||
endsAt=datetime.fromisoformat("2026-07-19T13:30:00+08:00"),
|
||||
)
|
||||
assert False, "validation should fail"
|
||||
except ValueError:
|
||||
pass
|
||||
Reference in New Issue
Block a user