evasion-annotator / tests /test_app_flow.py
Gautier Marti
Fix broken-item flag progression
dea7109
Raw
History Blame Contribute Delete
6.7 kB
import importlib
import os
import re
from pathlib import Path
from fastapi.testclient import TestClient
def _csrf(html: str) -> str:
match = re.search(r'name="csrf_token" value="([^"]+)"', html)
assert match
return match.group(1)
def test_enrollment_assignment_submission_and_media_authorization(tmp_path: Path) -> None:
os.environ.update(
{
"ANNOTATOR_DB_PATH": str(tmp_path / "app.db"),
"ANNOTATOR_MEDIA_DIR": str(tmp_path / "media"),
"ANNOTATOR_COOKIE_SECURE": "false",
"REQUIRE_ACCESS_CODE": "false",
"STUDY_OPEN": "true",
"STUDY_PHASE": "development",
}
)
from app import auth as auth_module
from app import db as db_module
from app import main as main_module
importlib.reload(auth_module)
importlib.reload(db_module)
main = importlib.reload(main_module)
with TestClient(main.app, follow_redirects=False) as client:
login_page = client.get("/")
assert login_page.status_code == 200
response = client.post(
"/login",
data={
"csrf_token": _csrf(login_page.text),
"participant_id": "P-INTEGRATION",
"access_code": "unused",
"finance_familiarity": "some",
"english_proficiency": "fluent",
"consent": "yes",
},
)
assert response.status_code == 303
assert response.headers["location"] == "/instructions"
practice = client.get("/instructions")
assert practice.status_code == 200
trained = client.post(
"/instructions",
data={
"csrf_token": _csrf(practice.text),
"practice_1": "substantive_answer_attempt",
"practice_2": "clarification_repair",
"practice_3": "explicit_disclosure_boundary",
},
)
assert trained.status_code == 303
assert trained.headers["location"] == "/label"
label = client.get("/label")
assert label.status_code == 200
assert 'value="flag" class="btn btn-flag-confirm" formnovalidate' in label.text
task_id = int(re.search(r'name="task_id" value="(\d+)"', label.text).group(1))
session = main.db.get_session()
try:
task = session.get(main.db.StudyTask, task_id)
item = session.get(main.db.StudyItem, task.item_id)
media_path = Path(os.environ["ANNOTATOR_MEDIA_DIR"]) / item.audio_filename
media_path.parent.mkdir(parents=True, exist_ok=True)
media_path.write_bytes(b"ID3" + b"\0" * 2000)
if task.condition == "text_audio":
assert client.get(f"/media/{item.id}").status_code == 200
else:
assert client.get(f"/media/{item.id}").status_code == 404
finally:
session.close()
submit = client.post(
"/submit",
data={
"csrf_token": _csrf(label.text),
"task_id": str(task_id),
"action": "label",
"gate": "clarification_repair",
"gate_confidence": "90",
"gate_confidence_touched": "1",
"rationale": "The executive asks the analyst to clarify the intended scope.",
"response_time_ms": "31000",
"audio_played_ms": "12000" if task.condition == "text_audio" else "0",
"audio_completed": "1" if task.condition == "text_audio" else "0",
},
)
assert submit.status_code == 303
session = main.db.get_session()
try:
annotation = session.query(main.db.Annotation).filter_by(task_id=task_id).one()
assert annotation.gate == "clarification_repair"
assert annotation.rasiah is None
assert session.get(main.db.StudyTask, task_id).completed is True
finally:
session.close()
def test_flag_bypasses_normal_annotation_requirements_and_advances(tmp_path: Path) -> None:
os.environ.update(
{
"ANNOTATOR_DB_PATH": str(tmp_path / "app.db"),
"ANNOTATOR_MEDIA_DIR": str(tmp_path / "media"),
"ANNOTATOR_COOKIE_SECURE": "false",
"REQUIRE_ACCESS_CODE": "false",
"STUDY_OPEN": "true",
"STUDY_PHASE": "development",
}
)
from app import auth as auth_module
from app import db as db_module
from app import main as main_module
importlib.reload(auth_module)
importlib.reload(db_module)
main = importlib.reload(main_module)
with TestClient(main.app, follow_redirects=False) as client:
login_page = client.get("/")
login = client.post(
"/login",
data={
"csrf_token": _csrf(login_page.text),
"participant_id": "P-FLAG",
"access_code": "unused",
"finance_familiarity": "some",
"english_proficiency": "fluent",
"consent": "yes",
},
)
assert login.status_code == 303
practice = client.get("/instructions")
trained = client.post(
"/instructions",
data={
"csrf_token": _csrf(practice.text),
"practice_1": "substantive_answer_attempt",
"practice_2": "clarification_repair",
"practice_3": "explicit_disclosure_boundary",
},
)
assert trained.status_code == 303
label = client.get("/label")
task_id = int(re.search(r'name="task_id" value="(\d+)"', label.text).group(1))
flagged = client.post(
"/submit",
data={
"csrf_token": _csrf(label.text),
"task_id": str(task_id),
"action": "flag",
"flag_reason": "Wrong question–response pairing",
},
)
assert flagged.status_code == 303
assert flagged.headers["location"] == "/label"
next_label = client.get("/label")
next_task_id = int(re.search(r'name="task_id" value="(\d+)"', next_label.text).group(1))
assert next_task_id != task_id
session = main.db.get_session()
try:
annotation = session.query(main.db.Annotation).filter_by(task_id=task_id).one()
assert annotation.flagged_broken is True
assert annotation.flag_reason == "Wrong question–response pairing"
assert session.get(main.db.StudyTask, task_id).completed is True
finally:
session.close()