|
| 1 | +"""Regression tests for issue #927 — language code lookup must be case-insensitive. |
| 2 | +
|
| 3 | +The locale files use mixed case for the region subtag (``pt-br.json`` vs |
| 4 | +``zh-CN.json``). BCP 47 tags are case-insensitive (RFC 5646 §2.1.1), so |
| 5 | +``--lang PT-BR``, ``--lang zh-cn``, and ``--lang ZH-TW`` must all resolve |
| 6 | +to the canonical file rather than silently falling back to English. |
| 7 | +""" |
| 8 | + |
| 9 | +import pytest |
| 10 | + |
| 11 | +from mempalace import i18n |
| 12 | +from mempalace.i18n import ( |
| 13 | + _canonical_lang, |
| 14 | + _load_entity_section, |
| 15 | + available_languages, |
| 16 | + get_entity_patterns, |
| 17 | + load_lang, |
| 18 | +) |
| 19 | + |
| 20 | + |
| 21 | +@pytest.fixture(autouse=True) |
| 22 | +def _reset_state(): |
| 23 | + """Reset the module-level entity cache between tests.""" |
| 24 | + i18n._entity_cache.clear() |
| 25 | + yield |
| 26 | + i18n._entity_cache.clear() |
| 27 | + |
| 28 | + |
| 29 | +def test_canonical_lang_lowercase_passthrough(): |
| 30 | + assert _canonical_lang("en") == "en" |
| 31 | + assert _canonical_lang("pt-br") == "pt-br" |
| 32 | + |
| 33 | + |
| 34 | +def test_canonical_lang_uppercase_resolves(): |
| 35 | + assert _canonical_lang("PT-BR") == "pt-br" |
| 36 | + assert _canonical_lang("ZH-CN") == "zh-CN" |
| 37 | + assert _canonical_lang("zh-cn") == "zh-CN" |
| 38 | + assert _canonical_lang("Pt-Br") == "pt-br" |
| 39 | + |
| 40 | + |
| 41 | +def test_canonical_lang_unknown_returns_none(): |
| 42 | + assert _canonical_lang("xx") is None |
| 43 | + assert _canonical_lang("") is None |
| 44 | + |
| 45 | + |
| 46 | +def test_load_lang_case_insensitive(): |
| 47 | + """`load_lang('PT-BR')` must load the pt-br dictionary, not English.""" |
| 48 | + en = load_lang("en") |
| 49 | + pt_lower = load_lang("pt-br") |
| 50 | + pt_upper = load_lang("PT-BR") |
| 51 | + assert pt_lower == pt_upper, "case should not change the loaded dict" |
| 52 | + # If load_lang silently fell back to English, both would equal `en`. |
| 53 | + if "pt-br" in available_languages() and pt_lower != en: |
| 54 | + assert i18n.current_lang() == "pt-br" |
| 55 | + |
| 56 | + |
| 57 | +def test_entity_section_loads_for_uppercase_input(): |
| 58 | + """`_load_entity_section('PT-BR')` must read pt-br.json, not return {}.""" |
| 59 | + pt_lower = _load_entity_section("pt-br") |
| 60 | + pt_upper = _load_entity_section("PT-BR") |
| 61 | + assert pt_lower == pt_upper |
| 62 | + |
| 63 | + |
| 64 | +def test_get_entity_patterns_case_insensitive(): |
| 65 | + """Entity patterns must be identical regardless of input case.""" |
| 66 | + lower = get_entity_patterns(("pt-br",)) |
| 67 | + upper = get_entity_patterns(("PT-BR",)) |
| 68 | + assert lower == upper |
| 69 | + |
| 70 | + |
| 71 | +def test_get_entity_patterns_shares_cache_across_cases(): |
| 72 | + """Different casing must hit the same cache entry — not duplicate work.""" |
| 73 | + get_entity_patterns(("zh-CN",)) |
| 74 | + cache_keys = list(i18n._entity_cache.keys()) |
| 75 | + get_entity_patterns(("ZH-CN",)) |
| 76 | + get_entity_patterns(("zh-cn",)) |
| 77 | + assert len(i18n._entity_cache) == len( |
| 78 | + cache_keys |
| 79 | + ), "different casings of the same language must not create new cache entries" |
| 80 | + |
| 81 | + |
| 82 | +def test_unknown_language_still_falls_back_to_english(): |
| 83 | + """A code with no matching file must fall through to English (existing contract).""" |
| 84 | + patterns = get_entity_patterns(("xx-yy",)) |
| 85 | + en = get_entity_patterns(("en",)) |
| 86 | + assert patterns["candidate_patterns"] == en["candidate_patterns"] |
0 commit comments