def create_lesson(client): response = client.post( "/api/v1/sessions", json={"topic": "Gravity", "learner_level": "beginner"}, ) assert response.status_code == 201 return response.json()["id"] def test_complete_teaching_flow(client): session_id = create_lesson(client) with client.stream( "POST", f"/api/v1/sessions/{session_id}/teach", json={"text": "Why do things fall?"}, ) as response: body = "".join(response.iter_text()) assert response.status_code == 200 assert "event: text_delta" in body assert "event: lesson_payload" in body assert "event: audio_ready" in body assert "event: done" in body session = client.get(f"/api/v1/sessions/{session_id}").json() assert len(session["messages"]) == 2 assert len(session["artifacts"]) == 1 quiz_id = session["artifacts"][0]["quiz"][0]["id"] answer = client.post( f"/api/v1/sessions/{session_id}/quiz/{quiz_id}/answer", json={"answer": "Attracts"}, ) assert answer.status_code == 200 assert answer.json()["is_correct"] is True assert answer.json()["score"] == 1.0 def test_input_validation_and_missing_resources(client): session_id = create_lesson(client) empty = client.post( f"/api/v1/sessions/{session_id}/teach", json={"text": " "} ) assert empty.status_code == 422 assert empty.json()["detail"]["code"] == "EMPTY_TRANSCRIPT" missing = client.get("/api/v1/sessions/not-real") assert missing.status_code == 404 def test_audio_and_session_deletion(client): session_id = create_lesson(client) client.post( f"/api/v1/sessions/{session_id}/teach", json={"text": "Explain gravity."}, ) audio_id = "11111111-1111-1111-1111-111111111111" audio = client.get(f"/api/v1/audio/{audio_id}") assert audio.status_code == 200 assert audio.headers["content-type"] == "audio/mpeg" deleted = client.delete(f"/api/v1/sessions/{session_id}") assert deleted.status_code == 204 assert client.get(f"/api/v1/sessions/{session_id}").status_code == 404 assert client.get(f"/api/v1/audio/{audio_id}").status_code == 404 def test_live_websocket_voice_turn(client): session_id = create_lesson(client) with client.websocket_connect( f"/api/v1/sessions/{session_id}/live" ) as websocket: ready = websocket.receive_json() assert ready["type"] == "ready" assert ready["input_audio"]["sample_rate"] == 16000 assert ready["output_audio"]["sample_rate"] == 24000 websocket.send_bytes(b"fake-microphone-pcm") input_transcription = websocket.receive_json() output_transcription = websocket.receive_json() audio = websocket.receive_bytes() lesson_payload = websocket.receive_json() turn_complete = websocket.receive_json() assert input_transcription["type"] == "input_transcription" assert output_transcription["type"] == "output_transcription" assert audio == b"fake-live-pcm" assert lesson_payload["type"] == "lesson_payload" assert turn_complete["type"] == "turn_complete" session = client.get(f"/api/v1/sessions/{session_id}").json() assert [message["role"] for message in session["messages"]] == [ "student", "teacher", ] assert len(session["artifacts"]) == 1