""" Тест DrHider — много проверок. Файл: /home/naeel/nubes/contracts/contracts-flask/deploy/tests/test_drhider.py """ import sys, os, io, zipfile, json, base64 sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) from services.drhider import obfuscate_files, TwoPassObfuscator, COMPANY_PATTERN, PERSON_PATTERN TEST_ZIP = "/home/naeel/nubes/contracts/dogovora/примеры_договоров_для_ИИ.zip" def run(text, desc): """Обработать текст и проверить что needle НЕ найдено.""" z, c = obfuscate_files([('t.txt', text.encode(), '')]) with zipfile.ZipFile(io.BytesIO(z)) as zf: out = zf.read('t.txt').decode() csv = zf.read('mapping.csv').decode() return out, csv errors = 0 def check(ok, msg): global errors if ok: print(f" ✅ {msg}") else: print(f" ❌ {msg}") errors += 1 print("=" * 60) print("DrHider — полный тест") print("=" * 60) # ── 1. Телефоны ── print("\n📞 Телефоны:") out, csv = run("Звоните +7 (495) 789-41-35 или 8-800-555-35-35", "телефоны") check("+7 (495) 789-41-35" not in out, "+7 (495) 789-41-35 заменён") check("8-800-555-35-35" not in out, "8-800-555-35-35 заменён") check("phone" in csv, "тип phone в CSV") # ── 2. Телефон не матчит число внутри счёта ── print("\n📞 Телефон vs номер счёта:") out, csv = run("Кор/сч 30101810400000000225", "счёт") check("30101810400000000225" not in out, "номер счёта заменён (ks)") check("+7" not in out, "нет ложного телефона внутри счёта") # ── 3. Email ── print("\n📧 Email:") out, csv = run("Пишите info@nubes.ru и support@company.org", "email") check("info@nubes.ru" not in out, "info@nubes.ru заменён") check("support@company.org" not in out, "support@company.org заменён") check("email" in csv, "тип email в CSV") # ── 4. Компании (разные кавычки) ── print("\n🏢 Компании:") out, csv = run('ООО "Ромашка" и АО \u201cXXX003\u201d и ЗАО «Тест»', "компании") check('ООО "Ромашка"' not in out, 'ООО "Ромашка" заменён') check('АО \u201cXXX003\u201d' not in out, 'АО "XXX003" (unicode) заменён') check('ЗАО «Тест»' not in out, 'ЗАО «Тест» заменён') check('company' in csv, 'тип company в CSV') # ── 5. НУБЕС whitelist ── print("\n🛡️ НУБЕС whitelist:") out, csv = run('ООО "НУБЕС" и ООО \u201cНУБЕС\u201d', "НУБЕС") check('ООО "НУБЕС"' in out, 'НУБЕС (ASCII) НЕ заменён') check('ООО \u201cНУБЕС\u201d' in out, 'НУБЕС (unicode) НЕ заменён') # ── 6. ИНН/ОГРН/КПП/БИК ── print("\n🔢 ИНН/ОГРН/КПП/БИК:") out, csv = run("ИНН 9706005293, КПП 772401001, ОГРН 1207700098759, БИК 044525225", "реквизиты") check("9706005293" not in out, "ИНН заменён") check("772401001" not in out, "КПП заменён") check("1207700098759" not in out, "ОГРН заменён") check("044525225" not in out, "БИК заменён") check("inn_ul" in csv, "inn_ul в CSV") # ── 7. Расчётный счёт ── print("\n💳 Расчётный счёт:") out, csv = run("р/с 40702810738000174030 и Кор/сч 30101810400000000225", "счета") check("40702810738000174030" not in out, "р/с заменён") check("30101810400000000225" not in out, "кор/сч заменён") check("rs" in csv, "rs в CSV") check("ks" in csv, "ks в CSV") # ── 8. ФИО (инициалы + полностью) ── print("\n👤 ФИО:") out, csv = run("Директор Степаненко В.С. и Иванов Александр Петрович", "ФИО") check("Степаненко В.С." not in out, "Степаненко В.С. заменён") check("Иванов Александр Петрович" not in out, "Иванов А.П. заменён") # ── 9. Паспорт ── print("\n📄 Паспорт:") out, csv = run("паспорт 12 34 567890", "паспорт") check("12 34 567890" not in out, "номер паспорта заменён") # ── 10. Согласованность (одна сущность → одна замена) ── print("\n🔄 Согласованность:") z, c = obfuscate_files([ ('a.txt', b'OOO Romashka +79991234567', ''), ('b.txt', b'OOO Romashka +79991234567', ''), ]) with zipfile.ZipFile(io.BytesIO(z)) as zf: a = zf.read('a.txt').decode() b = zf.read('b.txt').decode() check(a == b, f"оба файла одинаковы: {a}") # ── 11. ZIP внутри ZIP ── print("\n📦 ZIP внутри:") with open(TEST_ZIP, 'rb') as f: raw = f.read() z, c = obfuscate_files([('test.zip', raw, '')]) with zipfile.ZipFile(io.BytesIO(z)) as zf: names = zf.namelist() check(len(names) == 6, f"6 файлов (5 + csv): {len(names)}") check('mapping.csv' in names, "mapping.csv есть") check(not any(n.endswith('.zip') for n in names), "нет .zip в выдаче") # Проверим что docx внутри валидны for n in names: if n.endswith('.docx'): try: from docx import Document Document(io.BytesIO(zf.read(n))) check(True, f"{n} — валидный docx") except: check(False, f"{n} — БИТЫЙ docx") # ── 12. .doc бинарный — не трогаем ── print("\n📄 .doc бинарный:") # Создаём фейковый .doc файл (просто бинарные данные) z, c = obfuscate_files([('old.doc', b'\xD0\xCF\x11\xE0' + b'\x00' * 100, '')]) with zipfile.ZipFile(io.BytesIO(z)) as zf: doc_content = zf.read('old.doc') check(len(doc_content) == 104, ".doc сохранён без изменений") # ── 13. Много файлов ── print("\n📚 20 файлов:") many = [('f{}.txt'.format(i), 'OOO Test{} +7999{}'.format(i, i).encode(), '') for i in range(20)] z, c = obfuscate_files(many) with zipfile.ZipFile(io.BytesIO(z)) as zf: check(len(zf.namelist()) == 21, "20 файлов + csv") # ── 14. COMPANY_PATTERN не жадный ── print("\n🔤 COMPANY_PATTERN границы:") m = COMPANY_PATTERN.search("АО Test с дополнительным текстом дальше") check(m is not None and 'дальше' not in m.group(0), "не захватывает лишний текст") # ── 15. PERSON_PATTERN только кириллица ── print("\n🔤 PERSON_PATTERN кириллица:") m = PERSON_PATTERN.search("Next Generation Cloud service") check(m is None, "английский не матчится") m2 = PERSON_PATTERN.search("Иванов Иван Иванович") check(m2 is not None, "русский матчится") # ── ИТОГО ── print(f"\n{'='*60}") if errors: print(f"❌ {errors} ошибок") else: print("✅ ВСЕ ТЕСТЫ ПРОЙДЕНЫ") print(f"{'='*60}") sys.exit(0 if errors == 0 else 1)