from __future__ import annotations import uuid from contextlib import asynccontextmanager import pytest from sqlalchemy.dialects.postgresql import JSONB from app.crm import CrmOutcome, CrmResult from app.engine import INSERT_CRM_COMMAND, UPDATE_CRM_COMMAND, WorkflowEngine from app.repository import Profile class Result: rowcount = 1 class Connection: async def execute(self, statement, params=None): return Result() class FakeRepository: def __init__(self) -> None: self.mapping = None self.statements: list[str] = [] @asynccontextmanager async def transaction(self): yield Connection() async def active_mapping(self, user_id): return self.mapping async def reserve_limiter_token(self, refill_per_second, burst): return 0 class FakeCrm: def __init__(self, user_id: uuid.UUID) -> None: self.user_id = user_id self.calls: list[tuple[str, dict]] = [] async def call(self, method, params, *, mutating): self.calls.append((method, params)) if method == "crm.duplicate.findbycomm": return CrmResult(CrmOutcome.SUCCEEDED, {"CONTACT": ["9", "10"]}) if method == "crm.contact.get": contact_id = str(params["id"]) return CrmResult( CrmOutcome.SUCCEEDED, { "ID": contact_id, "CREATED_TIME": "2026-08-06T10:00:00Z", "UF_CRM_100": None, }, ) return CrmResult(CrmOutcome.SUCCEEDED, True) def test_crm_command_json_payloads_have_explicit_jsonb_types() -> None: assert isinstance(INSERT_CRM_COMMAND._bindparams["safe_request"].type, JSONB) assert isinstance(UPDATE_CRM_COMMAND._bindparams["safe_response"].type, JSONB) @pytest.mark.asyncio async def test_multiple_contacts_choose_numeric_newest(full_settings) -> None: user_id = uuid.uuid4() repository = FakeRepository() crm = FakeCrm(user_id) engine = WorkflowEngine(repository, crm, full_settings) await engine._map_or_create( uuid.uuid4(), Profile(user_id=user_id, phone="+79001234567", identity_status="A", profile_status="A"), ) updates = [params for method, params in crm.calls if method == "crm.contact.update"] assert updates[0]["id"] == "10" assert not any(method == "crm.contact.add" for method, _ in crm.calls)