Files
GIA/core/tests/test_phase1_command_reply.py
2026-03-04 02:19:22 +00:00

439 lines
15 KiB
Python

from __future__ import annotations
from asgiref.sync import async_to_sync
from django.test import TestCase
from core.commands.base import CommandContext
from core.commands.engine import _matches_trigger, process_inbound_message
from core.messaging.reply_sync import extract_reply_ref, resolve_reply_target
from core.views.compose import _command_options_for_channel
from core.models import (
ChatSession,
CommandAction,
CommandChannelBinding,
CommandProfile,
CommandVariantPolicy,
Message,
Person,
PersonIdentifier,
User,
)
class Phase1ReplyResolutionTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username="phase1-reply-user",
email="phase1-reply@example.com",
password="x",
)
self.person = Person.objects.create(user=self.user, name="Reply Person")
self.identifier = PersonIdentifier.objects.create(
user=self.user,
person=self.person,
service="signal",
identifier="+15550000001",
)
self.session = ChatSession.objects.create(
user=self.user,
identifier=self.identifier,
)
def test_resolve_reply_target_by_source_message_id(self):
anchor = Message.objects.create(
user=self.user,
session=self.session,
sender_uuid="+15550000001",
text="anchor",
ts=1000,
source_service="signal",
source_message_id="signal-msg-1",
)
resolved = async_to_sync(resolve_reply_target)(
self.user,
self.session,
{
"reply_source_service": "signal",
"reply_source_message_id": "signal-msg-1",
},
)
self.assertEqual(anchor.id, resolved.id if resolved else None)
def test_resolve_reply_target_with_bridge_ref_fallback(self):
anchor = Message.objects.create(
user=self.user,
session=self.session,
sender_uuid="+15550000001",
text="anchor",
ts=2000,
receipt_payload={
"bridge_refs": {
"signal": [
{
"xmpp_message_id": "xmpp-bridge-1",
"upstream_message_id": "signal-upstream-1",
"upstream_author": "+15550000001",
"upstream_ts": 2000,
"updated_at": 2000,
}
]
}
},
)
resolved = async_to_sync(resolve_reply_target)(
self.user,
self.session,
{
"reply_source_service": "signal",
"reply_source_message_id": "signal-upstream-1",
},
)
self.assertEqual(anchor.id, resolved.id if resolved else None)
def test_resolve_reply_target_miss(self):
resolved = async_to_sync(resolve_reply_target)(
self.user,
self.session,
{
"reply_source_service": "signal",
"reply_source_message_id": "does-not-exist",
},
)
self.assertIsNone(resolved)
def test_extract_reply_ref_xmpp(self):
result = extract_reply_ref(
"xmpp",
{
"reply_source_message_id": "xmpp-msg-1",
"reply_source_chat_id": "alice@example.test",
},
)
self.assertEqual("xmpp-msg-1", result.get("reply_source_message_id"))
self.assertEqual("xmpp", result.get("reply_source_service"))
def test_extract_reply_ref_signal(self):
result = extract_reply_ref(
"signal",
{
"envelope": {
"dataMessage": {
"quote": {"id": "signal-msg-quoted"},
}
}
},
)
self.assertEqual("signal-msg-quoted", result.get("reply_source_message_id"))
self.assertEqual("signal", result.get("reply_source_service"))
def test_extract_reply_ref_signal_target_sent_timestamp_variant(self):
result = extract_reply_ref(
"signal",
{
"envelope": {
"dataMessage": {
"quote": {
"targetSentTimestamp": 1772545268786,
"authorNumber": "+15550000001",
}
}
}
},
)
self.assertEqual(
"1772545268786",
result.get("reply_source_message_id"),
)
self.assertEqual("signal", result.get("reply_source_service"))
self.assertEqual("+15550000001", result.get("reply_source_chat_id"))
def test_extract_reply_ref_whatsapp(self):
result = extract_reply_ref(
"whatsapp",
{
"extendedTextMessage": {
"contextInfo": {
"stanzaId": "wa-msg-quoted",
"participant": "12345@s.whatsapp.net",
}
}
},
)
self.assertEqual("wa-msg-quoted", result.get("reply_source_message_id"))
self.assertEqual("whatsapp", result.get("reply_source_service"))
def test_extract_reply_ref_whatsapp_stanza_id_variant(self):
result = extract_reply_ref(
"whatsapp",
{
"extendedTextMessage": {
"contextInfo": {
"stanzaID": "wa-msg-quoted-2",
}
}
},
)
self.assertEqual("wa-msg-quoted-2", result.get("reply_source_message_id"))
self.assertEqual("whatsapp", result.get("reply_source_service"))
class Phase1CommandEngineTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username="phase1-command-user",
email="phase1-command@example.com",
password="x",
)
self.person = Person.objects.create(user=self.user, name="Command Person")
self.identifier = PersonIdentifier.objects.create(
user=self.user,
person=self.person,
service="signal",
identifier="+15550000002",
)
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="web",
channel_identifier="web-chan-1",
enabled=True,
)
def test_matches_trigger_exact_only(self):
self.assertTrue(_matches_trigger(self.profile, "#bp#"))
self.assertFalse(_matches_trigger(self.profile, " #bp# extra "))
def test_process_inbound_message_requires_reply(self):
msg = Message.objects.create(
user=self.user,
session=self.session,
sender_uuid="",
text="#bp#",
ts=3000,
source_service="web",
source_chat_id="web-chan-1",
message_meta={},
)
results = async_to_sync(process_inbound_message)(
CommandContext(
service="web",
channel_identifier="web-chan-1",
message_id=str(msg.id),
user_id=self.user.id,
message_text="#bp#",
payload={},
)
)
self.assertEqual(1, len(results))
self.assertEqual("skipped", results[0].status)
self.assertEqual("reply_required", results[0].error)
def test_process_inbound_message_skips_mirrored_origin(self):
msg = Message.objects.create(
user=self.user,
session=self.session,
sender_uuid="",
text="#bp#",
ts=4000,
source_service="web",
source_chat_id="web-chan-1",
message_meta={"origin_tag": "translation:test"},
)
results = async_to_sync(process_inbound_message)(
CommandContext(
service="web",
channel_identifier="web-chan-1",
message_id=str(msg.id),
user_id=self.user.id,
message_text="#bp#",
payload={},
)
)
self.assertEqual([], results)
def test_eligible_profile_matches_whatsapp_group_identifier_variants(self):
self.profile.channel_bindings.all().delete()
CommandChannelBinding.objects.create(
profile=self.profile,
direction="ingress",
service="whatsapp",
channel_identifier="120363402761690215@g.us",
enabled=True,
)
msg = Message.objects.create(
user=self.user,
session=self.session,
sender_uuid="",
text="#bp#",
ts=5000,
source_service="whatsapp",
source_chat_id="120363402761690215@g.us",
message_meta={},
)
results = async_to_sync(process_inbound_message)(
CommandContext(
service="whatsapp",
channel_identifier="120363402761690215",
message_id=str(msg.id),
user_id=self.user.id,
message_text="#bp#",
payload={},
)
)
self.assertEqual(1, len(results))
self.assertEqual("skipped", results[0].status)
self.assertEqual("reply_required", results[0].error)
def test_compose_command_options_show_bp_subcommands(self):
self.profile.channel_bindings.all().delete()
CommandChannelBinding.objects.create(
profile=self.profile,
direction="ingress",
service="whatsapp",
channel_identifier="120363402761690215@g.us",
enabled=True,
)
options = _command_options_for_channel(
self.user,
"whatsapp",
"120363402761690215@g.us",
)
names = [str(row.get("name") or "").strip().lower() for row in options]
self.assertIn("bp", names)
self.assertIn("bp set", names)
self.assertIn("bp set range", names)
self.assertIn("codex", names)
self.assertNotIn("announce task ids", names)
def test_first_user_codex_command_auto_enables_defaults_for_channel(self):
CommandProfile.objects.filter(user=self.user, slug="codex").delete()
msg = Message.objects.create(
user=self.user,
session=self.session,
sender_uuid="",
custom_author="USER",
text="#codex status#",
ts=6000,
source_service="web",
source_chat_id="web-chan-2",
message_meta={},
)
results = async_to_sync(process_inbound_message)(
CommandContext(
service="web",
channel_identifier="web-chan-2",
message_id=str(msg.id),
user_id=self.user.id,
message_text="#codex status#",
payload={},
)
)
self.assertEqual(1, len(results))
self.assertTrue(results[0].ok)
profile = CommandProfile.objects.filter(user=self.user, slug="codex").first()
self.assertIsNotNone(profile)
self.assertTrue(bool(profile.enabled if profile else False))
ingress_exists = CommandChannelBinding.objects.filter(
profile=profile,
direction="ingress",
enabled=True,
service="signal",
channel_identifier="+15550000002",
).exists()
egress_exists = CommandChannelBinding.objects.filter(
profile=profile,
direction="egress",
enabled=True,
service="signal",
channel_identifier="+15550000002",
).exists()
self.assertTrue(ingress_exists)
self.assertTrue(egress_exists)
def test_first_user_bp_command_auto_setup_is_idempotent(self):
CommandProfile.objects.filter(user=self.user, slug="bp").delete()
msg1 = Message.objects.create(
user=self.user,
session=self.session,
sender_uuid="",
custom_author="USER",
text="#bp#",
ts=7000,
source_service="web",
source_chat_id="web-chan-3",
message_meta={},
)
first_results = async_to_sync(process_inbound_message)(
CommandContext(
service="web",
channel_identifier="web-chan-3",
message_id=str(msg1.id),
user_id=self.user.id,
message_text="#bp#",
payload={},
)
)
self.assertEqual(1, len(first_results))
self.assertEqual("reply_required", first_results[0].error)
profile = CommandProfile.objects.filter(user=self.user, slug="bp").first()
self.assertIsNotNone(profile)
if profile is None:
return
self.assertEqual(3, CommandAction.objects.filter(profile=profile).count())
self.assertEqual(3, CommandVariantPolicy.objects.filter(profile=profile).count())
self.assertEqual(
2,
CommandChannelBinding.objects.filter(
profile=profile,
service="signal",
channel_identifier="+15550000002",
).count(),
)
msg2 = Message.objects.create(
user=self.user,
session=self.session,
sender_uuid="",
custom_author="USER",
text="#bp#",
ts=8000,
source_service="web",
source_chat_id="web-chan-3",
message_meta={},
)
second_results = async_to_sync(process_inbound_message)(
CommandContext(
service="web",
channel_identifier="web-chan-3",
message_id=str(msg2.id),
user_id=self.user.id,
message_text="#bp#",
payload={},
)
)
self.assertEqual(1, len(second_results))
self.assertEqual("reply_required", second_results[0].error)
self.assertEqual(3, CommandAction.objects.filter(profile=profile).count())
self.assertEqual(3, CommandVariantPolicy.objects.filter(profile=profile).count())
self.assertEqual(
2,
CommandChannelBinding.objects.filter(
profile=profile,
service="signal",
channel_identifier="+15550000002",
).count(),
)