test(generator): анонимность события проверяется и текстом, не только числом

- Зачем:
  - холодное ревью Кодексом (линия дефектов) показало дыру в проверке
    анонимности: личность, уехавшая строкой в сыром `ecommerce`, проходила
    мимо сравнения числовых колонок, и тест оставался зелёным. Дыра
    воспроизведена: user_id, дописанный в блок покупки, тест не замечал.
- Что:
  - тест анонимности спрашивает двумя способами: числовые колонки дня — со
    всеми личностями аудитории, строки покупок целиком — с личностями
    покупателей. Сырой блок собирается руками, и лишнее поле в нём теперь
    красит проверку.
- Проверка:
  - из `generator/`: make lint, make typecheck, make test (417 тестов).
  - мутация «дописать user_id в ecommerce покупки»: до правки тест зелёный,
    после — красный.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 12:46:16 +03:00
co-authored by Claude Opus 5
parent b2fcc7e593
commit db632c2558
+14 -3
View File
@@ -189,9 +189,12 @@ def test_both_cookies_of_a_pair_order_as_one_user(week: list[day.Day]):
def test_the_user_id_never_reaches_the_metrica_event(weekday: day.Day): def test_the_user_id_never_reaches_the_metrica_event(weekday: day.Day):
"""Кликстрим анонимен: личность держится формой контракта, а не забывчивостью. """Кликстрим анонимен: личность держится формой контракта, а не забывчивостью.
Проверяются числовые колонки события целиком: личность — число того же Спрашивается двумя способами, потому что утечь личность может двумя.
порядка, что кука и номер события, и попасть она могла бы только в Числом — тогда её видно в числовых колонках дня, и они сверяются со
такую. Имени для неё в контракте нет вовсе. всеми личностями дневной аудитории. Текстом — тогда сравнение чисел
её прозевало бы, поэтому строки покупок читаются целиком, вместе с
сырым `ecommerce`: этот блок собирается руками, и дописать в него
лишнее поле проще всего. Имени для личности в контракте схемы нет.
""" """
people = set(plan.audience(CANONICAL_SEED, WEEKDAY).person_id.tolist()) people = set(plan.audience(CANONICAL_SEED, WEEKDAY).person_id.tolist())
assert people assert people
@@ -200,4 +203,12 @@ def test_the_user_id_never_reaches_the_metrica_event(weekday: day.Day):
for name, value in weekday.columns.items(): for name, value in weekday.columns.items():
if np.issubdtype(value.dtype, np.integer): if np.issubdtype(value.dtype, np.integer):
assert not (set(value.tolist()) & people), name assert not (set(value.tolist()) & people), name
events = purchases_of(weekday)
text = "\n".join(
" ".join(str(value[row]) for value in events.values())
for row in range(events["WatchID"].size)
)
for person in weekday.orders.user_id.tolist():
assert str(person) not in text
assert set(weekday.orders.user_id.tolist()) <= people assert set(weekday.orders.user_id.tolist()) <= people