flexigo-support-bot / tests /test_personalization_isolation.py
victor34593993's picture
kill-switch guarantees + billing hardening
fb60961 verified
Raw
History Blame
3.97 kB
"""Every per-tenant personalization must be UNIQUE to its account and never
bleed into another tenant's widget/portal. Two fully-distinct stores are set up
and we assert each surface returns ONLY its own values."""
from __future__ import annotations
AUTH = {"Authorization": "Bearer test-admin-token"}
ALPHA = {
"brand_name": "Alpha Store", "assistant_name": "Ana", "brand_color": "#112233",
"welcome": "Bienvenido a Alpha", "avatar_url": "https://img/alpha.png",
"widget_mode": "both", "whatsapp_number": "+34111111111",
"support_email": "owner@alpha.com",
"starters": ["Comprar Alpha", "Pedido Alpha"],
"shopify_shop": "alpha.myshopify.com", "shopify_client_id": "alpha-id",
"shopify_client_secret": "alpha-secret",
}
BETA = {
"brand_name": "Beta Servicios", "assistant_name": "Bruno", "brand_color": "#998877",
"welcome": "Hola, soy Beta", "avatar_url": "https://img/beta.png",
"widget_mode": "chat", "whatsapp_number": "+34222222222",
"support_email": "owner@beta.com",
"starters": ["Pedir presupuesto Beta"],
}
async def _make(client, slug, cfg):
await client.post("/admin/tenants", headers=AUTH, json={"slug": slug})
tok = (await client.post(f"/admin/tenants/{slug}/dashboard-token", headers=AUTH)).json()[
"dashboard_token"
]
r = await client.put("/portal/api/config", headers={"Authorization": "Bearer " + tok}, json=cfg)
assert r.status_code == 200, r.text
return tok
async def test_every_personalization_is_unique_per_account(app_client):
_app, client = app_client
a_tok = await _make(client, "alpha", ALPHA)
b_tok = await _make(client, "beta", BETA)
# ── widget-config (what the public widget reads) returns ONLY own values ──
wa = (await client.get("/widget-config?t=alpha")).json()
wb = (await client.get("/widget-config?t=beta")).json()
for field in ("brand_name", "assistant_name", "brand_color", "welcome",
"avatar_url", "widget_mode", "whatsapp_number"):
assert wa[field] == ALPHA[field], field
assert wb[field] == BETA[field], field
assert wa[field] != wb[field], f"{field} bled between tenants"
assert wa["starters"] == ALPHA["starters"]
assert wb["starters"] == BETA["starters"]
# Alpha's values must NOT appear in Beta's config and vice-versa
assert "Alpha" not in str(wb) and "Ana" not in str(wb)
assert "Beta" not in str(wa) and "Bruno" not in str(wa)
# ── portal config (each owner with their own token) returns ONLY own ──
ca = (await client.get("/portal/api/config", headers={"Authorization": "Bearer " + a_tok})).json()
cb = (await client.get("/portal/api/config", headers={"Authorization": "Bearer " + b_tok})).json()
assert ca["slug"] == "alpha" and cb["slug"] == "beta"
assert ca["support_email"] == "owner@alpha.com" and cb["support_email"] == "owner@beta.com"
assert ca["shopify_connected"] is True # alpha set Shopify creds
assert cb["shopify_connected"] is False # beta did not
assert ca["shopify_shop"] == "alpha.myshopify.com"
assert cb["shopify_shop"] in ("", None)
# secrets never leak in either direction
assert "alpha-secret" not in str(ca) and "alpha-secret" not in str(cb)
# ── a token only ever reads ITS OWN config (cross-token gives the other's own) ──
cross = (await client.get("/portal/api/config", headers={"Authorization": "Bearer " + b_tok})).json()
assert cross["brand_name"] == "Beta Servicios" # b_tok can't see alpha
async def test_widget_config_unknown_tenant_is_disabled_generic(app_client):
"""An unknown slug never leaks another tenant's branding AND is disabled
(no fallback to default: a deleted client must get zero service)."""
_app, client = app_client
wc = (await client.get("/widget-config?t=does-not-exist")).json()
assert wc["enabled"] is False
assert wc["brand_name"] == "Asistente" and wc["starters"] == []