🧹 test: фикстуры без имён вендора и его продуктов (Acme, example.com)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
AndreyandClaude Fable 5.1 committed 2026-09-06 00:39:24 +03:00
1 parent a891947016
commit ed4393d22a
18 files changed
+84 -84

No files matched your search

@@ -25,7 +25,7 @@ class KnowledgeMetadataImportTests(TestCase):
)
self.app = create_category(
context=self.context,
name="FoxRay",
name="Acme",
parent=self.products,
)
self.client = TenantAPIClient()
@@ -53,16 +53,16 @@ class KnowledgeMetadataImportTests(TestCase):
response = self._import(
[
{
"title": "FoxRay support",
"title": "Acme support",
"description": "Support rules",
"categoryPath": ["Products", "FoxRay"],
"categoryPath": ["Products", "Acme"],
"content": "Procedure",
}
]
)
self.assertEqual(response.json()["created"], 1)
knowledge = Knowledge.objects.get(title="FoxRay support")
knowledge = Knowledge.objects.get(title="Acme support")
self.assertEqual(knowledge.category_id, self.app.id)
def test_unknown_category_path_fails_per_document(self) -> None:
@@ -93,7 +93,7 @@ class KnowledgeMetadataImportTests(TestCase):
[
{
"title": "Preserved",
"categoryPath": ["Products", "FoxRay"],
"categoryPath": ["Products", "Acme"],
"content": "Version one",
}
]
@@ -127,7 +127,7 @@ class KnowledgeMetadataImportTests(TestCase):
[
{
"title": "Metadata",
"categoryPath": ["Products", "FoxRay"],
"categoryPath": ["Products", "Acme"],
"content": "Stable content",
}
]
@@ -52,7 +52,7 @@ class PolicyInvariantMigrationTests(TestCase):
self.product = Product.objects.create(
organization=self.organization, code="app", name="FoxRay"
organization=self.organization, code="app", name="Acme"
)
@@ -54,7 +54,7 @@ EMAIL_CONFIG = {
def _raw(*, from_="Иван Петров <Ivan@example.com>", subject="Вопрос по FoxRay", text="Здравствуйте!", message_id="<m1@example.com>", html=None, attach=False) -> bytes:
def _raw(*, from_="Иван Петров <Ivan@example.com>", subject="Вопрос по Acme", text="Здравствуйте!", message_id="<m1@example.com>", html=None, attach=False) -> bytes:
message = MimeMessage()
@@ -140,7 +140,7 @@ class EmailNormalizeTests(TestCase):
self.assertEqual(inbound.text, "Здравствуйте!")
self.assertEqual(inbound.thread_meta, {"subject": "Вопрос по FoxRay", "last_message_id": "<m1@example.com>"})
self.assertEqual(inbound.thread_meta, {"subject": "Вопрос по Acme", "last_message_id": "<m1@example.com>"})
self.assertEqual(message_id, "<m1@example.com>")
@@ -162,7 +162,7 @@ class EmailNormalizeTests(TestCase):
inbound, _ = email_transport._normalize(
_parsed(_raw(html="<p>Добрый день!</p><p>Сколько стоит &laquo;FoxRay&raquo;?</p>")),
_parsed(_raw(html="<p>Добрый день!</p><p>Сколько стоит &laquo;Acme&raquo;?</p>")),
own_address="support@example.com",
@@ -174,7 +174,7 @@ class EmailNormalizeTests(TestCase):
self.assertIn("Добрый день!", inbound.text)
self.assertIn("«FoxRay»", inbound.text)
self.assertIn("«Acme»", inbound.text)
self.assertNotIn("<p>", inbound.text)
@@ -370,7 +370,7 @@ class EmailSendTests(TestCase):
self.organization = Organization.objects.get(slug="demo")
self.channel = Channel.objects.create(organization=self.organization, code="edevs", name="Edevs — главный сайт")
self.channel = Channel.objects.create(organization=self.organization, code="acme", name="Acme — главный сайт")
self.integration = _integration(organization=self.organization, channel=self.channel)
@@ -390,7 +390,7 @@ class EmailSendTests(TestCase):
external_chat_id="ivan@example.com",
transport_meta={"subject": "Вопрос по FoxRay", "last_message_id": "<m1@example.com>"},
transport_meta={"subject": "Вопрос по Acme", "last_message_id": "<m1@example.com>"},
)
@@ -412,7 +412,7 @@ class EmailSendTests(TestCase):
self.assertEqual(outgoing["To"], "ivan@example.com")
self.assertEqual(outgoing["Subject"], "Re: Вопрос по FoxRay")
self.assertEqual(outgoing["Subject"], "Re: Вопрос по Acme")
self.assertEqual(outgoing["In-Reply-To"], "<m1@example.com>")
@@ -464,7 +464,7 @@ class EmailIngestThreadMetaTests(TestCase):
self.organization = Organization.objects.get(slug="demo")
self.channel = Channel.objects.create(organization=self.organization, code="edevs", name="Edevs — главный сайт")
self.channel = Channel.objects.create(organization=self.organization, code="acme", name="Acme — главный сайт")
self.integration = _integration(organization=self.organization, channel=self.channel)
@@ -510,13 +510,13 @@ class EmailIngestThreadMetaTests(TestCase):
def test_subject_pinned_to_first_message_id_follows_last(self) -> None:
self._ingest(external_id="<m1@example.com>", subject="Вопрос по FoxRay", message_id="<m1@example.com>")
self._ingest(external_id="<m1@example.com>", subject="Вопрос по Acme", message_id="<m1@example.com>")
self._ingest(external_id="<m2@example.com>", subject="Re: Вопрос по FoxRay", message_id="<m2@example.com>")
self._ingest(external_id="<m2@example.com>", subject="Re: Вопрос по Acme", message_id="<m2@example.com>")
conversation = Conversation.objects.get(channel=self.channel)
self.assertEqual(conversation.transport_meta["subject"], "Вопрос по FoxRay")
self.assertEqual(conversation.transport_meta["subject"], "Вопрос по Acme")
self.assertEqual(conversation.transport_meta["last_message_id"], "<m2@example.com>")
@@ -52,10 +52,10 @@ class IngestLimitHandlingTests(TestCase):
def setUp(self) -> None:
bootstrap_owner(email="owner@example.com", password="temporary-password")
self.organization = Organization.objects.get(slug="demo")
self.channel = Channel.objects.create(organization=self.organization, code="app-sales", name="FoxRay — продажи")
self.channel = Channel.objects.create(organization=self.organization, code="app-sales", name="Acme — продажи")
AIAgent.objects.create(
channel=self.channel,
name="FoxRay Agent",
name="Acme Agent",
model="openai/gpt-4o-mini",
status=AIAgentStatus.ACTIVE,
)
@@ -179,10 +179,10 @@ class ContactShareIngestTests(TestCase):
def setUp(self) -> None:
bootstrap_owner(email="owner@example.com", password="temporary-password")
self.organization = Organization.objects.get(slug="demo")
self.channel = Channel.objects.create(organization=self.organization, code="app-sales", name="FoxRay — продажи")
self.channel = Channel.objects.create(organization=self.organization, code="app-sales", name="Acme — продажи")
AIAgent.objects.create(
channel=self.channel,
name="FoxRay Agent",
name="Acme Agent",
model="openai/gpt-4o-mini",
status=AIAgentStatus.ACTIVE,
)
@@ -233,7 +233,7 @@ class RequestContactApiTests(TestCase):
def setUp(self) -> None:
bootstrap_owner(email="owner@example.com", password="temporary-password")
self.organization = Organization.objects.get(slug="demo")
self.channel = Channel.objects.create(organization=self.organization, code="app-sales", name="FoxRay — продажи")
self.channel = Channel.objects.create(organization=self.organization, code="app-sales", name="Acme — продажи")
self.integration = _messenger_connection(self.channel)
self.contact = Contact.objects.create(organization=self.organization, name="Иван")
ConnectionIdentity.objects.create(
@@ -281,7 +281,7 @@ class ConversationReadTests(TestCase):
def setUp(self) -> None:
bootstrap_owner(email="owner@example.com", password="temporary-password")
self.organization = Organization.objects.get(slug="demo")
self.channel = Channel.objects.create(organization=self.organization, code="app-sales", name="FoxRay — продажи")
self.channel = Channel.objects.create(organization=self.organization, code="app-sales", name="Acme — продажи")
self.integration = _messenger_connection(self.channel)
contact = Contact.objects.create(organization=self.organization, name="Иван")
self.conversation = Conversation.objects.create(
@@ -348,7 +348,7 @@ class WebchatContactTests(TestCase):
def setUp(self) -> None:
bootstrap_owner(email="owner@example.com", password="temporary-password")
self.organization = Organization.objects.get(slug="demo")
self.channel = Channel.objects.create(organization=self.organization, code="edevs-web", name="Веб-чат")
self.channel = Channel.objects.create(organization=self.organization, code="demo-web", name="Веб-чат")
AIAgent.objects.create(
channel=self.channel,
name="Web Agent",
+1 -1
View File
@@ -74,7 +74,7 @@ class WebIntegrationCheckTests(TestCase):
self.channel = Channel.objects.create(organization=self.organization, code="edevs", name="Edevs — главный сайт")
self.channel = Channel.objects.create(organization=self.organization, code="acme", name="Acme — главный сайт")
@@ -16,7 +16,7 @@ from chatballs.notifications.services import notify
from chatballs.notifications.selectors import visible_for
def _notifier(organization, provider=IntegrationProvider.TELEGRAM, username="edevs_notify_bot"):
def _notifier(organization, provider=IntegrationProvider.TELEGRAM, username="chatballs_notify_bot"):
return Integration.objects.create(
organization=organization,
kind=IntegrationKind.MESSENGER,
@@ -39,9 +39,9 @@ class NotifierTestBase(TestCase):
class BindingTests(NotifierTestBase):
def test_deep_link_for_telegram_and_max(self) -> None:
code = issue_binding_code(context=self.context, integration=self.integration)
self.assertEqual(deep_link(self.integration, code.code), f"https://t.me/edevs_notify_bot?start={code.code}")
max_bot = _notifier(self.organization, provider=IntegrationProvider.MAX, username="edevs_max_bot")
self.assertEqual(deep_link(max_bot, "abc"), "https://max.ru/edevs_max_bot?start=abc")
self.assertEqual(deep_link(self.integration, code.code), f"https://t.me/chatballs_notify_bot?start={code.code}")
max_bot = _notifier(self.organization, provider=IntegrationProvider.MAX, username="chatballs_max_bot")
self.assertEqual(deep_link(max_bot, "abc"), "https://max.ru/chatballs_max_bot?start=abc")
def test_start_code_creates_binding_and_confirms(self) -> None:
code = issue_binding_code(context=self.context, integration=self.integration)
@@ -63,7 +63,7 @@ class BindingTests(NotifierTestBase):
def test_binding_is_exclusive_across_messengers(self) -> None:
# Уведомления идут в один мессенджер: привязка MAX заменяет привязку TG.
MessengerBinding.objects.create(user=self.owner, integration=self.integration, external_chat_id="111")
max_bot = _notifier(self.organization, provider=IntegrationProvider.MAX, username="edevs_max_bot")
max_bot = _notifier(self.organization, provider=IntegrationProvider.MAX, username="chatballs_max_bot")
code = issue_binding_code(context=self.context, integration=max_bot)
inbound = InboundMessage(external_id="3", user_id="9", chat_id="9", text=f"/start {code.code}", display_name="Андрей")
with mock.patch("chatballs.notifications.binding.transports.send_reply", return_value=True):
@@ -93,7 +93,7 @@ class DeliveryTests(NotifierTestBase):
context=self.context,
type=NotificationType.DIALOG_WAITING,
audience=NotificationAudience.OPERATORS,
title="Новый диалог · Edevs — сайт",
title="Новый диалог · Acme — сайт",
body="Гость · TELEGRAM: Привет",
)
self._dispatch_last_event()
@@ -156,7 +156,7 @@ class PollerSelectionTests(NotifierTestBase):
from chatballs.channels.models import Channel
from chatballs.conversations import poller
channel = Channel.objects.create(organization=self.organization, code="app-sales", name="FoxRay — продажи")
channel = Channel.objects.create(organization=self.organization, code="app-sales", name="Acme — продажи")
client_bot = Integration.objects.create(
organization=self.organization, kind=IntegrationKind.MESSENGER,
provider=IntegrationProvider.TELEGRAM, name="client-bot", secret="token", channel=channel,
@@ -227,7 +227,7 @@ class BindingApiTests(NotifierTestBase):
issued = self.client.post(f"/api/v1/notifications/messenger-bindings/{self.integration.id}/")
self.assertEqual(issued.status_code, 201)
payload = issued.json()
self.assertTrue(payload["deepLink"].startswith("https://t.me/edevs_notify_bot?start="))
self.assertTrue(payload["deepLink"].startswith("https://t.me/chatballs_notify_bot?start="))
MessengerBinding.objects.create(user=self.owner, integration=self.integration, external_chat_id="777")
self.assertTrue(self.client.get("/api/v1/notifications/messenger-bindings/").json()["items"][0]["bound"])
+1 -1
View File
@@ -22,7 +22,7 @@ class ProductApiTests(TestCase):
{
"code": "academy",
"name": "Academy",
"siteUrl": "https://academy.edevs.tech",
"siteUrl": "https://academy.example.com",
}
),
content_type="application/json",
@@ -47,8 +47,8 @@ def make_support_token(
return f"{header_b64}.{payload_b64}.{signature_b64}"
# Пример payload FoxRay (SPEC-HUB-0011 §4.1 / §5.2).
FOXRAY_DATA = {
# Пример payload Acme (SPEC-HUB-0011 §4.1 / §5.2).
ACME_DATA = {
"doctor": {"id": "u_456", "name": "Иван Петров", "email": "doctor@example.com"},
"clinic": {"id": "c_123", "name": "Клиника Альфа"},
"subscription": {"tariff": "pro", "status": "active"},
@@ -12,7 +12,7 @@ from chatballs.identity.bootstrap import bootstrap_owner
from chatballs.identity.models import Organization
from chatballs.products.models import Product
from chatballs.support.models import ContractStatus, ProductSupportContract
from chatballs.support.test_helpers import FOXRAY_DATA, make_support_token
from chatballs.support.test_helpers import ACME_DATA, make_support_token
from chatballs.webchat.testing import create_web_widget
SECRET = "test-support-secret-very-long-32bytes!!"
@@ -57,7 +57,7 @@ def _setup_support_channel(
channel = Channel.objects.create(
organization=organization,
code="app-support",
name="FoxRay — поддержка",
name="Acme — поддержка",
product=product,
requires_authenticated_product_identity=True,
allow_anonymous_sessions=False,
@@ -77,11 +77,11 @@ class SupportWidgetMessagesTests(TestCase):
self.channel, self.contract = _setup_support_channel(
self.organization, self.product
)
self.widget = create_web_widget(self.channel, name="FoxRay support widget")
self.widget = create_web_widget(self.channel, name="Acme support widget")
self.client = APIClient()
def _start_session(self) -> dict:
token = make_support_token(secret=SECRET, data=FOXRAY_DATA)
token = make_support_token(secret=SECRET, data=ACME_DATA)
response = self.client.post(
"/api/v1/support/sessions/",
data=json.dumps({"widgetKey": self.widget.public_key, "token": token}),
+13 -13
View File
@@ -15,7 +15,7 @@ from chatballs.support.models import (
ProductSupportContract,
SupportIdentitySnapshot,
)
from chatballs.support.test_helpers import FOXRAY_DATA, make_support_token
from chatballs.support.test_helpers import ACME_DATA, make_support_token
from chatballs.webchat.models import WebChatWidgetMode
from chatballs.webchat.testing import create_web_widget
@@ -87,7 +87,7 @@ class SupportSessionTests(TestCase):
self.channel = Channel.objects.create(
organization=self.organization,
code="app-support",
name="FoxRay — поддержка",
name="Acme — поддержка",
product=self.product,
requires_authenticated_product_identity=True,
allow_anonymous_sessions=False,
@@ -96,7 +96,7 @@ class SupportSessionTests(TestCase):
allow_checkout_actions=False,
)
self.contract.allowed_channels.add(self.channel)
self.widget = create_web_widget(self.channel, name="FoxRay support widget")
self.widget = create_web_widget(self.channel, name="Acme support widget")
self.client = APIClient()
def _start(self, token: str, widget_key: str | None = None):
@@ -107,7 +107,7 @@ class SupportSessionTests(TestCase):
)
def test_happy_path_creates_snapshot_and_conversation(self) -> None:
token = make_support_token(secret=SECRET, data=FOXRAY_DATA)
token = make_support_token(secret=SECRET, data=ACME_DATA)
response = self._start(token)
self.assertEqual(response.status_code, 201, response.content)
body = response.json()
@@ -133,14 +133,14 @@ class SupportSessionTests(TestCase):
)
def test_invalid_signature_denied(self) -> None:
token = make_support_token(secret="wrong-secret", data=FOXRAY_DATA)
token = make_support_token(secret="wrong-secret", data=ACME_DATA)
response = self._start(token)
self.assertEqual(response.status_code, 422)
self.assertEqual(response.json()["error"], "support_unavailable")
self._assert_denied_audit("TOKEN_SIGNATURE_INVALID")
def test_expired_token_denied(self) -> None:
token = make_support_token(secret=SECRET, data=FOXRAY_DATA, exp_delta=-100)
token = make_support_token(secret=SECRET, data=ACME_DATA, exp_delta=-100)
response = self._start(token)
self.assertEqual(response.status_code, 422)
self._assert_denied_audit("TOKEN_EXPIRED")
@@ -164,7 +164,7 @@ class SupportSessionTests(TestCase):
def test_disabled_contract_denied(self) -> None:
self.contract.status = ContractStatus.DISABLED
self.contract.save(update_fields=["status"])
token = make_support_token(secret=SECRET, data=FOXRAY_DATA)
token = make_support_token(secret=SECRET, data=ACME_DATA)
response = self._start(token)
self.assertEqual(response.status_code, 422)
self._assert_denied_audit("CONTRACT_DISABLED")
@@ -174,21 +174,21 @@ class SupportSessionTests(TestCase):
# принимать support-токен.
sales_channel = Channel.objects.create(
organization=self.organization, code="app-sales-x",
name="FoxRay sales", product=self.product,
name="Acme sales", product=self.product,
)
invalid_widget = create_web_widget(
sales_channel,
name="Invalid support widget",
mode=WebChatWidgetMode.AUTHENTICATED_PRODUCT,
)
token = make_support_token(secret=SECRET, data=FOXRAY_DATA)
token = make_support_token(secret=SECRET, data=ACME_DATA)
response = self._start(token, widget_key=invalid_widget.public_key)
self.assertEqual(response.status_code, 422)
self._assert_denied_audit("CHANNEL_NOT_SUPPORT")
def test_channel_product_mismatch_denied(self) -> None:
# iss=site, но канал привязан к app.
token = make_support_token(secret=SECRET, iss="site", data=FOXRAY_DATA)
token = make_support_token(secret=SECRET, iss="site", data=ACME_DATA)
response = self._start(token)
self.assertEqual(response.status_code, 422)
self._assert_denied_audit("CHANNEL_PRODUCT_MISMATCH")
@@ -203,18 +203,18 @@ class SupportSessionTests(TestCase):
self.assertEqual(response.json()["error"], "support_unavailable")
def test_continue_session_reuses_open_conversation(self) -> None:
token1 = make_support_token(secret=SECRET, data=FOXRAY_DATA, jti="jti-1")
token1 = make_support_token(secret=SECRET, data=ACME_DATA, jti="jti-1")
first = self._start(token1)
self.assertEqual(first.status_code, 201)
conv_id = first.json()["conversation"]["id"]
# Повторный старт тем же subject → тот же открытый диалог.
token2 = make_support_token(secret=SECRET, data=FOXRAY_DATA, jti="jti-2")
token2 = make_support_token(secret=SECRET, data=ACME_DATA, jti="jti-2")
second = self._start(token2)
self.assertEqual(second.status_code, 201)
self.assertEqual(second.json()["conversation"]["id"], conv_id)
def test_raw_token_not_in_audit(self) -> None:
token = make_support_token(secret="wrong-secret", data=FOXRAY_DATA, jti="secret-jti-xyz")
token = make_support_token(secret="wrong-secret", data=ACME_DATA, jti="secret-jti-xyz")
self._start(token)
denied = AuditEvent.objects.filter(action="support.session_denied").first()
self.assertIsNotNone(denied)
@@ -21,13 +21,13 @@ class SupportPortalManagementTests(SupportPortalTestCase):
widget_channel = Channel.objects.create(
organization=self.organization,
code="app-portal-chat",
name="FoxRay — чат портала",
name="Acme — чат портала",
product=self.product,
requires_authenticated_product_identity=False,
allow_anonymous_sessions=True,
allow_self_reported_contact=True,
)
widget = create_web_widget(widget_channel, name="FoxRay portal widget")
widget = create_web_widget(widget_channel, name="Acme portal widget")
options = self.client.get(
f"/api/v1/support/portals/{portal_id}/support-channels/"
@@ -18,19 +18,19 @@ class PublicSupportPortalTests(TestCase):
self.client.force_authenticate(result.owner)
portal = self.client.post(
"/api/v1/support/portals/",
{"slug": "edevs-help", "name": "Edevs Help"},
{"slug": "demo-help", "name": "Acme Help"},
format="json",
).json()["portal"]
self.portal_id = portal["id"]
self.portal_host = portal["hostedDomain"]
widget_channel = Channel.objects.create(
organization=result.organization,
code="edevs-help-chat",
name="Edevs Help — чат",
code="demo-help-chat",
name="Acme Help — чат",
requires_authenticated_product_identity=False,
allow_anonymous_sessions=True,
)
self.widget = create_web_widget(widget_channel, name="Edevs Help widget")
self.widget = create_web_widget(widget_channel, name="Acme Help widget")
self.client.patch(
f"/api/v1/support/portals/{self.portal_id}/",
{"widgetId": self.widget.id},
@@ -85,7 +85,7 @@ class PublicSupportPortalTests(TestCase):
portal = self.client.get("/api/v1/help/", HTTP_HOST=self.portal_host)
self.assertEqual(portal.status_code, 200, portal.content)
self.assertEqual(portal.json()["portal"]["name"], "Edevs Help")
self.assertEqual(portal.json()["portal"]["name"], "Acme Help")
self.assertEqual(
portal.json()["portal"]["webWidgetKey"],
self.widget.public_key,
@@ -146,7 +146,7 @@ class PublicSupportPortalTests(TestCase):
)
self.assertEqual(response.status_code, 200, response.content)
self.assertEqual(response.json()["portal"]["name"], "Edevs Help")
self.assertEqual(response.json()["portal"]["name"], "Acme Help")
@override_settings(ROOT_URLCONF="chatballs_backend.urls_platform")
def test_gateway_authorizes_only_published_portal_domains(self) -> None:
@@ -48,7 +48,7 @@ describe("knowledge API", () => {
await fetchKnowledgeList({
category: 7,
isEnabled: false,
search: "тариф FoxRay",
search: "тариф Acme",
});
const url = new URL(fetchMock.mock.calls[0][0], "https://app.example");
@@ -58,7 +58,7 @@ describe("knowledge API", () => {
expect(Object.fromEntries(url.searchParams)).toEqual({
category: "7",
isEnabled: "false",
search: "тариф FoxRay",
search: "тариф Acme",
});
});
@@ -66,8 +66,8 @@ describe("knowledge API", () => {
const fetchMock = mockSuccess({ items: [] });
await fetchKnowledgeCategories();
await createKnowledgeCategory({ name: "FoxRay", parentId: 3, sortOrder: 20 });
await updateKnowledgeCategory(7, { name: "FoxRay Pro", parentId: null, sortOrder: 30 });
await createKnowledgeCategory({ name: "Acme", parentId: 3, sortOrder: 20 });
await updateKnowledgeCategory(7, { name: "Acme Pro", parentId: null, sortOrder: 30 });
expect(fetchMock.mock.calls.map(([path]) => path)).toEqual([
`/api/v1/organizations/${organizationPublicId}/ai/knowledge/categories/`,
@@ -75,12 +75,12 @@ describe("knowledge API", () => {
`/api/v1/organizations/${organizationPublicId}/ai/knowledge/categories/7/`,
]);
expect(JSON.parse(fetchMock.mock.calls[1][1].body)).toEqual({
name: "FoxRay",
name: "Acme",
parentId: 3,
sortOrder: 20,
});
expect(JSON.parse(fetchMock.mock.calls[2][1].body)).toEqual({
name: "FoxRay Pro",
name: "Acme Pro",
parentId: null,
sortOrder: 30,
});
@@ -15,7 +15,7 @@ const categories: KnowledgeCategory[] = [
{ id: 1, name: "Без категории", parentId: null, sortOrder: 0, isSystem: true, knowledgeCount: 4 },
{ id: 3, name: "Продажи", parentId: null, sortOrder: 20, isSystem: false, knowledgeCount: 9 },
{ id: 2, name: "Продукты", parentId: null, sortOrder: 10, isSystem: false, knowledgeCount: 14 },
{ id: 5, name: "FoxRay", parentId: 2, sortOrder: 10, isSystem: false, knowledgeCount: 8 },
{ id: 5, name: "Acme", parentId: 2, sortOrder: 10, isSystem: false, knowledgeCount: 8 },
];
describe("knowledge category tree", () => {
@@ -18,10 +18,10 @@ describe("splitQuotedEmail", () => {
});
it("separates an inline localized reply header", () => {
const text = "Держись\nСр, 22 июля 2026 г. в 21:10, Foxray <foxray@example.com>: > Старое письмо";
const text = "Держись\nСр, 22 июля 2026 г. в 21:10, Acme <acme@example.com>: > Старое письмо";
expect(splitQuotedEmail(text)).toEqual({
latest: "Держись",
quoted: "Ср, 22 июля 2026 г. в 21:10, Foxray <foxray@example.com>: > Старое письмо",
quoted: "Ср, 22 июля 2026 г. в 21:10, Acme <acme@example.com>: > Старое письмо",
});
});
});
@@ -5,20 +5,20 @@ import { supportHref } from "./SupportLauncher";
describe("supportHref", () => {
it("passes the selected support channel to the product", () => {
expect(supportHref({
code: "foxray",
name: "Foxray",
siteUrl: "https://foxray.example/account?from=help",
code: "acme",
name: "Acme",
siteUrl: "https://acme.example/account?from=help",
supportAvailable: true,
supportWidgetKey: "wgt_foxray_support",
supportChannelCode: "foxray-support",
})).toBe("https://foxray.example/account?from=help&chatballsSupportWidget=wgt_foxray_support");
supportWidgetKey: "wgt_acme_support",
supportChannelCode: "acme-support",
})).toBe("https://acme.example/account?from=help&chatballsSupportWidget=wgt_acme_support");
});
it("does not build an unauthenticated fallback without a channel", () => {
expect(supportHref({
code: "foxray",
name: "Foxray",
siteUrl: "https://foxray.example",
code: "acme",
name: "Acme",
siteUrl: "https://acme.example",
supportAvailable: false,
supportChannelCode: null,
supportWidgetKey: null,
@@ -22,8 +22,8 @@ describe("webWidgetSnippet", () => {
vi.stubGlobal("window", { location: { origin: "https://acme.test" } });
const { webWidgetSnippet } = await import("./model");
expect(webWidgetSnippet("wgt_foxray")).toBe(
`<script src="https://acme.test/chat-widget.js" data-widget-key="wgt_foxray" async></script>`,
expect(webWidgetSnippet("wgt_acme")).toBe(
`<script src="https://acme.test/chat-widget.js" data-widget-key="wgt_acme" async></script>`,
);
});
+2 -2
View File
@@ -14,8 +14,8 @@ const OWNER = {
fullName: "Владелец",
role: "OWNER",
positionTitle: "Владелец",
organization: "edevs",
organizationName: "Edevs",
organization: "acme",
organizationName: "Acme",
department: null,
mustChangePassword: false,
totpRequired: false,