88
99import json
1010from io import StringIO
11- from pathlib import Path
12- from unittest .mock import MagicMock , call , patch
11+ from typing import Any
12+ from unittest .mock import MagicMock , patch
1313
1414import pytest
1515
2121# Helpers
2222# ---------------------------------------------------------------------------
2323
24- def _bare (with_ai : bool = False , with_eng_fallback : bool = False ) -> SuttaCentralExporterRu :
24+
25+ def _bare (with_ai : bool = False , with_eng_fallback : bool = False ) -> Any :
2526 """Return a SuttaCentralExporterRu instance without running __init__."""
2627 inst = object .__new__ (SuttaCentralExporterRu )
2728 inst .with_ai = with_ai
2829 inst .with_eng_fallback = with_eng_fallback
2930 inst .pth = MagicMock ()
31+ inst .ru_pth = MagicMock ()
3032 inst .db_session = MagicMock ()
3133 inst .sc_word_set = set ()
3234 inst .lookup_dict = {}
@@ -70,6 +72,7 @@ def _lookup(headword_ids=None, deconstructions=None):
7072# flip()
7173# ---------------------------------------------------------------------------
7274
75+
7376class TestFlip :
7477 def setup_method (self ):
7578 self .inst = _bare ()
@@ -146,28 +149,39 @@ def test_ru_meaning_with_literal_and_construction(self, _abbr):
146149 def test_empty_ru_meaning_lit_excluded (self , _abbr ):
147150 """Empty ru_meaning_lit (falsy) must not add '; досл.' clause."""
148151 inst = _bare ()
149- result = inst .make_sc_headword_entry (_hw (ru = _ru (ru_meaning = "учение" , ru_meaning_lit = "" )))
152+ result = inst .make_sc_headword_entry (
153+ _hw (ru = _ru (ru_meaning = "учение" , ru_meaning_lit = "" ))
154+ )
150155 assert "досл." not in result
151156 assert result == "сущ. <b>учение</b>"
152157
153158 @patch (f"{ MODULE } .ru_replace_abbreviations" , return_value = "гл" )
154159 def test_ai_entry_when_ai_enabled (self , _abbr ):
155160 inst = _bare (with_ai = True )
156- result = inst .make_sc_headword_entry (_hw (pos = "verb" , ru = _ru (ru_meaning_raw = "понимать" )))
161+ result = inst .make_sc_headword_entry (
162+ _hw (pos = "verb" , ru = _ru (ru_meaning_raw = "понимать" ))
163+ )
157164 assert result == "гл. <i>[пер. ИИ]</i> понимать"
158165
159166 @patch (f"{ MODULE } .ru_replace_abbreviations" , return_value = "гл" )
160167 def test_ai_entry_returns_none_when_ai_disabled (self , _abbr ):
161168 inst = _bare (with_ai = False )
162- result = inst .make_sc_headword_entry (_hw (pos = "verb" , ru = _ru (ru_meaning_raw = "понимать" )))
169+ result = inst .make_sc_headword_entry (
170+ _hw (pos = "verb" , ru = _ru (ru_meaning_raw = "понимать" ))
171+ )
163172 assert result is None
164173
165174 @patch (f"{ MODULE } .ru_replace_abbreviations" , return_value = "гл" )
166175 def test_ai_entry_includes_construction (self , _abbr ):
167176 inst = _bare (with_ai = True )
168177 result = inst .make_sc_headword_entry (
169- _hw (pos = "verb" , ru = _ru (ru_meaning_raw = "понимать" ), construction_summary = "pa + jānā" )
178+ _hw (
179+ pos = "verb" ,
180+ ru = _ru (ru_meaning_raw = "понимать" ),
181+ construction_summary = "pa + jānā" ,
182+ )
170183 )
184+ assert result is not None
171185 assert "[pa + jānā]" in result
172186
173187 @patch (f"{ MODULE } .make_meaning_combo" , return_value = "teaching; law" )
@@ -189,14 +203,19 @@ def test_eng_fallback_returns_none_when_disabled(self, _abbr):
189203 @patch (f"{ MODULE } .ru_replace_abbreviations" , return_value = "сущ" )
190204 def test_eng_fallback_includes_construction (self , _abbr , _combo ):
191205 inst = _bare (with_eng_fallback = True )
192- result = inst .make_sc_headword_entry (_hw (ru = None , construction_summary = "dhā + ma" ))
206+ result = inst .make_sc_headword_entry (
207+ _hw (ru = None , construction_summary = "dhā + ma" )
208+ )
209+ assert result is not None
193210 assert "[dhā + ma]" in result
194211
195212 @patch (f"{ MODULE } .ru_replace_abbreviations" , return_value = "сущ" )
196213 def test_all_empty_ru_fields_returns_none (self , _abbr ):
197214 """ru object with all empty meaning fields → None."""
198215 inst = _bare ()
199- result = inst .make_sc_headword_entry (_hw (ru = _ru (ru_meaning = "" , ru_meaning_raw = "" )))
216+ result = inst .make_sc_headword_entry (
217+ _hw (ru = _ru (ru_meaning = "" , ru_meaning_raw = "" ))
218+ )
200219 assert result is None
201220
202221 @patch (f"{ MODULE } .ru_replace_abbreviations" , return_value = "сущ" )
@@ -206,6 +225,7 @@ def test_ru_meaning_preferred_over_raw(self, _abbr):
206225 result = inst .make_sc_headword_entry (
207226 _hw (ru = _ru (ru_meaning = "учение" , ru_meaning_raw = "ИИ перевод" ))
208227 )
228+ assert result is not None
209229 assert "<b>учение</b>" in result
210230 assert "[пер. ИИ]" not in result
211231
@@ -222,6 +242,7 @@ def test_pos_abbreviation_always_called(self, mock_abbr):
222242# make_sc_dict()
223243# ---------------------------------------------------------------------------
224244
245+
225246class TestMakeScDict :
226247 def test_word_not_in_lookup_dict_skipped (self ):
227248 inst = _bare ()
@@ -290,7 +311,9 @@ def test_headwords_and_deconstructor_both_added(self, _abbr):
290311 hw = _hw (lemma_1 = "dhamma" , pos = "masc" , ru = _ru (ru_meaning = "учение" ))
291312 inst = _bare ()
292313 inst .sc_word_set = {"dhamma" }
293- inst .lookup_dict = {"dhamma" : _lookup (headword_ids = [1 ], deconstructions = ["x + y" ])}
314+ inst .lookup_dict = {
315+ "dhamma" : _lookup (headword_ids = [1 ], deconstructions = ["x + y" ])
316+ }
294317 inst .headword_dict = {1 : hw }
295318 inst .make_sc_dict ()
296319 assert len (inst .sc_dict ["dhamma" ]) == 2
@@ -322,6 +345,7 @@ def test_multiple_words_each_processed_independently(self):
322345# compile_sc_dict()
323346# ---------------------------------------------------------------------------
324347
348+
325349class TestCompileScDict :
326350 def test_empty_word_set (self ):
327351 inst = _bare ()
@@ -426,6 +450,7 @@ def test_compiled_entry_has_definition_key(self):
426450# make_lookup_dict()
427451# ---------------------------------------------------------------------------
428452
453+
429454class TestMakeLookupDict :
430455 def test_populates_lookup_dict_from_db (self ):
431456 inst = _bare ()
@@ -465,6 +490,7 @@ def test_duplicate_keys_last_one_wins(self):
465490# make_headwords_dict()
466491# ---------------------------------------------------------------------------
467492
493+
468494class TestMakeHeadwordsDict :
469495 def test_populates_headword_dict_by_id (self ):
470496 inst = _bare ()
@@ -512,6 +538,7 @@ def test_queries_dpdheadword_model(self):
512538# save_sc_dict()
513539# ---------------------------------------------------------------------------
514540
541+
515542class TestSaveScDict :
516543 def _run_save (self , inst ):
517544 """Run save_sc_dict, capture written bytes, return parsed JSON."""
@@ -532,9 +559,14 @@ def __exit__(self_, *a):
532559 def test_creates_parent_directory (self ):
533560 inst = _bare ()
534561 inst .sc_dict_compiled = []
535- with patch ("builtins.open" , return_value = MagicMock (__enter__ = lambda s : StringIO (), __exit__ = lambda s , * a : None )):
562+ with patch (
563+ "builtins.open" ,
564+ return_value = MagicMock (
565+ __enter__ = lambda s : StringIO (), __exit__ = lambda s , * a : None
566+ ),
567+ ):
536568 inst .save_sc_dict ()
537- inst .pth .sc_pli2ru_dpd_json .parent .mkdir .assert_called_once_with (
569+ inst .ru_pth .sc_pli2ru_dpd_json .parent .mkdir .assert_called_once_with (
538570 parents = True , exist_ok = True
539571 )
540572
@@ -575,12 +607,17 @@ def test_opens_file_at_correct_path(self):
575607 inst = _bare ()
576608 inst .sc_dict_compiled = []
577609 mock_path = MagicMock ()
578- inst .pth .sc_pli2ru_dpd_json = mock_path
579-
580- with patch ("builtins.open" , return_value = MagicMock (__enter__ = lambda s : StringIO (), __exit__ = lambda s , * a : None )) as m_open :
610+ inst .ru_pth .sc_pli2ru_dpd_json = mock_path
611+
612+ with patch (
613+ "builtins.open" ,
614+ return_value = MagicMock (
615+ __enter__ = lambda s : StringIO (), __exit__ = lambda s , * a : None
616+ ),
617+ ) as m_open :
581618 inst .save_sc_dict ()
582619
583- m_open .assert_called_once_with (mock_path , "w" )
620+ m_open .assert_called_once_with (mock_path , "w" , encoding = "utf-8" )
584621
585622
586623# ---------------------------------------------------------------------------
0 commit comments