feat(reader): multi-word and phrasal verb detector in reader (Task 14)

This commit is contained in:
2026-10-06 15:16:02 +03:00
parent 37a1f1ab35
commit 1c3f03a64c
9 changed files with 738 additions and 16 deletions
+89
View File
@@ -1271,4 +1271,93 @@ def test_sentence_translation_ai_and_caching():
assert "active_model" in status_data
def test_phrasal_verb_detector_unit_and_integration():
"""Validates phrasal verb lemmatization, context detection, separable verbs, and API integration."""
from app.phrasal import lemmatize_word, detect_phrasal_verb
# 1. Lemmatization tests
assert "take" in lemmatize_word("took")
assert "give" in lemmatize_word("giving")
assert "figure" in lemmatize_word("figured")
assert "run" in lemmatize_word("running")
# 2. Direct unit detection tests
# Contiguous 2-word phrasal verb: clicking verb or particle
res_verb = detect_phrasal_verb("The plane took off into the sky.", "took")
assert res_verb is not None
assert res_verb["term"] == "take off"
assert res_verb["raw"] == "took off"
assert res_verb["separable"] is False
res_part = detect_phrasal_verb("The plane took off into the sky.", "off")
assert res_part is not None
assert res_part["term"] == "take off"
# Contiguous 3-word phrasal verb
res_triple = detect_phrasal_verb("She came up with a great solution.", "came")
assert res_triple is not None
assert res_triple["term"] == "come up with"
assert res_triple["raw"] == "came up with"
res_fwd = detect_phrasal_verb("I look forward to our trip.", "forward")
assert res_fwd is not None
assert res_fwd["term"] == "look forward to"
# Separable phrasal verb
res_sep_v = detect_phrasal_verb("Please turn it off before leaving.", "turn")
assert res_sep_v is not None
assert res_sep_v["term"] == "turn off"
assert res_sep_v["separable"] is True
res_sep_p = detect_phrasal_verb("Please turn it off before leaving.", "off")
assert res_sep_p is not None
assert res_sep_p["term"] == "turn off"
assert res_sep_p["separable"] is True
# Negative case (no phrasal verb)
res_none = detect_phrasal_verb("The red apple fell onto the soft grass.", "apple")
assert res_none is None
# 3. HTTP Endpoint Integration
client.cookies.clear()
unauth = client.get("/api/detect-phrasal-verb?sentence=The+plane+took+off&word=took")
assert unauth.status_code == 401
login_res = client.post("/api/login", json={"username": "testadmin", "password": "testpass12345"})
assert login_res.status_code == 200
detect_res = client.get("/api/detect-phrasal-verb?sentence=The+plane+took+off&word=took")
assert detect_res.status_code == 200
data = detect_res.json()
assert data["detected"] is True
assert data["phrasal_verb"]["term"] == "take off"
# 4. Contextual integration inside /api/lookup
lookup_res = client.get("/api/lookup?term=took&context=The+plane+took+off+into+the+sky")
assert lookup_res.status_code == 200
lookup_data = lookup_res.json()
assert lookup_data["query"] == "took"
assert lookup_data.get("phrasal_verb") is not None
assert lookup_data["phrasal_verb"]["term"] == "take off"
assert "взлетать" in lookup_data["phrasal_verb"]["translation"]
assert lookup_data["phrasal_verb"]["in_library"] is False
# 5. In-library status check for phrasal verb
add_card = client.post("/api/cards", json={
"term": "take off",
"translation": "взлетать, снимать",
"context_sentence": "The plane took off into the sky.",
"collection": "Фразовые глаголы & Идиомы",
"tags": "phrasal-verbs, reader"
})
assert add_card.status_code == 200
lookup_after = client.get("/api/lookup?term=took&context=The+plane+took+off+into+the+sky")
assert lookup_after.status_code == 200
after_data = lookup_after.json()
assert after_data["phrasal_verb"]["in_library"] is True
assert after_data["phrasal_verb"]["library_card"] is not None