Add persian-tutor: Gradio-based GCSE Persian language learning app
Vocabulary study with FSRS spaced repetition, AI tutoring (Ollama/Claude), essay marking, idioms browser, Anki export, and dashboard. 918 vocabulary entries across 39 categories. 41 tests passing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
86
python/persian-tutor/tests/test_anki_export.py
Normal file
86
python/persian-tutor/tests/test_anki_export.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""Tests for anki_export.py — Anki .apkg generation."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from anki_export import export_deck
|
||||
|
||||
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": "colour_red",
|
||||
"section": "High-frequency language",
|
||||
"category": "Colours",
|
||||
"english": "red",
|
||||
"persian": "قرمز",
|
||||
"finglish": "ghermez",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def test_export_deck_creates_file(tmp_path):
|
||||
"""export_deck should create a valid .apkg file."""
|
||||
output = str(tmp_path / "test.apkg")
|
||||
result = export_deck(SAMPLE_VOCAB, output_path=output)
|
||||
assert result == output
|
||||
assert os.path.exists(output)
|
||||
assert os.path.getsize(output) > 0
|
||||
|
||||
|
||||
def test_export_deck_is_valid_zip(tmp_path):
|
||||
"""An .apkg file is a zip archive containing an Anki SQLite database."""
|
||||
output = str(tmp_path / "test.apkg")
|
||||
export_deck(SAMPLE_VOCAB, output_path=output)
|
||||
assert zipfile.is_zipfile(output)
|
||||
|
||||
|
||||
def test_export_deck_with_category_filter(tmp_path):
|
||||
"""export_deck with category filter should only include matching entries."""
|
||||
output = str(tmp_path / "test.apkg")
|
||||
export_deck(SAMPLE_VOCAB, categories=["Colours"], output_path=output)
|
||||
# File should exist and be smaller than unfiltered
|
||||
assert os.path.exists(output)
|
||||
size_filtered = os.path.getsize(output)
|
||||
|
||||
output2 = str(tmp_path / "test_all.apkg")
|
||||
export_deck(SAMPLE_VOCAB, output_path=output2)
|
||||
size_all = os.path.getsize(output2)
|
||||
|
||||
# Filtered deck should be smaller (fewer cards)
|
||||
assert size_filtered <= size_all
|
||||
|
||||
|
||||
def test_export_deck_empty_vocab(tmp_path):
|
||||
"""export_deck with empty vocabulary should still create a valid file."""
|
||||
output = str(tmp_path / "test.apkg")
|
||||
export_deck([], output_path=output)
|
||||
assert os.path.exists(output)
|
||||
|
||||
|
||||
def test_export_deck_no_category_match(tmp_path):
|
||||
"""export_deck with non-matching category filter should create empty deck."""
|
||||
output = str(tmp_path / "test.apkg")
|
||||
export_deck(SAMPLE_VOCAB, categories=["Nonexistent"], output_path=output)
|
||||
assert os.path.exists(output)
|
||||
Reference in New Issue
Block a user