"""Tests for net.i2p.util Tier 2 — i18n & enhanced crypto. TDD — tests for Translate/TranslateReader and FortunaRandomSource. """ from __future__ import annotations import os import tempfile import pytest from i2p_util.translate import Translate, TranslateReader from i2p_util.fortuna import FortunaRandomSource # --------------------------------------------------------------------------- # TranslateReader # --------------------------------------------------------------------------- class TestTranslateReader: """Load .properties translation files.""" def test_load_properties(self): with tempfile.TemporaryDirectory() as td: path = os.path.join(td, "messages_en.properties") with open(path, "w") as f: f.write("greeting=Hello\n") f.write("farewell=Goodbye\n") reader = TranslateReader() props = reader.load(path) assert props["greeting"] == "Hello" assert props["farewell"] == "Goodbye" def test_skip_comments(self): with tempfile.TemporaryDirectory() as td: path = os.path.join(td, "test.properties") with open(path, "w") as f: f.write("# comment\n") f.write("! another comment\n") f.write("key=value\n") reader = TranslateReader() props = reader.load(path) assert len(props) == 1 assert props["key"] == "value" def test_skip_blank_lines(self): with tempfile.TemporaryDirectory() as td: path = os.path.join(td, "test.properties") with open(path, "w") as f: f.write("a=1\n\n\nb=2\n") reader = TranslateReader() props = reader.load(path) assert len(props) == 2 def test_value_with_equals(self): with tempfile.TemporaryDirectory() as td: path = os.path.join(td, "test.properties") with open(path, "w") as f: f.write("url=http://example.com?a=1&b=2\n") reader = TranslateReader() props = reader.load(path) assert props["url"] == "http://example.com?a=1&b=2" def test_missing_file_returns_empty(self): reader = TranslateReader() props = reader.load("/nonexistent/file.properties") assert props == {} def test_colon_separator(self): """Java .properties also supports : as separator.""" with tempfile.TemporaryDirectory() as td: path = os.path.join(td, "test.properties") with open(path, "w") as f: f.write("key:value\n") reader = TranslateReader() props = reader.load(path) assert props["key"] == "value" # --------------------------------------------------------------------------- # Translate # --------------------------------------------------------------------------- class TestTranslate: """Locale-specific message lookup.""" def test_get_string(self): t = Translate() t.add_strings({"hello": "Hello, World!"}) assert t.get_string("hello") == "Hello, World!" def test_get_string_missing_returns_key(self): t = Translate() assert t.get_string("missing_key") == "missing_key" def test_get_string_with_params(self): t = Translate() t.add_strings({"greet": "Hello, {0}!"}) assert t.get_string("greet", "Alice") == "Hello, Alice!" def test_set_locale(self): t = Translate() t.set_locale("fr") assert t.get_locale() == "fr" def test_default_locale_en(self): t = Translate() assert t.get_locale() == "en" def test_load_from_directory(self): with tempfile.TemporaryDirectory() as td: path = os.path.join(td, "messages_en.properties") with open(path, "w") as f: f.write("test_key=test_value\n") t = Translate() t.load_bundle(td, "messages") assert t.get_string("test_key") == "test_value" # --------------------------------------------------------------------------- # FortunaRandomSource # --------------------------------------------------------------------------- class TestFortunaRandomSource: """Fortuna-based CSPRNG.""" def test_next_bytes(self): rng = FortunaRandomSource() data = rng.next_bytes(32) assert len(data) == 32 def test_next_bytes_different(self): rng = FortunaRandomSource() a = rng.next_bytes(16) b = rng.next_bytes(16) assert a != b def test_next_int_in_range(self): rng = FortunaRandomSource() for _ in range(100): val = rng.next_int(10) assert 0 <= val < 10 def test_seed_affects_output(self): """Seeding with different values should produce different sequences.""" rng1 = FortunaRandomSource(seed=b"seed_a") rng2 = FortunaRandomSource(seed=b"seed_b") assert rng1.next_bytes(16) != rng2.next_bytes(16) def test_next_boolean(self): rng = FortunaRandomSource() results = {rng.next_boolean() for _ in range(100)} assert True in results assert False in results