139 lines
4.4 KiB
Python
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_falls_back_to_draft_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.assertTrue(result.ok)
|
|
run = CommandRun.objects.get(trigger_message=trigger, profile=self.profile)
|
|
self.assertEqual("ok", run.status)
|
|
self.assertIn("bp_ai_failed", str(run.error))
|
|
self.assertTrue(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)
|