test_party_writes.py
Language Python13.4 KB316 lines
"""Pins the payloads and replies of create_contact, update_contact,create_company and update_company, which share one internal implementation."""import asyncioimport pytestfrom conftest import seqimport serverrun = asyncio.runNO_DUP: dict = {}SAM = [{"ID": "7", "NAME": "Sam", "LAST_NAME": "Lee"}]CONTACT_FIELDS = {"fields": {"NAME": {"type": "string", "title": "First name"},"UF_CRM_MARKET": {"type": "enumeration","title": "Market","items": [{"ID": "44", "VALUE": "Government"}, {"ID": "45", "VALUE": "Retail"}],},}}COMPANY_FIELDS = {"fields": {"TITLE": {"type": "string", "title": "Company name"},"UF_CRM_TIER": {"type": "enumeration","title": "Partner tier","items": [{"ID": "61", "VALUE": "Palladium"}, {"ID": "62", "VALUE": "Iridium"}],},}}def fm(value):return [{"VALUE": value, "VALUE_TYPE": "WORK"}]# --------------------------------------------------------------------------- create_contactdef test_create_contact_full_payload(fake_bitrix, logged):bx = fake_bitrix({"crm.duplicate.findbycomm": NO_DUP,"user.get": SAM,"crm.contact.add": 501,})out = run(server.create_contact(" Ana ", last_name="Reyes ", email=" ana@buyer.example ",phone="+1 555 010 2000", assignee="7"))assert out == "Created contact 501: Ana Reyes."assert bx.calls == [("crm.duplicate.findbycomm", {"entity_type": "CONTACT", "type": "EMAIL", "values": ["ana@buyer.example"]}),("crm.duplicate.findbycomm", {"entity_type": "CONTACT", "type": "PHONE", "values": ["+1 555 010 2000"]}),("user.get", {"ID": "7"}),("crm.contact.add", {"fields": {"NAME": "Ana","LAST_NAME": "Reyes","EMAIL": fm("ana@buyer.example"),"PHONE": fm("+1 555 010 2000"),"ASSIGNED_BY_ID": "7",}}),]assert list(bx.calls[-1][1]["fields"]) == ["NAME", "LAST_NAME", "EMAIL", "PHONE", "ASSIGNED_BY_ID"]assert logged == [("create_contact", {"name": "Ana", "id": 501}, "created")]def test_create_contact_name_only(fake_bitrix, logged):bx = fake_bitrix({"crm.contact.add": {"id": 88}})out = run(server.create_contact("Ana"))assert out == "Created contact 88: Ana."assert bx.calls == [("crm.contact.add", {"fields": {"NAME": "Ana"}})]assert logged == [("create_contact", {"name": "Ana", "id": 88}, "created")]def test_create_contact_passes_through_unexpected_add_result(fake_bitrix, logged):fake_bitrix({"crm.contact.add": "90"})assert run(server.create_contact("Ana")) == "Created contact 90: Ana."def test_create_contact_returns_existing_duplicate(fake_bitrix, logged):bx = fake_bitrix({"crm.duplicate.findbycomm": seq(NO_DUP, {"CONTACT": [42, 43]})})out = run(server.create_contact("Ana", email="ana@buyer.example", phone="5550102000"))assert out == "Contact already exists (id 42, phone match); not creating a duplicate. Link it instead."assert bx.methods == ["crm.duplicate.findbycomm", "crm.duplicate.findbycomm"]assert logged == []def test_create_contact_dup_check_error_is_skipped(fake_bitrix, logged):bx = fake_bitrix({"crm.duplicate.findbycomm": server.BitrixError("ACCESS_DENIED: no"),"crm.contact.add": 3,})assert run(server.create_contact("Ana", email="ana@buyer.example")) == "Created contact 3: Ana."assert bx.methods == ["crm.duplicate.findbycomm", "crm.contact.add"]def test_create_contact_requires_name(fake_bitrix, logged):bx = fake_bitrix({})assert run(server.create_contact(" \x00 ")) == "Refused: a contact name is required."assert bx.calls == []def test_create_contact_unknown_assignee(fake_bitrix, logged):bx = fake_bitrix({"user.search": []})out = run(server.create_contact("Ana", assignee="Nobody"))assert out == "No Bitrix user found matching 'Nobody'."assert bx.calls == [("user.search", {"FILTER": {"FIND": "Nobody"}})]assert logged == []def test_create_contact_add_error(fake_bitrix, logged):fake_bitrix({"crm.contact.add": server.BitrixError("INVALID: bad")})assert run(server.create_contact("Ana")) == "Error creating contact: INVALID: bad"assert logged == [("create_contact", {"name": "Ana"}, "error: INVALID: bad")]# --------------------------------------------------------------------------- create_companydef test_create_company_full_payload(fake_bitrix, logged):bx = fake_bitrix({"crm.duplicate.findbycomm": NO_DUP,"user.current": {"ID": "9", "NAME": "Me"},"crm.company.add": 700,})out = run(server.create_company(" Harbor Signs ", email="hello@harbor.example",phone="555-010-3000", assignee="me"))assert out == "Created company 700: Harbor Signs."assert bx.calls == [("crm.duplicate.findbycomm", {"entity_type": "COMPANY", "type": "EMAIL", "values": ["hello@harbor.example"]}),("crm.duplicate.findbycomm", {"entity_type": "COMPANY", "type": "PHONE", "values": ["555-010-3000"]}),("user.current", None),("crm.company.add", {"fields": {"TITLE": "Harbor Signs","EMAIL": fm("hello@harbor.example"),"PHONE": fm("555-010-3000"),"ASSIGNED_BY_ID": "9",}}),]assert list(bx.calls[-1][1]["fields"]) == ["TITLE", "EMAIL", "PHONE", "ASSIGNED_BY_ID"]assert logged == [("create_company", {"title": "Harbor Signs", "id": 700}, "created")]def test_create_company_returns_existing_duplicate(fake_bitrix, logged):fake_bitrix({"crm.duplicate.findbycomm": {"COMPANY": ["12"]}})out = run(server.create_company("Harbor Signs", email="hello@harbor.example"))assert out == "Company already exists (id 12, email match); not creating a duplicate. Link it instead."assert logged == []def test_create_company_requires_title(fake_bitrix, logged):bx = fake_bitrix({})assert run(server.create_company(" ")) == "Refused: a company name is required."assert bx.calls == []def test_create_company_numeric_assignee_is_taken_as_id(fake_bitrix, logged):# A numeric assignee is used as the id even when user.get finds nobody.bx = fake_bitrix({"user.get": [], "crm.company.add": 5})assert run(server.create_company("Harbor", assignee="31")) == "Created company 5: Harbor."assert bx.calls[-1] == ("crm.company.add", {"fields": {"TITLE": "Harbor", "ASSIGNED_BY_ID": "31"}})def test_create_company_add_error(fake_bitrix, logged):fake_bitrix({"crm.company.add": server.BitrixError("INVALID: bad")})assert run(server.create_company("Harbor")) == "Error creating company: INVALID: bad"assert logged == [("create_company", {"title": "Harbor"}, "error: INVALID: bad")]# --------------------------------------------------------------------------- update_contactdef test_update_contact_full_payload(fake_bitrix, logged):bx = fake_bitrix({"user.get": SAM,"crm.item.fields": CONTACT_FIELDS,"crm.contact.update": True,})out = run(server.update_contact(" 12 ", name="Ana", last_name="Reyes", email="ana@buyer.example",phone="5550102000", assignee="7", fields={"Market": "Government"}))assert out == "Updated contact 12: NAME, LAST_NAME, EMAIL, PHONE, ASSIGNED_BY_ID, Market=Government."assert bx.calls == [("user.get", {"ID": "7"}),("crm.item.fields", {"entityTypeId": 3, "useOriginalUfNames": "Y"}),("crm.contact.update", {"id": "12", "fields": {"NAME": "Ana","LAST_NAME": "Reyes","EMAIL": fm("ana@buyer.example"),"PHONE": fm("5550102000"),"ASSIGNED_BY_ID": "7","UF_CRM_MARKET": "44",}}),]assert list(bx.calls[-1][1]["fields"]) == ["NAME", "LAST_NAME", "EMAIL", "PHONE", "ASSIGNED_BY_ID", "UF_CRM_MARKET"]fields_logged = ["NAME", "LAST_NAME", "EMAIL", "PHONE", "ASSIGNED_BY_ID", "UF_CRM_MARKET"]assert logged == [("update_contact", {"record_id": "12", "fields": fields_logged}, "updated")]def test_update_contact_single_field(fake_bitrix, logged):bx = fake_bitrix({"crm.contact.update": True})assert run(server.update_contact("12", phone="5550102000")) == "Updated contact 12: PHONE."assert bx.calls == [("crm.contact.update", {"id": "12", "fields": {"PHONE": fm("5550102000")}})]def test_update_contact_rejects_standard_field_in_fields(fake_bitrix, logged):bx = fake_bitrix({"crm.item.fields": CONTACT_FIELDS})out = run(server.update_contact("12", fields={"First name": "Ana"}))assert out == ("Refused: NAME are standard fields; use the name/email/phone args ""instead of `fields`.")assert bx.methods == ["crm.item.fields"]def test_update_contact_unknown_enum_option(fake_bitrix, logged):fake_bitrix({"crm.item.fields": CONTACT_FIELDS})out = run(server.update_contact("12", fields={"Market": "Space"}))assert out == "'Space' is not an option of UF_CRM_MARKET. Options: Government | Retail"def test_update_contact_nothing_to_update(fake_bitrix, logged):bx = fake_bitrix({})out = run(server.update_contact("12", name=" "))assert out == "Nothing to update: pass a name/email/phone/assignee or fields."assert bx.calls == []def test_update_contact_invalid_id(fake_bitrix, logged):bx = fake_bitrix({})out = run(server.update_contact("12a", name="Ana"))assert out == "Invalid record_id '12a': a CRM record ID is a positive integer."assert bx.calls == []def test_update_contact_unknown_assignee(fake_bitrix, logged):bx = fake_bitrix({"user.search": []})out = run(server.update_contact("12", name="Ana", assignee="Nobody"))assert out == "No Bitrix user found matching 'Nobody'."assert bx.methods == ["user.search"]def test_update_contact_update_error(fake_bitrix, logged):fake_bitrix({"crm.contact.update": server.BitrixError("NOT_FOUND: gone")})out = run(server.update_contact("12", name="Ana"))assert out == "Error updating contact 12: NOT_FOUND: gone"assert logged == [("update_contact", {"record_id": "12", "fields": ["NAME"]}, "error: NOT_FOUND: gone")]# --------------------------------------------------------------------------- update_companydef test_update_company_full_payload(fake_bitrix, logged):bx = fake_bitrix({"user.search": [{"ID": "7", "NAME": "Sam", "LAST_NAME": "Lee"}],"crm.item.fields": COMPANY_FIELDS,"crm.company.update": True,})out = run(server.update_company("40", title=" Harbor Signs ", email="hello@harbor.example",phone="555-010-3000", assignee="Sam", fields={"Partner tier": "Iridium"}))assert out == "Updated company 40: TITLE, EMAIL, PHONE, ASSIGNED_BY_ID, Partner tier=Iridium."assert bx.calls == [("user.search", {"FILTER": {"FIND": "Sam"}}),("crm.item.fields", {"entityTypeId": 4, "useOriginalUfNames": "Y"}),("crm.company.update", {"id": "40", "fields": {"TITLE": "Harbor Signs","EMAIL": fm("hello@harbor.example"),"PHONE": fm("555-010-3000"),"ASSIGNED_BY_ID": "7","UF_CRM_TIER": "62",}}),]fields_logged = ["TITLE", "EMAIL", "PHONE", "ASSIGNED_BY_ID", "UF_CRM_TIER"]assert logged == [("update_company", {"record_id": "40", "fields": fields_logged}, "updated")]def test_update_company_rejects_standard_field_in_fields(fake_bitrix, logged):fake_bitrix({"crm.item.fields": COMPANY_FIELDS})out = run(server.update_company("40", fields={"Company name": "X"}))assert out == ("Refused: TITLE are standard fields; use the title/email/phone args ""instead of `fields`.")def test_update_company_nothing_to_update(fake_bitrix, logged):out = run(server.update_company("40"))assert out == "Nothing to update: pass a title/email/phone/assignee or fields."def test_update_company_invalid_id(fake_bitrix, logged):bx = fake_bitrix({})out = run(server.update_company("-4", title="Harbor"))assert out == "Invalid record_id '-4': a CRM record ID is a positive integer."assert bx.calls == []def test_update_company_update_error(fake_bitrix, logged):fake_bitrix({"crm.company.update": server.BitrixError("NOT_FOUND: gone")})out = run(server.update_company("40", title="Harbor"))assert out == "Error updating company 40: NOT_FOUND: gone"assert logged == [("update_company", {"record_id": "40", "fields": ["TITLE"]}, "error: NOT_FOUND: gone")]# --------------------------------------------------------------------------- gates@pytest.mark.parametrize("call", [lambda: server.create_contact("Ana"),lambda: server.update_contact("12", name="Ana"),lambda: server.create_company("Harbor"),lambda: server.update_company("40", title="Harbor"),])def test_party_writes_respect_both_switches(call, fake_bitrix, logged, monkeypatch):bx = fake_bitrix({})monkeypatch.setattr(server, "WRITES_ENABLED", False)assert run(call()) == "Writes are disabled on this server (BITRIX_WRITES=off). No change was made."monkeypatch.setattr(server, "WRITES_ENABLED", True)monkeypatch.setattr(server, "PARTY_WRITES_ENABLED", False)assert run(call()) == ("Contact/company writes are disabled (BITRIX_WRITE_PARTIES=off). ""No change was made.")assert bx.calls == []