A Python port of the Invisible Internet Project (I2P)
at main 158 lines 5.3 kB view raw
1"""Tests for net.i2p.util Tier 2 — i18n & enhanced crypto. 2 3TDD — tests for Translate/TranslateReader and FortunaRandomSource. 4""" 5 6from __future__ import annotations 7 8import os 9import tempfile 10 11import pytest 12 13from i2p_util.translate import Translate, TranslateReader 14from i2p_util.fortuna import FortunaRandomSource 15 16 17# --------------------------------------------------------------------------- 18# TranslateReader 19# --------------------------------------------------------------------------- 20 21 22class TestTranslateReader: 23 """Load .properties translation files.""" 24 25 def test_load_properties(self): 26 with tempfile.TemporaryDirectory() as td: 27 path = os.path.join(td, "messages_en.properties") 28 with open(path, "w") as f: 29 f.write("greeting=Hello\n") 30 f.write("farewell=Goodbye\n") 31 reader = TranslateReader() 32 props = reader.load(path) 33 assert props["greeting"] == "Hello" 34 assert props["farewell"] == "Goodbye" 35 36 def test_skip_comments(self): 37 with tempfile.TemporaryDirectory() as td: 38 path = os.path.join(td, "test.properties") 39 with open(path, "w") as f: 40 f.write("# comment\n") 41 f.write("! another comment\n") 42 f.write("key=value\n") 43 reader = TranslateReader() 44 props = reader.load(path) 45 assert len(props) == 1 46 assert props["key"] == "value" 47 48 def test_skip_blank_lines(self): 49 with tempfile.TemporaryDirectory() as td: 50 path = os.path.join(td, "test.properties") 51 with open(path, "w") as f: 52 f.write("a=1\n\n\nb=2\n") 53 reader = TranslateReader() 54 props = reader.load(path) 55 assert len(props) == 2 56 57 def test_value_with_equals(self): 58 with tempfile.TemporaryDirectory() as td: 59 path = os.path.join(td, "test.properties") 60 with open(path, "w") as f: 61 f.write("url=http://example.com?a=1&b=2\n") 62 reader = TranslateReader() 63 props = reader.load(path) 64 assert props["url"] == "http://example.com?a=1&b=2" 65 66 def test_missing_file_returns_empty(self): 67 reader = TranslateReader() 68 props = reader.load("/nonexistent/file.properties") 69 assert props == {} 70 71 def test_colon_separator(self): 72 """Java .properties also supports : as separator.""" 73 with tempfile.TemporaryDirectory() as td: 74 path = os.path.join(td, "test.properties") 75 with open(path, "w") as f: 76 f.write("key:value\n") 77 reader = TranslateReader() 78 props = reader.load(path) 79 assert props["key"] == "value" 80 81 82# --------------------------------------------------------------------------- 83# Translate 84# --------------------------------------------------------------------------- 85 86 87class TestTranslate: 88 """Locale-specific message lookup.""" 89 90 def test_get_string(self): 91 t = Translate() 92 t.add_strings({"hello": "Hello, World!"}) 93 assert t.get_string("hello") == "Hello, World!" 94 95 def test_get_string_missing_returns_key(self): 96 t = Translate() 97 assert t.get_string("missing_key") == "missing_key" 98 99 def test_get_string_with_params(self): 100 t = Translate() 101 t.add_strings({"greet": "Hello, {0}!"}) 102 assert t.get_string("greet", "Alice") == "Hello, Alice!" 103 104 def test_set_locale(self): 105 t = Translate() 106 t.set_locale("fr") 107 assert t.get_locale() == "fr" 108 109 def test_default_locale_en(self): 110 t = Translate() 111 assert t.get_locale() == "en" 112 113 def test_load_from_directory(self): 114 with tempfile.TemporaryDirectory() as td: 115 path = os.path.join(td, "messages_en.properties") 116 with open(path, "w") as f: 117 f.write("test_key=test_value\n") 118 t = Translate() 119 t.load_bundle(td, "messages") 120 assert t.get_string("test_key") == "test_value" 121 122 123# --------------------------------------------------------------------------- 124# FortunaRandomSource 125# --------------------------------------------------------------------------- 126 127 128class TestFortunaRandomSource: 129 """Fortuna-based CSPRNG.""" 130 131 def test_next_bytes(self): 132 rng = FortunaRandomSource() 133 data = rng.next_bytes(32) 134 assert len(data) == 32 135 136 def test_next_bytes_different(self): 137 rng = FortunaRandomSource() 138 a = rng.next_bytes(16) 139 b = rng.next_bytes(16) 140 assert a != b 141 142 def test_next_int_in_range(self): 143 rng = FortunaRandomSource() 144 for _ in range(100): 145 val = rng.next_int(10) 146 assert 0 <= val < 10 147 148 def test_seed_affects_output(self): 149 """Seeding with different values should produce different sequences.""" 150 rng1 = FortunaRandomSource(seed=b"seed_a") 151 rng2 = FortunaRandomSource(seed=b"seed_b") 152 assert rng1.next_bytes(16) != rng2.next_bytes(16) 153 154 def test_next_boolean(self): 155 rng = FortunaRandomSource() 156 results = {rng.next_boolean() for _ in range(100)} 157 assert True in results 158 assert False in results