generated from kgod/ai-review-template
feat: start resume sessions in new flow
This commit is contained in:
+82
-14
@@ -1,8 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
@@ -46,11 +48,7 @@ def start_manual_profile(
|
||||
session_id = body["session_id"]
|
||||
assert body["stage"] == "PRIVACY_CONSENT"
|
||||
|
||||
source = event(client, session_id, body, "accept", {"accepted": True})
|
||||
assert source.status_code == 200
|
||||
assert source.json()["stage"] == "RESUME_SOURCE_SELECT"
|
||||
|
||||
phone_selector = event(client, session_id, source.json(), "select", {"value": "manual"})
|
||||
phone_selector = event(client, session_id, body, "accept", {"accepted": True})
|
||||
assert phone_selector.status_code == 200
|
||||
assert phone_selector.json()["stage"] == "PHONE_SELECTION"
|
||||
|
||||
@@ -109,22 +107,92 @@ def campus_ready(client: TestClient) -> tuple[str, dict[str, Any]]:
|
||||
return start_manual_profile(client, job_type="campus")
|
||||
|
||||
|
||||
def test_privacy_precedes_resume_source_selection(client: TestClient) -> None:
|
||||
def test_privacy_continues_directly_into_new_resume_flow(client: TestClient) -> None:
|
||||
created = client.post(f"{BASE}/sessions", json={})
|
||||
session_id = created.json()["session_id"]
|
||||
source = event(client, session_id, created.json(), "accept", {"accepted": True})
|
||||
assert source.status_code == 200
|
||||
body = source.json()
|
||||
assert body["stage"] == "RESUME_SOURCE_SELECT"
|
||||
options = active_component(body)["data"]["options"]
|
||||
assert {option["value"] for option in options} == {"import", "manual"}
|
||||
phone_selector = event(client, session_id, created.json(), "accept", {"accepted": True})
|
||||
assert phone_selector.status_code == 200
|
||||
body = phone_selector.json()
|
||||
assert body["stage"] == "PHONE_SELECTION"
|
||||
assert active_component(body)["data"]["component"] == "resume_phone_selector"
|
||||
assert "导入已有简历" not in json.dumps(body, ensure_ascii=False)
|
||||
|
||||
with client.app.state.database.transaction() as connection:
|
||||
session = client.app.state.database.fetch_session(connection, session_id)
|
||||
assert session is not None
|
||||
assert session["profile"]["resume_source"] == "manual"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("legacy_stage", ["RESUME_SOURCE_SELECT", "RESUME_IMPORT_UPLOAD"])
|
||||
def test_legacy_source_stages_advance_to_new_resume_flow(
|
||||
client: TestClient, legacy_stage: str
|
||||
) -> None:
|
||||
created = client.post(f"{BASE}/sessions", json={}).json()
|
||||
session_id = created["session_id"]
|
||||
accepted = event(client, session_id, created, "accept", {"accepted": True})
|
||||
assert accepted.status_code == 200
|
||||
|
||||
with client.app.state.database.transaction(immediate=True) as connection:
|
||||
session = client.app.state.database.fetch_session(connection, session_id)
|
||||
assert session is not None
|
||||
profile = dict(session["profile"])
|
||||
profile["resume_source"] = "import"
|
||||
client.app.state.database.update_session(
|
||||
connection,
|
||||
session_id,
|
||||
stage=legacy_stage,
|
||||
profile=profile,
|
||||
)
|
||||
|
||||
migrated = client.get(f"{BASE}/sessions/{session_id}/timeline")
|
||||
assert migrated.status_code == 200
|
||||
body = migrated.json()
|
||||
assert body["stage"] == "PHONE_SELECTION"
|
||||
assert active_component(body)["data"]["component"] == "resume_phone_selector"
|
||||
turn_count = len(body["turns"])
|
||||
|
||||
repeated = client.get(f"{BASE}/sessions/{session_id}/timeline").json()
|
||||
assert len(repeated["turns"]) == turn_count
|
||||
with client.app.state.database.transaction() as connection:
|
||||
session = client.app.state.database.fetch_session(connection, session_id)
|
||||
assert session is not None
|
||||
assert session["profile"]["resume_source"] == "manual"
|
||||
|
||||
|
||||
def test_concurrent_legacy_stage_refresh_advances_only_once(client: TestClient) -> None:
|
||||
created = client.post(f"{BASE}/sessions", json={}).json()
|
||||
session_id = created["session_id"]
|
||||
accepted = event(client, session_id, created, "accept", {"accepted": True})
|
||||
assert accepted.status_code == 200
|
||||
baseline_turn_count = len(
|
||||
client.get(f"{BASE}/sessions/{session_id}/timeline").json()["turns"]
|
||||
)
|
||||
|
||||
with client.app.state.database.transaction(immediate=True) as connection:
|
||||
session = client.app.state.database.fetch_session(connection, session_id)
|
||||
assert session is not None
|
||||
profile = dict(session["profile"])
|
||||
profile["resume_source"] = "import"
|
||||
client.app.state.database.update_session(
|
||||
connection,
|
||||
session_id,
|
||||
stage="RESUME_SOURCE_SELECT",
|
||||
profile=profile,
|
||||
)
|
||||
|
||||
agent = client.app.state.resume_agent
|
||||
with ThreadPoolExecutor(max_workers=4) as executor:
|
||||
responses = list(executor.map(lambda _: agent.timeline(session_id), range(4)))
|
||||
|
||||
assert all(response.stage == "PHONE_SELECTION" for response in responses)
|
||||
timeline = client.get(f"{BASE}/sessions/{session_id}/timeline").json()
|
||||
assert len(timeline["turns"]) == baseline_turn_count + 1
|
||||
|
||||
|
||||
def test_manual_phone_is_strict_and_retryable(client: TestClient) -> None:
|
||||
created = client.post(f"{BASE}/sessions", json={}).json()
|
||||
session_id = created["session_id"]
|
||||
source = event(client, session_id, created, "accept", {"accepted": True}).json()
|
||||
phone_selector = event(client, session_id, source, "select", {"value": "manual"}).json()
|
||||
phone_selector = event(client, session_id, created, "accept", {"accepted": True}).json()
|
||||
phone_input = event(client, session_id, phone_selector, "select", {"source": "other"}).json()
|
||||
|
||||
invalid = event(client, session_id, phone_input, "submit", {"phone": "+8613800138000"})
|
||||
|
||||
Reference in New Issue
Block a user