Files
GIA/core/tests/test_bp_fallback.py
2026-03-02 12:45:24 +00:00

139 lines
4.4 KiB
Python

from __future__ import annotations
from unittest.mock import AsyncMock, patch
from asgiref.sync import async_to_sync
from django.test import TransactionTestCase
from core.commands.base import CommandContext
from core.commands.handlers.bp import BPCommandHandler
from core.models import (
AI,
BusinessPlanDocument,
ChatSession,
CommandAction,
CommandChannelBinding,
CommandProfile,
CommandRun,
Message,
Person,
PersonIdentifier,
User,
)
class BPFallbackTests(TransactionTestCase):
def setUp(self):
self.user = User.objects.create_user(
username="bp-fallback-user",
email="bp-fallback@example.com",
password="x",
)
self.person = Person.objects.create(user=self.user, name="Fallback Person")
self.identifier = PersonIdentifier.objects.create(
user=self.user,
person=self.person,
service="whatsapp",
identifier="120363402761690215",
)
self.session = ChatSession.objects.create(
user=self.user,
identifier=self.identifier,
)
self.profile = CommandProfile.objects.create(
user=self.user,
slug="bp",
name="Business Plan",
enabled=True,
trigger_token="#bp#",
reply_required=True,
exact_match_only=True,
)
CommandChannelBinding.objects.create(
profile=self.profile,
direction="ingress",
service="whatsapp",
channel_identifier="120363402761690215",
enabled=True,
)
CommandChannelBinding.objects.create(
profile=self.profile,
direction="egress",
service="web",
channel_identifier="120363402761690215",
enabled=True,
)
for action_type, position in (
("extract_bp", 0),
("save_document", 1),
("post_result", 2),
):
CommandAction.objects.create(
profile=self.profile,
action_type=action_type,
enabled=True,
position=position,
)
AI.objects.create(
user=self.user,
base_url="https://example.invalid",
api_key="test-key",
model="gpt-4o-mini",
)
def test_bp_fails_fast_when_ai_fails(self):
anchor = Message.objects.create(
user=self.user,
session=self.session,
sender_uuid="peer",
text="Anchor content",
ts=1000,
source_service="whatsapp",
source_message_id="wa-anchor-1",
)
trigger = Message.objects.create(
user=self.user,
session=self.session,
sender_uuid="peer",
text="#bp#",
ts=2000,
source_service="whatsapp",
source_message_id="wa-trigger-1",
reply_to=anchor,
reply_source_service="whatsapp",
reply_source_message_id="wa-anchor-1",
)
with patch(
"core.commands.handlers.bp.ai_runner.run_prompt",
new=AsyncMock(side_effect=RuntimeError("quota")),
):
result = async_to_sync(BPCommandHandler().execute)(
CommandContext(
service="whatsapp",
channel_identifier="120363402761690215",
message_id=str(trigger.id),
user_id=self.user.id,
message_text="#bp#",
payload={},
)
)
self.assertFalse(result.ok)
run = CommandRun.objects.get(trigger_message=trigger, profile=self.profile)
self.assertEqual("failed", run.status)
self.assertIn("bp_ai_failed", str(run.error))
self.assertFalse(BusinessPlanDocument.objects.filter(trigger_message=trigger).exists())
def test_bp_uses_same_ai_selection_order_as_compose(self):
AI.objects.create(
user=self.user,
base_url="https://example.invalid",
api_key="another-key",
model="gpt-4o",
)
selected = AI.objects.filter(user=self.user).first()
# Compose uses QuerySet.first() without explicit ordering; BP should match.
self.assertIsNotNone(selected)
self.assertEqual(self.profile.user_id, selected.user_id)