Files
replypier/backend/tests/services/test_contact_capture.py
WangShen007 164f5f9615 test: 补充权限、消息与会话服务回归用例
会话读取、接管、重分配与 Socket 订阅统一校验角色、组织与可见性。
2026-10-08 22:35:00 +08:00

146 lines
5.7 KiB
Python

"""
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Handoff contact capture: which fields the form asks for, and what happens to an
address the customer row is not allowed to take.
"""
import pytest
from app.models.customer import Customer
from app.repositories.customer import CustomerRepository
from app.services.contact_capture import (
CONTACT_EMAIL_META_KEY,
build_handoff_contact_form,
retain_unstored_email,
)
def _anon(db, org_id, email="1786455337798@noemail.com"):
c = Customer(organization_id=org_id, email=email)
db.add(c)
db.commit()
db.refresh(c)
return c
# ---------- which fields the form asks for ----------
@pytest.mark.parametrize("email,name,expected", [
(None, None, ["email", "name"]), # nothing known
("abc@noemail.com", None, ["email", "name"]), # anonymous placeholder
("123@telegram.channel", None, ["email", "name"]), # synthesized channel key
("abc@noemail.com", "Arun", ["email"]), # name already known
("real@acme.com", None, ["name"]), # address already known
])
def test_form_asks_only_for_what_is_missing(email, name, expected):
from types import SimpleNamespace
form = build_handoff_contact_form(
SimpleNamespace(email=email, full_name=name), collect_email=True, collect_name=True
)
assert [f["name"] for f in form["fields"]] == expected
def test_no_form_when_nothing_is_missing():
from types import SimpleNamespace
assert build_handoff_contact_form(
SimpleNamespace(email="real@acme.com", full_name="Arun"), True, True
) is None
def test_no_form_when_both_toggles_are_off():
from types import SimpleNamespace
assert build_handoff_contact_form(
SimpleNamespace(email=None, full_name=None), collect_email=False, collect_name=False
) is None
# ---------- an address the customer row cannot take ----------
def test_email_owned_by_another_customer_is_kept_on_meta_data(db, test_organization):
"""The reported bug. A returning anonymous visitor (or a shared team inbox)
submits an address another customer in the org already owns; update_contact
refuses it for the unique constraint, and it used to be dropped on the floor
— the visitor asked to be contacted and nothing recorded how."""
_anon(db, test_organization.id, email="taken@acme.com")
visitor = _anon(db, test_organization.id)
repo = CustomerRepository(db)
result = repo.update_contact(visitor.id, email="taken@acme.com", full_name=None)
assert result["email_updated"] is False # constraint held, as designed
assert retain_unstored_email(repo, visitor.id, "taken@acme.com", result) is True
db.refresh(visitor)
assert visitor.meta_data[CONTACT_EMAIL_META_KEY] == "taken@acme.com"
def test_name_only_submission_still_retains_the_dropped_email(db, test_organization):
"""Submitting a name too made the panel visibly change, which is what made
this look name-dependent — the email was being lost either way."""
_anon(db, test_organization.id, email="taken2@acme.com")
visitor = _anon(db, test_organization.id)
repo = CustomerRepository(db)
result = repo.update_contact(visitor.id, email="taken2@acme.com", full_name="Runix")
assert result["email_updated"] is False
assert result["name_updated"] is True
assert retain_unstored_email(repo, visitor.id, "taken2@acme.com", result) is True
db.refresh(visitor)
assert visitor.full_name == "Runix"
assert visitor.meta_data[CONTACT_EMAIL_META_KEY] == "taken2@acme.com"
def test_a_normal_capture_writes_no_metadata(db, test_organization):
"""The address landed on the customer, so there is nothing to retain and the
panel shouldn't grow a duplicate line."""
visitor = _anon(db, test_organization.id)
repo = CustomerRepository(db)
result = repo.update_contact(visitor.id, email="fresh@acme.com", full_name=None)
assert result["email_updated"] is True
assert retain_unstored_email(repo, visitor.id, "fresh@acme.com", result) is False
db.refresh(visitor)
assert not (visitor.meta_data or {}).get(CONTACT_EMAIL_META_KEY)
def test_resubmitting_the_address_already_on_file_is_a_no_op(db, test_organization):
visitor = _anon(db, test_organization.id, email="mine@acme.com")
repo = CustomerRepository(db)
result = repo.update_contact(visitor.id, email="mine@acme.com", full_name=None)
assert retain_unstored_email(repo, visitor.id, "mine@acme.com", result) is False
db.refresh(visitor)
assert not (visitor.meta_data or {}).get(CONTACT_EMAIL_META_KEY)
def test_retaining_preserves_existing_meta_data(db, test_organization):
"""Integrator-supplied custom_data must survive — update_meta_data merges."""
_anon(db, test_organization.id, email="taken3@acme.com")
visitor = _anon(db, test_organization.id)
visitor.meta_data = {"student_name": "Priya"}
db.commit()
repo = CustomerRepository(db)
result = repo.update_contact(visitor.id, email="taken3@acme.com", full_name=None)
retain_unstored_email(repo, visitor.id, "taken3@acme.com", result)
db.refresh(visitor)
assert visitor.meta_data["student_name"] == "Priya"
assert visitor.meta_data[CONTACT_EMAIL_META_KEY] == "taken3@acme.com"