Remove real contact numbers from tests and update tooling

- Replace real phone numbers in tests with Ofcom-reserved fictitious
  numbers (447700900xxx range) throughout test suite
- Add SIGNAL_NUMBER to stack.env.example documenting required env var
- Update pre-commit hooks to latest versions (black 26.3.0, isort 8.0.1,
  flake8 7.3.0, djhtml 3.0.10, ripsecrets v0.1.11)
- Add CLAUDE.md with rule prohibiting real contact identifiers in code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 22:38:06 +00:00
parent ff66bc9e1f
commit add685a326
7 changed files with 90 additions and 26 deletions

View File

@@ -3,7 +3,11 @@ from __future__ import annotations
from django.test import TestCase
from core.models import Person, PersonIdentifier, User
from core.presence import AvailabilitySignal, latest_state_for_people, record_native_signal
from core.presence import (
AvailabilitySignal,
latest_state_for_people,
record_native_signal,
)
from core.presence.inference import now_ms
from core.views.compose import _compose_availability_payload, _context_base

View File

@@ -8,18 +8,18 @@ from django.test import TestCase, override_settings
from core.assist.repeat_answer import find_repeat_answer, learn_from_message
from core.models import (
AnswerSuggestionEvent,
Chat,
ChatSession,
ChatTaskSource,
DerivedTask,
DerivedTaskEvent,
Message,
Person,
PersonIdentifier,
TaskCompletionPattern,
TaskEpic,
TaskProject,
User,
Message,
Chat,
)
from core.tasks.engine import process_inbound_task_intelligence
@@ -34,7 +34,9 @@ class RepeatAnswerTests(TestCase):
service="whatsapp",
identifier="120363402761690215@g.us",
)
self.session = ChatSession.objects.create(user=self.user, identifier=self.identifier)
self.session = ChatSession.objects.create(
user=self.user, identifier=self.identifier
)
def test_suggest_only_for_repeated_group_question(self):
q1 = Message.objects.create(
@@ -71,7 +73,9 @@ class RepeatAnswerTests(TestCase):
self.assertIsNotNone(suggestion)
self.assertIn("deploy", suggestion.answer_text.lower())
self.assertTrue(
AnswerSuggestionEvent.objects.filter(message=q2, status="suggested").exists()
AnswerSuggestionEvent.objects.filter(
message=q2, status="suggested"
).exists()
)
@@ -86,7 +90,9 @@ class TaskEngineTests(TestCase):
service="whatsapp",
identifier="120363402761690215@g.us",
)
self.session = ChatSession.objects.create(user=self.user, identifier=self.identifier)
self.session = ChatSession.objects.create(
user=self.user, identifier=self.identifier
)
self.project = TaskProject.objects.create(user=self.user, name="Ops")
ChatTaskSource.objects.create(
user=self.user,
@@ -95,7 +101,9 @@ class TaskEngineTests(TestCase):
project=self.project,
enabled=True,
)
TaskCompletionPattern.objects.create(user=self.user, phrase="done", enabled=True)
TaskCompletionPattern.objects.create(
user=self.user, phrase="done", enabled=True
)
def test_creates_derived_task_on_task_like_message(self):
m = Message.objects.create(
@@ -111,7 +119,9 @@ class TaskEngineTests(TestCase):
task = DerivedTask.objects.get(origin_message=m)
self.assertEqual("open", task.status_snapshot)
self.assertTrue(task.reference_code)
self.assertTrue(DerivedTaskEvent.objects.filter(task=task, event_type="created").exists())
self.assertTrue(
DerivedTaskEvent.objects.filter(task=task, event_type="created").exists()
)
def test_marks_completion_from_regex_marker(self):
seed = Message.objects.create(
@@ -138,7 +148,9 @@ class TaskEngineTests(TestCase):
task.refresh_from_db()
self.assertEqual("completed", task.status_snapshot)
self.assertTrue(
DerivedTaskEvent.objects.filter(task=task, event_type="completion_marked").exists()
DerivedTaskEvent.objects.filter(
task=task, event_type="completion_marked"
).exists()
)
def test_matches_whatsapp_private_channel_variants(self):
@@ -172,7 +184,9 @@ class TaskEngineTests(TestCase):
service="signal",
identifier="+447700900555",
)
signal_session = ChatSession.objects.create(user=self.user, identifier=signal_identifier)
signal_session = ChatSession.objects.create(
user=self.user, identifier=signal_identifier
)
ChatTaskSource.objects.create(
user=self.user,
service="signal",
@@ -308,7 +322,9 @@ class TaskEngineTests(TestCase):
)
async_to_sync(process_inbound_task_intelligence)(cmd)
self.assertTrue(mocked_send.await_count >= 1)
list_payloads = [str(call.kwargs.get("text") or "") for call in mocked_send.await_args_list]
list_payloads = [
str(call.kwargs.get("text") or "") for call in mocked_send.await_args_list
]
self.assertTrue(any("open tasks" in row.lower() for row in list_payloads))
self.assertTrue(any("#1" in row for row in list_payloads))
@@ -334,7 +350,9 @@ class TaskEngineTests(TestCase):
)
async_to_sync(process_inbound_task_intelligence)(m1)
async_to_sync(process_inbound_task_intelligence)(m2)
self.assertEqual(2, DerivedTask.objects.filter(user=self.user, project=self.project).count())
self.assertEqual(
2, DerivedTask.objects.filter(user=self.user, project=self.project).count()
)
cmd = Message.objects.create(
user=self.user,
session=self.session,
@@ -352,7 +370,9 @@ class TaskEngineTests(TestCase):
)
self.assertEqual(1, len(remaining))
self.assertEqual("one", remaining[0])
payloads = [str(call.kwargs.get("text") or "") for call in mocked_send.await_args_list]
payloads = [
str(call.kwargs.get("text") or "") for call in mocked_send.await_args_list
]
self.assertTrue(any("removed #2" in row.lower() for row in payloads))
@patch("core.tasks.engine.send_message_raw", new_callable=AsyncMock)
@@ -368,9 +388,14 @@ class TaskEngineTests(TestCase):
source_chat_id="120363402761690215@g.us",
)
async_to_sync(process_inbound_task_intelligence)(m)
payloads = [str(call.kwargs.get("text") or "") for call in mocked_send.await_args_list]
payloads = [
str(call.kwargs.get("text") or "") for call in mocked_send.await_args_list
]
self.assertTrue(
any(".l list tasks" in row.lower() and ".undo" in row.lower() for row in payloads),
any(
".l list tasks" in row.lower() and ".undo" in row.lower()
for row in payloads
),
"Expected periodic reminder to mention both .l and .undo.",
)
@@ -386,8 +411,12 @@ class TaskEngineTests(TestCase):
source_chat_id="120363402761690215@g.us",
)
async_to_sync(process_inbound_task_intelligence)(msg)
self.assertTrue(TaskEpic.objects.filter(project=self.project, name="Security").exists())
payloads = [str(call.kwargs.get("text") or "") for call in mocked_send.await_args_list]
self.assertTrue(
TaskEpic.objects.filter(project=self.project, name="Security").exists()
)
payloads = [
str(call.kwargs.get("text") or "") for call in mocked_send.await_args_list
]
self.assertTrue(any("whatsapp usage" in row.lower() for row in payloads))
def test_task_with_epic_token_assigns_epic(self):

View File

@@ -1,8 +1,9 @@
from __future__ import annotations
from unittest.mock import patch
from asgiref.sync import async_to_sync
from django.test import TestCase
from unittest.mock import patch
from core.clients import signalapi
@@ -43,7 +44,9 @@ class _FakeClientSession:
class SignalSendNormalizationTests(TestCase):
def test_normalize_signal_recipient_phone_and_uuid(self):
self.assertEqual("+447700900000", signalapi.normalize_signal_recipient("447700900000"))
self.assertEqual(
"+447700900000", signalapi.normalize_signal_recipient("447700900000")
)
self.assertEqual(
"+447700900000", signalapi.normalize_signal_recipient("+44 7700-900000")
)
@@ -71,4 +74,3 @@ class SignalSendNormalizationTests(TestCase):
self.assertGreaterEqual(len(_FakeClientSession.posted_payloads), 1)
first_payload = _FakeClientSession.posted_payloads[0]
self.assertEqual(["+447700900000"], first_payload.get("recipients"))

View File

@@ -40,4 +40,3 @@ class SignalUnlinkFallbackTests(TestCase):
self.assertTrue(result)
self.assertEqual(2, mock_delete.call_count)
mock_wipe.assert_called_once()