import sys import types from services import battlecard_service from repositories.tenant_repository import tenant_repository from repositories.competitor_repository import competitor_repository from repositories.product_repository import product_repository from repositories.battlecard_repository import battlecard_repository def _install_fake_extractor(monkeypatch, fn): for name in ('core', 'core.extractor'): if name not in sys.modules: monkeypatch.setitem(sys.modules, name, types.ModuleType(name)) sys.modules['core.extractor'].extract_with_openrouter = fn def _setup(): tenant = tenant_repository.create({ 'name': 'G Adventures', 'domain': 'gadventures.com', 'tier': 'analyse', 'status': 'active', 'max_seats': 5, }) competitor = competitor_repository.create({ 'tenant_id': tenant['id'], 'name': 'Intrepid Travel', 'website': 'https://intrepidtravel.com', 'catalogue_url': 'https://intrepidtravel.com/adventures', 'primary_market': 'GB', 'active': True, }) product_repository.create({ 'tenant_id': tenant['id'], 'competitor_id': competitor['id'], 'trip_name': 'Peru Classic', 'majority_price_usd': 2190, 'duration_days': 15, }) return tenant, competitor def test_generate_battlecard_creates_record(monkeypatch): tenant, competitor = _setup() _install_fake_extractor(monkeypatch, lambda prompt, content: { 'positioning': 'Budget-mid, group focused', 'strengths': ['Strong brand'], 'weaknesses': ['Basic accommodation'], 'pricing_observation': 'Discounting Q3', }) card = battlecard_service.generate_battlecard(competitor['id'], tenant['id']) assert card is not None assert card['content_json']['positioning'] == 'Budget-mid, group focused' assert 'Strong brand' in card['content'] assert battlecard_repository.get_for_competitor(competitor['id']) is not None def test_generate_battlecard_returns_none_on_extraction_failure(monkeypatch): tenant, competitor = _setup() def _raise(prompt, content): raise RuntimeError('all models exhausted') _install_fake_extractor(monkeypatch, _raise) result = battlecard_service.generate_battlecard(competitor['id'], tenant['id']) assert result is None def test_generate_battlecard_handles_no_products_gracefully(monkeypatch): tenant = tenant_repository.create({ 'name': 'G Adventures', 'domain': 'gadventures.com', 'tier': 'analyse', 'status': 'active', 'max_seats': 5, }) competitor = competitor_repository.create({ 'tenant_id': tenant['id'], 'name': 'New Competitor', 'website': 'https://new.com', 'catalogue_url': 'https://new.com/trips', 'primary_market': 'US', 'active': True, }) _install_fake_extractor(monkeypatch, lambda prompt, content: { 'positioning': 'Unknown', 'strengths': [], 'weaknesses': [], 'pricing_observation': 'n/a', }) card = battlecard_service.generate_battlecard(competitor['id'], tenant['id']) assert card is not None