"""Tests for modules/vocab.py — vocabulary search and flashcard logic.""" import json import sys from pathlib import Path from unittest.mock import patch import pytest sys.path.insert(0, str(Path(__file__).parent.parent)) SAMPLE_VOCAB = [ { "id": "verb_go", "section": "High-frequency language", "category": "Common verbs", "english": "to go", "persian": "رفتن", "finglish": "raftan", }, { "id": "verb_eat", "section": "High-frequency language", "category": "Common verbs", "english": "to eat", "persian": "خوردن", "finglish": "khordan", }, { "id": "adj_big", "section": "High-frequency language", "category": "Common adjectives", "english": "big", "persian": "بزرگ", "finglish": "bozorg", }, { "id": "colour_red", "section": "High-frequency language", "category": "Colours", "english": "red", "persian": "قرمز", "finglish": "ghermez", }, ] @pytest.fixture(autouse=True) def mock_vocab_and_db(tmp_path): """Mock vocabulary loading and use temp DB.""" import db as db_mod import modules.vocab as vocab_mod # Temp DB db_mod._conn = None db_mod.DB_PATH = tmp_path / "test.db" db_mod.init_db() # Mock vocab vocab_mod._vocab_data = SAMPLE_VOCAB yield vocab_mod db_mod.close() vocab_mod._vocab_data = None def test_load_vocab(mock_vocab_and_db): """load_vocab should return the vocabulary data.""" data = mock_vocab_and_db.load_vocab() assert len(data) == 4 def test_get_categories(mock_vocab_and_db): """get_categories should return unique sorted categories.""" cats = mock_vocab_and_db.get_categories() assert "Colours" in cats assert "Common verbs" in cats assert "Common adjectives" in cats def test_search_english(mock_vocab_and_db): """Search should find entries by English text.""" results = mock_vocab_and_db.search("go") assert len(results) == 1 assert results[0]["id"] == "verb_go" def test_search_persian(mock_vocab_and_db): """Search should find entries by Persian text.""" results = mock_vocab_and_db.search("رفتن") assert len(results) == 1 assert results[0]["id"] == "verb_go" def test_search_finglish(mock_vocab_and_db): """Search should find entries by Finglish text.""" results = mock_vocab_and_db.search("raftan") assert len(results) == 1 assert results[0]["id"] == "verb_go" def test_search_empty(mock_vocab_and_db): """Empty search should return empty list.""" assert mock_vocab_and_db.search("") == [] assert mock_vocab_and_db.search(None) == [] def test_search_no_match(mock_vocab_and_db): """Search with no match should return empty list.""" assert mock_vocab_and_db.search("zzzzz") == [] def test_get_random_word(mock_vocab_and_db): """get_random_word should return a valid entry.""" word = mock_vocab_and_db.get_random_word() assert word is not None assert "id" in word assert "english" in word assert "persian" in word def test_get_random_word_with_category(mock_vocab_and_db): """get_random_word with category filter should only return matching entries.""" word = mock_vocab_and_db.get_random_word(category="Colours") assert word is not None assert word["category"] == "Colours" def test_get_random_word_nonexistent_category(mock_vocab_and_db): """get_random_word with bad category should return None.""" word = mock_vocab_and_db.get_random_word(category="Nonexistent") assert word is None def test_check_answer_correct_en_to_fa(mock_vocab_and_db): """Correct Persian answer should be marked correct.""" correct, answer, entry = mock_vocab_and_db.check_answer( "verb_go", "رفتن", direction="en_to_fa" ) assert correct is True def test_check_answer_incorrect_en_to_fa(mock_vocab_and_db): """Incorrect Persian answer should be marked incorrect with correct answer.""" correct, answer, entry = mock_vocab_and_db.check_answer( "verb_go", "خوردن", direction="en_to_fa" ) assert correct is False assert answer == "رفتن" def test_check_answer_fa_to_en(mock_vocab_and_db): """Correct English answer (case-insensitive) should be marked correct.""" correct, answer, entry = mock_vocab_and_db.check_answer( "verb_go", "To Go", direction="fa_to_en" ) assert correct is True def test_check_answer_nonexistent_word(mock_vocab_and_db): """Checking answer for nonexistent word should return False.""" correct, answer, entry = mock_vocab_and_db.check_answer( "nonexistent", "test", direction="en_to_fa" ) assert correct is False assert entry is None def test_format_word_card(mock_vocab_and_db): """format_word_card should produce RTL HTML with correct content.""" entry = SAMPLE_VOCAB[0] html = mock_vocab_and_db.format_word_card(entry, show_transliteration="Finglish") assert "رفتن" in html assert "to go" in html assert "raftan" in html def test_format_word_card_no_transliteration(mock_vocab_and_db): """format_word_card with transliteration off should not show finglish.""" entry = SAMPLE_VOCAB[0] html = mock_vocab_and_db.format_word_card(entry, show_transliteration="off") assert "raftan" not in html def test_get_flashcard_batch(mock_vocab_and_db): """get_flashcard_batch should return a batch of entries.""" batch = mock_vocab_and_db.get_flashcard_batch(count=2) assert len(batch) == 2 assert all("id" in e for e in batch) def test_get_word_status_new(mock_vocab_and_db): """Unreviewed word should have status 'new'.""" assert mock_vocab_and_db.get_word_status("verb_go") == "new" def test_get_word_status_learning(mock_vocab_and_db): """Recently reviewed word should have status 'learning'.""" import db import fsrs db.update_word_progress("verb_go", fsrs.Rating.Good) assert mock_vocab_and_db.get_word_status("verb_go") == "learning"