fix(generator): сохранена фактура визита при восстановлении

- Зачем:
  - визит после восстановления не должен менять браузер и источники перехода внутри одного click_id.
- Что:
  - добавлен base_click_id в state v3 для восстановления донора фактуры.
  - исправлено восстановление timestamp offset без потери микросекунд.
  - расширены тесты и стыковая проверка browser/source и device/os/geo.
- Проверка:
  - uv run --with-requirements generator/requirements.txt pytest generator/tests -q.
  - bash -n scripts/check_generated_analytics.sh.
  - git diff --cached --check.
This commit is contained in:
2026-07-04 21:46:23 +03:00
parent 0cbfe9b32e
commit e2d06841de
13 changed files with 464 additions and 77 deletions
+10 -10
View File
@@ -926,11 +926,11 @@ class TestGeneratorServiceBackfill:
}
class TestGeneratorServiceStateV2:
"""Тесты подключения state v2 к сервисному запуску."""
class TestGeneratorServiceState:
"""Тесты подключения state к сервисному запуску."""
def test_start_restores_tick_stream_state_v2(self, base_config, event_dictionary):
"""Сервис восстанавливает популяцию и активные визиты из state v2."""
def test_start_restores_tick_stream_state(self, base_config, event_dictionary):
"""Сервис восстанавливает популяцию и активные визиты из state."""
source_generator = EventGenerator(event_dictionary, base_config)
source_stream = TickStreamGenerator(source_generator)
tick_at = datetime.now(timezone.utc).replace(tzinfo=None)
@@ -1073,8 +1073,8 @@ class TestGeneratorServiceStateV2:
assert service._model_time == model_t_end
assert service.stream.active_visit_count == source_stream.active_visit_count
def test_save_state_writes_tick_stream_state_v2(self, base_config):
"""Сервис сохраняет v2-снимок тикового слоя."""
def test_save_state_writes_tick_stream_state(self, base_config):
"""Сервис сохраняет снимок тикового слоя."""
service = GeneratorService(base_config)
service.state_manager = MagicMock()
tick_at = datetime.now(timezone.utc).replace(tzinfo=None)
@@ -1085,7 +1085,7 @@ class TestGeneratorServiceStateV2:
service._save_state("batch-1")
saved_state = service.state_manager.save.call_args.args[0]
assert saved_state.version == "2.0"
assert saved_state.version == "3.0"
assert saved_state.model_timestamp == tick_at.replace(tzinfo=timezone.utc)
assert saved_state.wall_timestamp.tzinfo is not None
assert saved_state.model_time_speed == base_config.model_time_speed
@@ -1162,13 +1162,13 @@ class TestGeneratorServiceStateV2:
state_manager.load.assert_not_called()
assert service._tick == 0
def test_invalid_restored_v2_state_starts_fresh(self, base_config, caplog):
"""Сервис не падает, если v2 state ссылается на неизвестный профиль."""
def test_invalid_restored_state_starts_fresh(self, base_config, caplog):
"""Сервис не падает, если state ссылается на неизвестный профиль."""
state_manager = MagicMock()
state_manager.load.return_value = GeneratorState(
tick=9,
rng_state=random.Random(42).getstate(),
last_batch_id="bad-v2",
last_batch_id="bad-state",
last_timestamp=datetime.now(timezone.utc),
model_timestamp=base_config.model_t0,
wall_timestamp=datetime.now(timezone.utc),