""" Тесты builder.py — создание ZIP и mapping.csv. Запуск: python3 tests/test_builder.py """ import io import zipfile import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from drhider.builder import build_zip, build_mapping_csv passed = 0 failed = 0 def check(name, condition): global passed, failed if condition: passed += 1 print(f" ✅ {name}") else: failed += 1 print(f" ❌ {name}") # ═══════════════════════════════════════════════════════════════ # Test build_zip # ═══════════════════════════════════════════════════════════════ print("\n=== build_zip ===") # 1. Базовый ZIP с одним файлом zip_data = build_zip([("test.md", b"content")]) with zipfile.ZipFile(io.BytesIO(zip_data)) as zf: names = zf.namelist() check("один файл в ZIP", len(names) == 1) check("имя test.md", "test.md" in names) check("контент верный", zf.read("test.md") == b"content") # 2. ZIP с несколькими файлами zip_data = build_zip([ ("договор_obfuscated.md", "# Договор\nТекст".encode("utf-8")), ("спецификация_obfuscated.md", "| Услуга | Цена |".encode("utf-8")), ]) with zipfile.ZipFile(io.BytesIO(zip_data)) as zf: names = zf.namelist() check("два файла", len(names) == 2) check("mapping.csv нет", "mapping.csv" not in names) check("договор читается", "Договор" in zf.read("договор_obfuscated.md").decode("utf-8")) # 3. ZIP с mapping.csv zip_data = build_zip( [("file.md", b"data")], mapping_csv="тип,оригинал,замена\nperson,Иванов,Иванов_0001\n" ) with zipfile.ZipFile(io.BytesIO(zip_data)) as zf: names = zf.namelist() check("mapping.csv есть", "mapping.csv" in names) csv_content = zf.read("mapping.csv").decode("utf-8") check("mapping с BOM", csv_content.startswith('\ufeff')) check("данные в mapping", "Иванов_0001" in csv_content) # 4. ZIP с кириллицей в именах zip_data = build_zip([("договор_№123.md", "текст".encode("utf-8"))]) with zipfile.ZipFile(io.BytesIO(zip_data)) as zf: check("кириллическое имя читается", "договор_№123.md" in zf.namelist()) # 5. ZIP с бинарными данными binary = bytes(range(256)) zip_data = build_zip([("binary.bin", binary)]) with zipfile.ZipFile(io.BytesIO(zip_data)) as zf: check("бинарный файл сохранён", zf.read("binary.bin") == binary) # 6. Пустой mapping zip_data = build_zip([("f.md", b"x")], mapping_csv="") with zipfile.ZipFile(io.BytesIO(zip_data)) as zf: check("пустой mapping не добавляется", "mapping.csv" not in zf.namelist()) # ═══════════════════════════════════════════════════════════════ # Test build_mapping_csv # ═══════════════════════════════════════════════════════════════ print("\n=== build_mapping_csv ===") # 1. Простой mapping mapping = {"Иванов И.И.": "Иванов_0001"} csv_str = build_mapping_csv(mapping) check("заголовок есть", csv_str.startswith("тип_данных,оригинал,замена")) check("данные есть", "Иванов_0001" in csv_str) # 2. Пустой mapping csv_str = build_mapping_csv({}) lines = csv_str.strip().split("\n") check("пустой — только заголовок", len(lines) == 1) # 3. Сортировка mapping = {"Б": "Б_0001", "А": "А_0001"} csv_str = build_mapping_csv(mapping) check("сортировка А перед Б", csv_str.index("А_0001") < csv_str.index("Б_0001")) # 4. Кавычки в значениях mapping = {'ООО "Ромашка"': "ООО_Технология_0001"} csv_str = build_mapping_csv(mapping) check("кавычки экранированы", 'ООО "Ромашка"' in csv_str or 'ООО ""Ромашка""' in csv_str) # ═══════════════════════════════════════════════════════════════ # Test: интеграция build_zip + build_mapping_csv # ═══════════════════════════════════════════════════════════════ print("\n=== build_zip + build_mapping_csv ===") mapping = {"Иванов И.И.": "Иванов_0001", "ООО Ромашка": "ООО_Технология_0001"} csv = build_mapping_csv(mapping) zip_data = build_zip([("doc.md", "# Договор с Иванов_0001".encode("utf-8"))], mapping_csv=csv) with zipfile.ZipFile(io.BytesIO(zip_data)) as zf: check("два файла в ZIP", len(zf.namelist()) == 2) doc = zf.read("doc.md").decode("utf-8") check("doc с заменой", "Иванов_0001" in doc) csv_content = zf.read("mapping.csv").decode("utf-8") check("CSV с обеими заменами", "Иванов_0001" in csv_content and "ООО_Технология_0001" in csv_content) # ═══════════════════════════════════════════════════════════════ print(f"\n{'='*50}") print(f"Результат: {passed} OK, {failed} FAIL из {passed + failed}") if failed > 0: print("❌ ТЕСТЫ ПРОВАЛЕНЫ") sys.exit(1) else: print("✅ ВСЕ ТЕСТЫ ПРОЙДЕНЫ")