mirror of
https://github.com/usestrix/strix.git
synced 2026-10-07 02:58:26 +00:00
test(i18n): add comprehensive i18n tests
- 34 tests covering all i18n functionality - Test set_language(), get_language(), _detect_language() - Test t() translation, fallback, interpolation - Test get_language_directive() for English and Spanish - Test locale key consistency between en.json and es.json - Test Settings.language field with env var
This commit is contained in:
parent
0fd997613b
commit
2ed1a69672
1 changed files with 208 additions and 0 deletions
208
tests/test_i18n.py
Normal file
208
tests/test_i18n.py
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
"""Tests for the i18n internationalization module."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
import strix.i18n as mod
|
||||
from strix.config.settings import Settings
|
||||
from strix.i18n import (
|
||||
SUPPORTED_LANGUAGES,
|
||||
_detect_language,
|
||||
_load_locale,
|
||||
_normalize_lang,
|
||||
get_language,
|
||||
get_language_directive,
|
||||
set_language,
|
||||
t,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_i18n_state():
|
||||
"""Reset module-level state between tests."""
|
||||
mod._language = None
|
||||
mod._locales.clear()
|
||||
yield
|
||||
mod._language = None
|
||||
mod._locales.clear()
|
||||
|
||||
|
||||
class TestSetLanguage:
|
||||
def test_set_language_spanish(self):
|
||||
set_language("es")
|
||||
assert get_language() == "es"
|
||||
|
||||
def test_set_language_english(self):
|
||||
set_language("en")
|
||||
assert get_language() == "en"
|
||||
|
||||
def test_set_language_normalizes(self):
|
||||
set_language("ES")
|
||||
assert get_language() == "es"
|
||||
|
||||
def test_set_language_none_resets(self):
|
||||
set_language("es")
|
||||
set_language(None)
|
||||
# After reset, should fall back to detection
|
||||
assert get_language() == "en" # default
|
||||
|
||||
def test_set_language_unsupported_falls_back(self):
|
||||
set_language("fr")
|
||||
assert get_language() == "en"
|
||||
|
||||
|
||||
class TestNormalizeLang:
|
||||
def test_normalize_lowercase(self):
|
||||
assert _normalize_lang("es") == "es"
|
||||
|
||||
def test_normalize_uppercase(self):
|
||||
assert _normalize_lang("ES") == "es"
|
||||
|
||||
def test_normalize_strips(self):
|
||||
assert _normalize_lang(" es ") == "es"
|
||||
|
||||
def test_normalize_unsupported(self):
|
||||
assert _normalize_lang("fr") == "en"
|
||||
|
||||
def test_normalize_empty(self):
|
||||
assert _normalize_lang("") == "en"
|
||||
|
||||
|
||||
class TestDetectLanguage:
|
||||
def test_default_is_english(self):
|
||||
assert _detect_language() == "en"
|
||||
|
||||
def test_explicit_set_takes_priority(self):
|
||||
mod._language = "es"
|
||||
assert _detect_language() == "es"
|
||||
|
||||
def test_env_var_detected(self):
|
||||
with patch.dict(os.environ, {"STRIX_LANGUAGE": "es"}):
|
||||
assert _detect_language() == "es"
|
||||
|
||||
def test_env_var_overrides_config(self):
|
||||
with patch.dict(os.environ, {"STRIX_LANGUAGE": "es"}):
|
||||
assert _detect_language() == "es"
|
||||
|
||||
def test_system_locale_detected(self):
|
||||
with patch.dict(os.environ, {"LANG": "es_ES.UTF-8"}, clear=False):
|
||||
mod._language = None
|
||||
assert _detect_language() == "es"
|
||||
|
||||
|
||||
class TestLoadLocale:
|
||||
def test_load_english(self):
|
||||
locale = _load_locale("en")
|
||||
assert "cli.description" in locale
|
||||
assert isinstance(locale, dict)
|
||||
|
||||
def test_load_spanish(self):
|
||||
locale = _load_locale("es")
|
||||
assert "cli.description" in locale
|
||||
|
||||
def test_load_missing_returns_empty(self):
|
||||
locale = _load_locale("nonexistent")
|
||||
assert locale == {}
|
||||
|
||||
def test_cached_after_first_load(self):
|
||||
_load_locale("en")
|
||||
assert "en" in mod._locales
|
||||
|
||||
|
||||
class TestTranslationFunction:
|
||||
def test_t_returns_english_by_default(self):
|
||||
result = t("cli.description")
|
||||
assert "Strix" in result
|
||||
assert "Penetration" in result
|
||||
|
||||
def test_t_returns_spanish_when_set(self):
|
||||
set_language("es")
|
||||
result = t("cli.description")
|
||||
assert "Penetración" in result
|
||||
|
||||
def test_t_falls_back_to_english_for_missing_key(self):
|
||||
set_language("es")
|
||||
# Use a key that exists in en but not es
|
||||
result = t("cli.description")
|
||||
# Should still return something (English fallback)
|
||||
assert result != "cli.description"
|
||||
|
||||
def test_t_returns_key_for_missing(self):
|
||||
result = t("nonexistent.key.xyz")
|
||||
assert result == "nonexistent.key.xyz"
|
||||
|
||||
def test_t_interpolates_placeholders(self):
|
||||
result = t("cli.scan_started", target="example.com")
|
||||
assert "example.com" in result
|
||||
|
||||
def test_t_interpolates_spanish(self):
|
||||
set_language("es")
|
||||
result = t("cli.scan_started", target="example.com")
|
||||
assert "example.com" in result
|
||||
assert "escaneo" in result.lower()
|
||||
|
||||
def test_t_handles_missing_placeholder_gracefully(self):
|
||||
# Should not raise, just return with missing placeholder
|
||||
result = t("cli.scan_started") # target not provided
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
class TestLanguageDirective:
|
||||
def test_english_returns_empty(self):
|
||||
set_language("en")
|
||||
directive = get_language_directive()
|
||||
assert directive == ""
|
||||
|
||||
def test_spanish_returns_directive(self):
|
||||
set_language("es")
|
||||
directive = get_language_directive()
|
||||
assert "Spanish" in directive
|
||||
assert "CVE" in directive # preservation rule
|
||||
assert "CWE" in directive
|
||||
|
||||
def test_directive_preserves_identifiers(self):
|
||||
set_language("es")
|
||||
directive = get_language_directive()
|
||||
assert "CVE identifiers" in directive
|
||||
assert "CWE identifiers" in directive
|
||||
assert "CVSS scores" in directive
|
||||
assert "Source code" in directive
|
||||
assert "Shell commands" in directive
|
||||
|
||||
|
||||
class TestLocaleKeyConsistency:
|
||||
def test_all_en_keys_exist_in_es(self):
|
||||
en = _load_locale("en")
|
||||
es = _load_locale("es")
|
||||
missing = set(en.keys()) - set(es.keys())
|
||||
assert missing == set(), f"Missing Spanish keys: {missing}"
|
||||
|
||||
def test_locale_files_are_valid_json(self):
|
||||
locales_dir = Path(__file__).parent.parent / "strix" / "locales"
|
||||
for json_file in locales_dir.glob("*.json"):
|
||||
data = json.loads(json_file.read_text(encoding="utf-8"))
|
||||
assert isinstance(data, dict), f"{json_file.name} is not a dict"
|
||||
|
||||
def test_supported_languages_match_files(self):
|
||||
locales_dir = Path(__file__).parent.parent / "strix" / "locales"
|
||||
for lang in SUPPORTED_LANGUAGES:
|
||||
locale_file = locales_dir / f"{lang}.json"
|
||||
assert locale_file.exists(), f"Missing locale file: {locale_file}"
|
||||
|
||||
|
||||
class TestSettingsLanguageField:
|
||||
def test_settings_has_language_field(self):
|
||||
s = Settings()
|
||||
assert hasattr(s, "language")
|
||||
assert s.language == "en"
|
||||
|
||||
def test_settings_language_from_env(self):
|
||||
with patch.dict(os.environ, {"STRIX_LANGUAGE": "es"}):
|
||||
s = Settings()
|
||||
assert s.language == "es"
|
||||
Loading…
Add table
Reference in a new issue