Files
contexta_be/tests/test_config_plans.py
belviskhoremk 92d4c2fc5e feat: add appointments, campaigns, admin, storage, tests and various updates
- Add new routers: admin, appointments, campaigns
- Add storage service and logging config
- Add migrations directory and test suite with pytest config
- Add supabase_migration_features.sql
- Update models, dependencies, config, and existing routers
- Remove whatsapp_service (deleted)
- Update pyproject.toml and uv.lock dependencies

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 09:11:58 +00:00

286 lines
10 KiB
Python

"""
Tests for PLAN_LIMITS in config.py.
Ensures each plan has the correct feature gates and that the pricing
tiers are properly differentiated.
"""
import pytest
from app.config import PLAN_LIMITS, MODEL_CATALOG, DEFAULT_MODELS, MODEL_PROVIDERS
class TestPlanStructure:
"""Every plan must have all required keys."""
REQUIRED_KEYS = {
"max_chatbots", "max_published", "max_documents_per_chatbot",
"max_document_size_mb", "models", "conversations_limit",
"code_export", "analytics", "gap_suggestions", "channels",
"url_sources", "leads_per_month", "inbox_replies", "leads_editing",
"show_branding", "appointments", "appointments_chatbots",
"campaigns", "campaigns_per_month", "max_campaign_recipients",
}
@pytest.mark.parametrize("plan", ["free", "starter", "business", "agency", "enterprise"])
def test_all_required_keys_present(self, plan):
config = PLAN_LIMITS[plan]
missing = self.REQUIRED_KEYS - set(config.keys())
assert not missing, f"Plan '{plan}' missing keys: {missing}"
@pytest.mark.parametrize("plan", ["free", "starter", "business", "agency", "enterprise"])
def test_models_list_is_non_empty(self, plan):
models = PLAN_LIMITS[plan]["models"]
assert len(models) > 0
@pytest.mark.parametrize("plan", ["free", "starter", "business", "agency", "enterprise"])
def test_default_model_exists_for_plan(self, plan):
default = DEFAULT_MODELS.get(plan)
assert default is not None, f"No default model for {plan}"
class TestFreePlanRestrictions:
def setup_method(self):
self.plan = PLAN_LIMITS["free"]
def test_max_published_is_one(self):
assert self.plan["max_published"] == 1
def test_no_inbox_replies(self):
assert self.plan["inbox_replies"] is False
def test_no_leads_editing(self):
assert self.plan["leads_editing"] is False
def test_no_appointments(self):
assert self.plan["appointments"] is False
assert self.plan["appointments_chatbots"] == 0
def test_no_campaigns(self):
assert self.plan["campaigns"] is False
assert self.plan["campaigns_per_month"] == 0
assert self.plan["max_campaign_recipients"] == 0
def test_show_branding(self):
assert self.plan["show_branding"] is True
def test_no_analytics(self):
assert self.plan["analytics"] is False
def test_no_gap_suggestions(self):
assert self.plan["gap_suggestions"] is False
def test_no_channels(self):
assert self.plan["channels"] == []
def test_no_url_sources(self):
assert self.plan["url_sources"] == 0
def test_no_leads(self):
assert self.plan["leads_per_month"] == 0
def test_only_free_model(self):
models = self.plan["models"]
assert len(models) == 1
assert "llama" in models[0].lower()
class TestStarterPlan:
def setup_method(self):
self.plan = PLAN_LIMITS["starter"]
def test_max_published_is_three(self):
assert self.plan["max_published"] == 3
def test_has_inbox_replies(self):
assert self.plan["inbox_replies"] is True
def test_has_leads_editing(self):
assert self.plan["leads_editing"] is True
def test_has_appointments(self):
assert self.plan["appointments"] is True
def test_appointments_limited_to_one_chatbot(self):
assert self.plan["appointments_chatbots"] == 1
def test_has_campaigns(self):
assert self.plan["campaigns"] is True
def test_campaigns_limited_per_month(self):
assert 0 < self.plan["campaigns_per_month"] < 999999
def test_campaign_recipients_limited(self):
assert 0 < self.plan["max_campaign_recipients"] < 999999
def test_show_branding(self):
# Starter still shows branding
assert self.plan["show_branding"] is True
def test_has_analytics(self):
assert self.plan["analytics"] is True
def test_no_gap_suggestions(self):
assert self.plan["gap_suggestions"] is False
def test_has_telegram_channel(self):
assert "telegram" in self.plan["channels"]
def test_fireworks_models_only(self):
models = self.plan["models"]
for m in models:
assert "fireworks" in m
def test_no_premium_models(self):
premium = {"gpt-4o", "gpt-4o-mini", "claude-haiku-4-5-20251001",
"gemini-2.5-flash", "gemini-2.5-lite", "gemini-2.5-pro"}
assert not premium.intersection(set(self.plan["models"]))
class TestBusinessPlan:
def setup_method(self):
self.plan = PLAN_LIMITS["business"]
def test_max_published_is_ten(self):
assert self.plan["max_published"] == 10
def test_can_remove_branding(self):
assert self.plan["show_branding"] is False
def test_unlimited_appointments_chatbots(self):
assert self.plan["appointments_chatbots"] == 999999
def test_unlimited_campaigns_per_month(self):
assert self.plan["campaigns_per_month"] == 999999
def test_has_campaign_recipient_limit(self):
# Business is capped below Agency/Enterprise
assert self.plan["max_campaign_recipients"] < PLAN_LIMITS["agency"]["max_campaign_recipients"]
def test_has_gap_suggestions(self):
assert self.plan["gap_suggestions"] is True
def test_has_premium_models(self):
premium = {"gpt-4o", "gpt-4o-mini", "claude-haiku-4-5-20251001"}
assert premium.issubset(set(self.plan["models"]))
def test_has_google_models(self):
google = {"gemini-2.5-flash", "gemini-2.5-pro"}
assert google.issubset(set(self.plan["models"]))
class TestAgencyPlan:
def setup_method(self):
self.plan = PLAN_LIMITS["agency"]
def test_unlimited_published(self):
assert self.plan["max_published"] == 999999
def test_unlimited_campaign_recipients(self):
assert self.plan["max_campaign_recipients"] == 999999
def test_code_export_enabled(self):
assert self.plan["code_export"] is True
def test_gap_suggestions_enabled(self):
assert self.plan["gap_suggestions"] is True
def test_no_branding(self):
assert self.plan["show_branding"] is False
class TestEnterprisePlan:
def setup_method(self):
self.plan = PLAN_LIMITS["enterprise"]
def test_wildcard_models(self):
assert "*" in self.plan["models"]
def test_all_features_enabled(self):
assert self.plan["appointments"] is True
assert self.plan["campaigns"] is True
assert self.plan["inbox_replies"] is True
assert self.plan["leads_editing"] is True
assert self.plan["gap_suggestions"] is True
assert self.plan["code_export"] is True
assert self.plan["show_branding"] is False
def test_unlimited_everything(self):
BIG = 999999
assert self.plan["max_published"] == BIG
assert self.plan["appointments_chatbots"] == BIG
assert self.plan["campaigns_per_month"] == BIG
assert self.plan["max_campaign_recipients"] == BIG
class TestTierProgression:
"""Each higher tier must be strictly better than the tier below it."""
def test_max_published_increases_with_tier(self):
assert PLAN_LIMITS["free"]["max_published"] \
<= PLAN_LIMITS["starter"]["max_published"] \
<= PLAN_LIMITS["business"]["max_published"] \
<= PLAN_LIMITS["agency"]["max_published"]
def test_conversation_limits_increase_with_tier(self):
assert PLAN_LIMITS["free"]["conversations_limit"] \
< PLAN_LIMITS["starter"]["conversations_limit"] \
< PLAN_LIMITS["business"]["conversations_limit"] \
< PLAN_LIMITS["agency"]["conversations_limit"]
def test_business_has_more_models_than_starter(self):
starter_models = set(PLAN_LIMITS["starter"]["models"])
business_models = set(PLAN_LIMITS["business"]["models"])
assert starter_models.issubset(business_models)
assert len(business_models) > len(starter_models)
def test_appointment_chatbots_increases_with_tier(self):
assert PLAN_LIMITS["free"]["appointments_chatbots"] \
<= PLAN_LIMITS["starter"]["appointments_chatbots"] \
<= PLAN_LIMITS["business"]["appointments_chatbots"]
def test_campaign_recipients_increases_with_tier(self):
assert PLAN_LIMITS["free"]["max_campaign_recipients"] \
< PLAN_LIMITS["starter"]["max_campaign_recipients"] \
< PLAN_LIMITS["business"]["max_campaign_recipients"] \
<= PLAN_LIMITS["agency"]["max_campaign_recipients"]
class TestModelCatalog:
"""MODEL_CATALOG and MODEL_PROVIDERS consistency checks."""
def test_all_catalog_models_have_required_fields(self):
for model_id, meta in MODEL_CATALOG.items():
assert "name" in meta, f"{model_id} missing 'name'"
assert "provider" in meta, f"{model_id} missing 'provider'"
assert "badge" in meta, f"{model_id} missing 'badge'"
def test_all_catalog_models_have_provider_mapping(self):
for model_id in MODEL_CATALOG:
assert model_id in MODEL_PROVIDERS, \
f"{model_id} in MODEL_CATALOG but not in MODEL_PROVIDERS"
def test_provider_values_are_known(self):
known = {"fireworks", "openai", "anthropic", "google"}
for model_id, provider in MODEL_PROVIDERS.items():
assert provider in known, \
f"{model_id} has unknown provider '{provider}'"
def test_non_enterprise_plan_models_are_in_catalog(self):
for plan_name, plan in PLAN_LIMITS.items():
if plan_name == "enterprise":
continue
for model_id in plan["models"]:
assert model_id in MODEL_CATALOG, \
f"Plan '{plan_name}' references '{model_id}' not in MODEL_CATALOG"
def test_default_models_are_in_catalog(self):
for plan, model_id in DEFAULT_MODELS.items():
assert model_id in MODEL_CATALOG, \
f"DEFAULT_MODELS[{plan}] = '{model_id}' not in MODEL_CATALOG"
def test_default_models_are_in_plan_limits(self):
for plan, model_id in DEFAULT_MODELS.items():
plan_models = PLAN_LIMITS[plan]["models"]
if "*" not in plan_models:
assert model_id in plan_models, \
f"Default model '{model_id}' for plan '{plan}' not in that plan's models list"