Files
GIA/core/tests/test_presence_engine.py

149 lines
4.7 KiB
Python

from __future__ import annotations
from django.test import TestCase
from core.models import (
ContactAvailabilityEvent,
ContactAvailabilitySettings,
ContactAvailabilitySpan,
Person,
PersonIdentifier,
User,
)
from core.presence.engine import (
AvailabilitySignal,
ensure_fading_state,
record_inferred_signal,
record_native_signal,
)
from core.presence.inference import now_ms
class PresenceEngineTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(
"presence-user", "presence@example.com", "x"
)
self.person = Person.objects.create(user=self.user, name="Presence Person")
self.identifier = PersonIdentifier.objects.create(
user=self.user,
person=self.person,
service="signal",
identifier="+15550001111",
)
ContactAvailabilitySettings.objects.update_or_create(
user=self.user,
defaults={
"enabled": True,
"show_in_chat": True,
"show_in_groups": True,
"inference_enabled": True,
"retention_days": 90,
"fade_threshold_seconds": 1,
},
)
def test_read_receipt_signal_creates_available_event(self):
ts = now_ms()
event = record_native_signal(
AvailabilitySignal(
user=self.user,
person=self.person,
person_identifier=self.identifier,
service="signal",
source_kind="read_receipt",
availability_state="available",
confidence=0.95,
ts=ts,
payload={"origin": "test"},
)
)
self.assertIsNotNone(event)
self.assertEqual(
1, ContactAvailabilityEvent.objects.filter(user=self.user).count()
)
self.assertEqual("available", event.availability_state)
def test_inactivity_transitions_to_fading(self):
base_ts = now_ms()
record_inferred_signal(
AvailabilitySignal(
user=self.user,
person=self.person,
person_identifier=self.identifier,
service="signal",
source_kind="read_receipt",
availability_state="available",
confidence=0.95,
ts=base_ts,
)
)
fade_event = ensure_fading_state(
user=self.user,
person=self.person,
person_identifier=self.identifier,
service="signal",
at_ts=base_ts + 10_000,
)
self.assertIsNotNone(fade_event)
self.assertEqual("fading", fade_event.availability_state)
def test_explicit_unavailable_blocks_fade_inference(self):
base_ts = now_ms()
record_native_signal(
AvailabilitySignal(
user=self.user,
person=self.person,
person_identifier=self.identifier,
service="xmpp",
source_kind="native_presence",
availability_state="unavailable",
confidence=1.0,
ts=base_ts,
)
)
fade_event = ensure_fading_state(
user=self.user,
person=self.person,
person_identifier=self.identifier,
service="xmpp",
at_ts=base_ts + 60_000,
)
self.assertIsNone(fade_event)
self.assertEqual(
1, ContactAvailabilityEvent.objects.filter(user=self.user).count()
)
def test_adjacent_same_state_events_extend_single_span(self):
ts0 = now_ms()
record_native_signal(
AvailabilitySignal(
user=self.user,
person=self.person,
person_identifier=self.identifier,
service="signal",
source_kind="typing_start",
availability_state="available",
confidence=0.9,
ts=ts0,
)
)
record_native_signal(
AvailabilitySignal(
user=self.user,
person=self.person,
person_identifier=self.identifier,
service="signal",
source_kind="message_in",
availability_state="available",
confidence=0.8,
ts=ts0 + 5_000,
)
)
spans = list(
ContactAvailabilitySpan.objects.filter(user=self.user).order_by("start_ts")
)
self.assertEqual(1, len(spans))
self.assertEqual(ts0, spans[0].start_ts)
self.assertEqual(ts0 + 5_000, spans[0].end_ts)