flexigo-support-bot / tests /test_onboarding_e2e.py
victor34593993's picture
feat: WhatsApp channel
977bd69 verified
Raw
History Blame
4 kB
"""End-to-end onboarding proof: a brand-new client, REAL extraction + REAL
embeddings (no index mock), answering only from ITS OWN uploaded document,
fully isolated from another client. This is the "can I onboard a real store
and trust it" guarantee.
Marked slow because it loads the embedding model.
"""
from __future__ import annotations
import shutil
from pathlib import Path
import pytest
from app.models import ChatSession
from app.tenancy import get_tenant_by_slug
from app.tools import knowledge_tool
from app.tools.registry import ToolContext
AUTH = {"Authorization": "Bearer test-admin-token"}
# Distinctive facts with rare tokens so retrieval + isolation are unambiguous.
CLIENT_A = {
"slug": "mochilas-trekmax",
"brand": "Mochilas TrekMax",
"file": ("garantia.md", b"La garantia de la mochila TrekMax Summit es de siete anos. "
b"El material es Cordura 1000D totalmente impermeable.", "text/markdown"),
"ask": "cuanto dura la garantia de la mochila summit y de que material es",
"must_have": "TrekMax",
"must_not_have": "GotaFina",
}
CLIENT_B = {
"slug": "riego-gotafina",
"brand": "Riego GotaFina",
"file": ("kit.md", b"El kit de riego GotaFina incluye 30 metros de manguera de "
b"poliuretano y boquillas de laton macizo.", "text/markdown"),
"ask": "que incluye el kit de riego y de que son las boquillas",
"must_not_have": "TrekMax",
"must_have": "GotaFina",
}
async def _onboard(client, db, c):
# 1) Create the tenant with its brand (what the admin form does).
created = await client.post(
"/admin/tenants", headers=AUTH,
json={"slug": c["slug"], "brand_name": c["brand"]},
)
assert created.status_code == 201, created.text
assert created.json()["brand_name"] == c["brand"]
assert created.json()["has_shopify_secret"] is False
# 2) Set its Shopify credentials — secret is stored encrypted, never echoed.
upd = await client.put(
f"/admin/tenants/{c['slug']}", headers=AUTH,
json={"shopify_shop": f"{c['slug']}.myshopify.com",
"shopify_client_id": "id123",
"shopify_client_secret": "shpss_super_secret"},
)
assert upd.status_code == 200
assert upd.json()["has_shopify_secret"] is True
# 3) Upload its real document — REAL extract -> chunk -> embed -> store.
up = await client.post(
f"/admin/tenants/{c['slug']}/sources/file", headers=AUTH,
files={"file": c["file"]},
)
assert up.status_code == 201, up.text
assert up.json()["status"] == "indexed", up.json()
assert up.json().get("error") in (None, "")
tenant = await get_tenant_by_slug(db, c["slug"])
return tenant.id
@pytest.mark.slow
async def test_full_client_onboarding_and_isolation(app_client, db_session):
_app, client = app_client
try:
a_id = await _onboard(client, db_session, CLIENT_A)
b_id = await _onboard(client, db_session, CLIENT_B)
async def ask(tenant_id, question):
ctx = ToolContext(
db=db_session,
session=ChatSession(shop="x", tenant_id=tenant_id),
tenant_id=tenant_id,
)
return (await knowledge_tool.run({"query": question}, ctx))["context"]
# Client A's bot answers from A's doc, and NEVER sees B's.
a_ctx = await ask(a_id, CLIENT_A["ask"])
assert CLIENT_A["must_have"] in a_ctx, a_ctx
assert CLIENT_A["must_not_have"] not in a_ctx, f"LEAK from other tenant: {a_ctx}"
# Client B's bot answers from B's doc, and NEVER sees A's.
b_ctx = await ask(b_id, CLIENT_B["ask"])
assert CLIENT_B["must_have"] in b_ctx, b_ctx
assert CLIENT_B["must_not_have"] not in b_ctx, f"LEAK from other tenant: {b_ctx}"
finally:
shutil.rmtree(Path("uploads") / CLIENT_A["slug"], ignore_errors=True)
shutil.rmtree(Path("uploads") / CLIENT_B["slug"], ignore_errors=True)