chore(tradein/privacy): перенумерация 231 и merge main - коллизия префикса (#2547)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m21s
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m21s
This commit is contained in:
parent
b93bee5393
commit
3ee99efaa4
6 changed files with 18 additions and 18 deletions
|
|
@ -118,7 +118,7 @@ async def create_trade_in_lead(
|
|||
# ранее — только audit-лог, #2497 TODO). client_ip может быть None (нет
|
||||
# X-Forwarded-For и request.client) — колонка nullable, CAST(NULL AS inet) валиден.
|
||||
#
|
||||
# ЭТАП 4 B2C: expires_at (migration 230) — раньше лид хранился бессрочно
|
||||
# ЭТАП 4 B2C: expires_at (migration 231) — раньше лид хранился бессрочно
|
||||
# (никакого TTL вообще не было, в отличие от trade_in_estimates.expires_at).
|
||||
# Считаем на insert-time тем же паттерном, что estimator.py делает для
|
||||
# trade_in_estimates — retention-период вынесен в settings, не хардкод.
|
||||
|
|
|
|||
|
|
@ -824,7 +824,7 @@ class Settings(BaseSettings):
|
|||
# к задаче. ENV: TRADE_IN_ESTIMATE_RETENTION_HOURS.
|
||||
trade_in_estimate_retention_hours: int = 24
|
||||
|
||||
# trade_in_leads.expires_at TTL (дни от момента создания, migration 230).
|
||||
# trade_in_leads.expires_at TTL (дни от момента создания, migration 231).
|
||||
# У trade_in_leads раньше вообще не было срока хранения — лид (телефон +
|
||||
# согласие) жил в БД бессрочно. 180 дней (6 месяцев) — рабочий default для
|
||||
# НЕконвертированных маркетинговых лидов (типичный индустриальный диапазон
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"""Physically delete expired personal data — ЭТАП 4 B2C retention enforcement (152-ФЗ).
|
||||
|
||||
WHY:
|
||||
trade_in_estimates.expires_at (и, начиная с migration 230, trade_in_leads.expires_at)
|
||||
trade_in_estimates.expires_at (и, начиная с migration 231, trade_in_leads.expires_at)
|
||||
defined a retention window, but neither table had any background job that actually
|
||||
DELETEd rows once expired -- expires_at was used ONLY as a read-time filter
|
||||
(GET /estimate/{id}: "AND expires_at > NOW()"). Personal data (address / phone)
|
||||
|
|
@ -11,7 +11,7 @@ WHY:
|
|||
WHAT:
|
||||
Batched physical DELETE for both tables, run nightly by the kit-scheduler (see
|
||||
app.services.product_handlers._job_purge_expired_trade_in_data, scrape_schedules
|
||||
row seeded by migration 230 -- seeded enabled=false, see that migration's docstring
|
||||
row seeded by migration 231 -- seeded enabled=false, see that migration's docstring
|
||||
for why). Same architecture as app/tasks/deactivate_stale_avito.py (sync, DB-only,
|
||||
invoked via run_in_executor from the async kit handler).
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
-- defense-in-depth на случай будущего кода, который забудет про gate).
|
||||
--
|
||||
-- Индекс на expires_at — обслуживает будущую retention-задачу
|
||||
-- purge_expired_trade_in_data (см. migration 230), которая физически
|
||||
-- purge_expired_trade_in_data (см. migration 231), которая физически
|
||||
-- удаляет строки, чей expires_at истёк (сегодня expires_at используется
|
||||
-- ТОЛЬКО как read-time фильтр, см. GET /estimate/{id}: "AND expires_at >
|
||||
-- NOW()" — без индекса такой batched-DELETE делал бы full scan таблицы
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
-- 230_trade_in_privacy_retention.sql
|
||||
-- 231_trade_in_privacy_retention.sql
|
||||
-- ЭТАП 4 B2C launch — retention enforcement (152-ФЗ): срок хранения ДОЛЖЕН
|
||||
-- приводить к физическому удалению, а не быть декоративным полем.
|
||||
--
|
||||
|
|
@ -26,7 +26,7 @@ os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:
|
|||
from app.tasks import purge_expired_trade_in_data as task_mod
|
||||
|
||||
_SQL_DIR = Path(__file__).resolve().parents[1] / "data" / "sql"
|
||||
_MIGRATION_230 = _SQL_DIR / "230_trade_in_privacy_retention.sql"
|
||||
_MIGRATION_231 = _SQL_DIR / "231_trade_in_privacy_retention.sql"
|
||||
|
||||
|
||||
class _FakeResult:
|
||||
|
|
@ -197,29 +197,29 @@ def test_idempotent_zero_rowcount_is_not_an_error(monkeypatch: pytest.MonkeyPatc
|
|||
assert marked["kind"] == "done"
|
||||
|
||||
|
||||
# ── migration 230 ────────────────────────────────────────────────────────────
|
||||
# ── migration 231 ────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_migration_230_exists() -> None:
|
||||
assert _MIGRATION_230.is_file(), f"missing migration: {_MIGRATION_230}"
|
||||
def test_migration_231_exists() -> None:
|
||||
assert _MIGRATION_231.is_file(), f"missing migration: {_MIGRATION_231}"
|
||||
|
||||
|
||||
def test_migration_230_is_transactional() -> None:
|
||||
sql = _MIGRATION_230.read_text("utf-8")
|
||||
def test_migration_231_is_transactional() -> None:
|
||||
sql = _MIGRATION_231.read_text("utf-8")
|
||||
assert "BEGIN;" in sql
|
||||
assert "COMMIT;" in sql
|
||||
|
||||
|
||||
def test_migration_230_backfills_and_sets_not_null() -> None:
|
||||
sql = _MIGRATION_230.read_text("utf-8")
|
||||
def test_migration_231_backfills_and_sets_not_null() -> None:
|
||||
sql = _MIGRATION_231.read_text("utf-8")
|
||||
assert "ADD COLUMN IF NOT EXISTS expires_at" in sql
|
||||
assert "WHERE expires_at IS NULL" in sql
|
||||
assert "SET NOT NULL" in sql
|
||||
assert "180 days" in sql
|
||||
|
||||
|
||||
def test_migration_230_seeds_purge_schedule_disabled_by_default() -> None:
|
||||
sql = _MIGRATION_230.read_text("utf-8")
|
||||
def test_migration_231_seeds_purge_schedule_disabled_by_default() -> None:
|
||||
sql = _MIGRATION_231.read_text("utf-8")
|
||||
assert "'purge_expired_trade_in_data'" in sql
|
||||
assert "ON CONFLICT (source) DO NOTHING" in sql
|
||||
# Seeded disabled -- first automated PII-DELETE job in trade-in deserves a
|
||||
|
|
@ -227,6 +227,6 @@ def test_migration_230_seeds_purge_schedule_disabled_by_default() -> None:
|
|||
assert re.search(r"'purge_expired_trade_in_data',\s*\n\s*false,", sql)
|
||||
|
||||
|
||||
def test_migration_230_no_psycopg_trap() -> None:
|
||||
sql = _MIGRATION_230.read_text("utf-8")
|
||||
def test_migration_231_no_psycopg_trap() -> None:
|
||||
sql = _MIGRATION_231.read_text("utf-8")
|
||||
assert not re.search(r":\w+::", sql)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue