fix(finder): реальный телефон директора убран из тестов службы поиска
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled

Утренняя чистка ПДн искала только в app/ и docs/ и пропустила моя/sales-finder —
папка скрыта от обычного поиска, номер оставался живым в трёх тестах (26 раз).
Заменён на фиктивный 79990000001, все 321 тест зелёные.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-07-19 15:01:25 +03:00
parent 31d6df0fcc
commit 235c55fd6f
4 changed files with 38 additions and 38 deletions
+4 -4
View File
@@ -1,6 +1,6 @@
# Brain Status (auto-generated)
Last updated: 2026-07-19T08:53:58.508Z
Last updated: 2026-07-19T09:11:02.760Z
| Контролёр | Состояние | Детали |
|---|---|---|
@@ -112,9 +112,9 @@ Episodes since last run: 542 / threshold: 10
| PID | Имя | CPU-время | Возраст |
|---|---|---|---|
| 3540 | MsMpEng | 5.14ч | NaNч |
| 10836 | Code | 2.88ч | NaNч |
| 1312 | svchost | 1.11ч | 0.0ч |
| 3540 | MsMpEng | 5.22ч | NaNч |
| 10836 | Code | 2.95ч | NaNч |
| 1312 | svchost | 1.13ч | 0.0ч |
⚠️ Проверь, не «осиротевшие» ли это процессы от завершённых Claude-сессий.
+31 -31
View File
@@ -11,7 +11,7 @@ def test_normalize_11_digits_with_7():
assert pc.normalize_phone("79139884603") == ("79139884603", "")
def test_normalize_10_digits_mobile():
assert pc.normalize_phone("9990000009") == ("79990000009", "")
assert pc.normalize_phone("9990000001") == ("79990000001", "")
def test_normalize_10_digits_landline():
assert pc.normalize_phone("3812361211") == ("73812361211", "")
@@ -50,24 +50,24 @@ def test_prepare_drops_tail_fragments():
assert [g["phone"] for g in got] == ["79059223914"]
def test_prepare_keeps_order_and_notes():
got = pc.prepare_phones(["9990000009", "3812361211"], city="Омск")
assert [g["phone"] for g in got] == ["79990000009", "73812361211"]
got = pc.prepare_phones(["9990000001", "3812361211"], city="Омск")
assert [g["phone"] for g in got] == ["79990000001", "73812361211"]
assert all(g["note"] == "" for g in got)
def test_prepare_real_screenshot_case():
raws = ["9990000009", "9139884603", "3812361211", "89139884603",
raws = ["9990000001", "9139884603", "3812361211", "89139884603",
"89059223914", "9223914", "3812302920", "3812606679"]
got = pc.prepare_phones(raws, city="Омск")
# 9139884603 выброшен как хвост 89139884603 (остаётся ВТОРОЕ вхождение),
# 9223914 — хвост 89059223914
assert [g["phone"] for g in got] == [
"79990000009", "73812361211", "79139884603",
"79990000001", "73812361211", "79139884603",
"79059223914", "73812302920", "73812606679"]
# --- parse_clean_response / clean_phone: ДаДата «Стандартизация телефона» ---
CLEAN_MOBILE = [{"source": "+79990000009", "type": "Мобильный", "phone": "+7 999 000-00-09",
CLEAN_MOBILE = [{"source": "+79990000001", "type": "Мобильный", "phone": "+7 999 000-00-01",
"provider": "ПАО \"МТС\"", "city": None, "region": "Омская",
"qc": 0}]
CLEAN_LANDLINE = [{"source": "+73812361211", "type": "Стационарный", "phone": "+7 3812 36-12-11",
@@ -99,15 +99,15 @@ def test_clean_phone_uses_cache(tmp_path):
calls.append(url)
return CLEAN_MOBILE
keys = ("api", "secret")
a = pc.clean_phone("+79990000009", keys, fetch=fake_fetch, cache_dir=str(tmp_path))
b = pc.clean_phone("+79990000009", keys, fetch=fake_fetch, cache_dir=str(tmp_path))
a = pc.clean_phone("+79990000001", keys, fetch=fake_fetch, cache_dir=str(tmp_path))
b = pc.clean_phone("+79990000001", keys, fetch=fake_fetch, cache_dir=str(tmp_path))
assert a["kind"] == b["kind"] == "mobile"
assert len(calls) == 1 # второй раз — из кэша, денег не тратим
def test_clean_phone_network_error_is_unknown(tmp_path):
def boom(url, headers, body):
raise IOError("сеть упала")
got = pc.clean_phone("+79990000009", ("a", "s"), fetch=boom, cache_dir=str(tmp_path))
got = pc.clean_phone("+79990000001", ("a", "s"), fetch=boom, cache_dir=str(tmp_path))
assert got["kind"] == "unknown"
CLEAN_VIRTUAL = [{"source": "+79001112233", "type": "Виртуальный", "phone": "+7 900 111-22-33",
@@ -140,8 +140,8 @@ def test_clean_phone_does_not_cache_service_error(tmp_path):
def fake_fetch(url, headers, body):
calls.append(url)
return responses[len(calls) - 1]
a = pc.clean_phone("+79990000009", ("a", "s"), fetch=fake_fetch, cache_dir=str(tmp_path))
b = pc.clean_phone("+79990000009", ("a", "s"), fetch=fake_fetch, cache_dir=str(tmp_path))
a = pc.clean_phone("+79990000001", ("a", "s"), fetch=fake_fetch, cache_dir=str(tmp_path))
b = pc.clean_phone("+79990000001", ("a", "s"), fetch=fake_fetch, cache_dir=str(tmp_path))
assert a["kind"] == "unknown" and b["kind"] == "mobile"
assert len(calls) == 2 # сбой сервиса не закэшировался — со второй попытки добрали
@@ -179,7 +179,7 @@ def test_hlr_alive_send_then_poll(tmp_path):
def fake_fetch(url, params):
calls.append(url)
return seq[len(calls) - 1]
v = pc.hlr_alive("+79990000009", ("login", "psw"),
v = pc.hlr_alive("+79990000001", ("login", "psw"),
fetch=fake_fetch, cache_dir=str(tmp_path), pause=0)
assert v == "alive"
assert calls[0].endswith("send.php") and calls[1].endswith("status.php")
@@ -199,7 +199,7 @@ def test_hlr_alive_cached_no_second_charge(tmp_path):
def test_hlr_alive_timeout_is_unknown_and_not_cached(tmp_path):
def fake_fetch(url, params):
return {"id": 77, "cnt": 1} if url.endswith("send.php") else {"status": 0, "err": 0}
v = pc.hlr_alive("+79990000009", ("l", "p"), fetch=fake_fetch,
v = pc.hlr_alive("+79990000001", ("l", "p"), fetch=fake_fetch,
cache_dir=str(tmp_path), tries=2, pause=0)
assert v == "unknown"
assert not list(tmp_path.iterdir()) # unknown не кэшируем
@@ -207,7 +207,7 @@ def test_hlr_alive_timeout_is_unknown_and_not_cached(tmp_path):
def test_hlr_alive_network_error_is_unknown(tmp_path):
def boom(url, params):
raise IOError()
assert pc.hlr_alive("+79990000009", ("l", "p"), fetch=boom,
assert pc.hlr_alive("+79990000001", ("l", "p"), fetch=boom,
cache_dir=str(tmp_path)) == "unknown"
def test_parse_hlr_string_fields():
@@ -229,12 +229,12 @@ def test_hlr_alive_id_zero_is_valid(tmp_path):
def fake_fetch(url, params):
calls.append(url)
return seq[len(calls) - 1]
v = pc.hlr_alive("+79990000009", ("l", "p"), fetch=fake_fetch,
v = pc.hlr_alive("+79990000001", ("l", "p"), fetch=fake_fetch,
cache_dir=str(tmp_path), pause=0)
assert v == "alive" # id=0 — успешная оплаченная отправка, опрашиваем статус
def test_hlr_alive_no_id_is_unknown(tmp_path):
v = pc.hlr_alive("+79990000009", ("l", "p"),
v = pc.hlr_alive("+79990000001", ("l", "p"),
fetch=lambda url, params: {"cnt": 1},
cache_dir=str(tmp_path), pause=0)
assert v == "unknown"
@@ -253,11 +253,11 @@ def _hlr_stub(phone, creds, **kw):
def test_check_full_happy_path():
checked, warn = pc.check_contact_phones(
["9990000009", "9139884603", "3812361211"], "Омск",
["9990000001", "9139884603", "3812361211"], "Омск",
("api", "secret"), ("login", "psw"), clean=_clean_stub, hlr=_hlr_stub)
assert warn is None
by = {c["phone"]: c for c in checked}
assert by["79990000009"]["kind"] == "mobile" and by["79990000009"]["alive"] == "alive"
assert by["79990000001"]["kind"] == "mobile" and by["79990000001"]["alive"] == "alive"
assert by["79139884603"]["alive"] == "dead"
assert by["73812361211"]["kind"] == "landline" and by["73812361211"]["alive"] is None
@@ -271,7 +271,7 @@ def test_check_no_dadata_keys_still_normalizes():
def test_check_no_smsc_creds_types_only():
checked, warn = pc.check_contact_phones(
["9990000009"], "Омск", ("api", "secret"), None,
["9990000001"], "Омск", ("api", "secret"), None,
clean=_clean_stub, hlr=_hlr_stub)
assert checked[0]["kind"] == "mobile" and checked[0]["alive"] is None
assert "smsc.txt" in warn
@@ -281,51 +281,51 @@ def test_check_hlr_only_for_mobiles():
def hlr_spy(phone, creds, **kw):
calls.append(phone)
return "alive"
pc.check_contact_phones(["9990000009", "3812361211"], "Омск",
pc.check_contact_phones(["9990000001", "3812361211"], "Омск",
("a", "s"), ("l", "p"), clean=_clean_stub, hlr=hlr_spy)
assert calls == ["79990000009"] # городской HLR-ом не проверяем (деньги)
assert calls == ["79990000001"] # городской HLR-ом не проверяем (деньги)
def test_sort_mobiles_first_gone_last():
checked = [
{"phone": "73812361211", "kind": "landline", "alive": None},
{"phone": "79139884603", "kind": "mobile", "alive": "dead"},
{"phone": "79990000009", "kind": "mobile", "alive": "alive"},
{"phone": "79990000001", "kind": "mobile", "alive": "alive"},
{"phone": "79000000000", "kind": "mobile", "alive": "gone"},
]
got = [c["phone"] for c in pc.sort_for_display(checked)]
assert got == ["79990000009", "79139884603", "73812361211", "79000000000"]
assert got == ["79990000001", "79139884603", "73812361211", "79000000000"]
def test_check_junk_not_hlr_checked_and_one_raise_does_not_kill_batch():
# мусорный номер не гоняем через HLR (деньги), а упавший clean не роняет остальные
calls = []
def clean_stub(phone, keys, **kw):
if phone == "79990000009":
if phone == "79990000001":
raise IOError("сервис лёг")
return {"kind": "junk", "provider": "", "city": "", "qc": 2}
def hlr_spy(phone, creds, **kw):
calls.append(phone)
return "alive"
checked, warn = pc.check_contact_phones(
["9990000009", "9139884603"], "Омск", ("a", "s"), ("l", "p"),
["9990000001", "9139884603"], "Омск", ("a", "s"), ("l", "p"),
clean=clean_stub, hlr=hlr_spy)
assert [c["phone"] for c in checked] == ["79990000009", "79139884603"]
assert [c["phone"] for c in checked] == ["79990000001", "79139884603"]
assert checked[0]["kind"] == "unknown" # упавший clean → номер без пометки
assert checked[1]["kind"] == "junk"
assert calls == [] # ни junk, ни unknown в HLR не ушли
def test_check_both_warnings_joined():
_, warn = pc.check_contact_phones(["9990000009"], "Омск", None, None,
_, warn = pc.check_contact_phones(["9990000001"], "Омск", None, None,
clean=_clean_stub, hlr=_hlr_stub)
assert "dadata_clean_keys.txt" in warn and "smsc.txt" in warn and "; " in warn
def test_check_unrecognized_raws_reported_not_lost():
checked, warn = pc.check_contact_phones(
["9990000009", "12345"], "Омск", None, None,
["9990000001", "12345"], "Омск", None, None,
clean=_clean_stub, hlr=_hlr_stub)
assert [c["phone"] for c in checked] == ["79990000009"]
assert [c["phone"] for c in checked] == ["79990000001"]
assert "не распознаны и скрыты: 12345" in warn
@@ -342,9 +342,9 @@ def test_sort_junk_ranks_above_gone():
# --- label / is_dead: одна строка для показа ---
def test_label_mobile_alive():
c = {"phone": "79990000009", "kind": "mobile", "provider": "МТС",
c = {"phone": "79990000001", "kind": "mobile", "provider": "МТС",
"city": "", "alive": "alive", "note": ""}
assert pc.label(c) == "79990000009 · МТС"
assert pc.label(c) == "79990000001 · МТС"
def test_label_landline():
c = {"phone": "73812361211", "kind": "landline", "provider": "Ростелеком",
+1 -1
View File
@@ -29,7 +29,7 @@ def test_to_rows_has_contact_columns():
def test_to_rows_uses_checked_phone_labels():
from salesfinder.phonecheck import label as pc_label
alive = {"phone": "79990000009", "kind": "mobile", "provider": "МТС",
alive = {"phone": "79990000001", "kind": "mobile", "provider": "МТС",
"city": "", "alive": "alive", "note": ""}
dead = {"phone": "79139884603", "kind": "mobile", "provider": "Билайн",
"city": "", "alive": "dead", "note": ""}
+2 -2
View File
@@ -398,7 +398,7 @@ def test_results_shows_collected_contacts():
def test_results_shows_checked_phones_dead_struck_through():
alive = {"phone": "79990000009", "kind": "mobile", "provider": "МТС",
alive = {"phone": "79990000001", "kind": "mobile", "provider": "МТС",
"city": "", "alive": "alive", "note": ""}
offline = {"phone": "79139884603", "kind": "mobile", "provider": "Билайн",
"city": "", "alive": "dead", "note": ""}
@@ -411,7 +411,7 @@ def test_results_shows_checked_phones_dead_struck_through():
"evidence": [], "hotness": 90}]}
html = ui.results_html(run)
assert "<s class=dead>79000000000 · не существует</s>" in html
assert "79990000009 · МТС" in html
assert "79990000001 · МТС" in html
assert "79139884603 · Билайн" in html
assert "<s class=dead>79139884603" not in html
assert "отключён" not in html