44
55import pytest
66
7- from api .analytics import PLATFORM_PATTERNS , detect_platform , track_og_image
7+ from api .analytics import PLATFORM_PATTERNS , _detect_whatsapp_or_signal , detect_platform , track_og_image
88
99
1010class TestDetectPlatform :
@@ -14,9 +14,29 @@ def test_detects_twitter(self) -> None:
1414 """Should detect Twitter bot."""
1515 assert detect_platform ("Twitterbot/1.0" ) == "twitter"
1616
17- def test_detects_whatsapp (self ) -> None :
18- """Should detect WhatsApp."""
19- assert detect_platform ("WhatsApp/2.21.4.22" ) == "whatsapp"
17+ def test_detects_whatsapp_ios (self ) -> None :
18+ """Should detect real WhatsApp iOS."""
19+ assert detect_platform ("WhatsApp/2.23.18.78 i" ) == "whatsapp"
20+
21+ def test_detects_whatsapp_android (self ) -> None :
22+ """Should detect real WhatsApp Android."""
23+ assert detect_platform ("WhatsApp/2.21.22.23 A" ) == "whatsapp"
24+
25+ def test_detects_whatsapp_desktop (self ) -> None :
26+ """Should detect real WhatsApp Desktop."""
27+ assert detect_platform ("WhatsApp/2.2336.9 N" ) == "whatsapp"
28+
29+ def test_detects_signal_as_fake_whatsapp (self ) -> None :
30+ """Should detect Signal (which uses fake WhatsApp User-Agent).
31+
32+ Signal deliberately uses 'WhatsApp' User-Agent to bypass rate limits,
33+ but without full version number like real WhatsApp.
34+ See: https://github.com/signalapp/Signal-Android/issues/10060
35+ """
36+ # Signal sends simple "WhatsApp" or "WhatsApp/2" without full version
37+ assert detect_platform ("WhatsApp" ) == "signal"
38+ assert detect_platform ("WhatsApp/2" ) == "signal"
39+ assert detect_platform ("WhatsApp/2.1" ) == "signal" # Only 2-part version
2040
2141 def test_detects_facebook (self ) -> None :
2242 """Should detect Facebook."""
@@ -60,8 +80,49 @@ def test_case_insensitive(self) -> None:
6080 assert detect_platform ("twitterbot/1.0" ) == "twitter"
6181
6282 def test_all_platforms_have_patterns (self ) -> None :
63- """Should have 27 platform patterns defined."""
64- assert len (PLATFORM_PATTERNS ) == 27
83+ """Should have 25 platform patterns in dict (whatsapp/signal handled separately)."""
84+ # 27 total platforms: 25 in PLATFORM_PATTERNS + whatsapp + signal (special handling)
85+ assert len (PLATFORM_PATTERNS ) == 25
86+
87+
88+ class TestWhatsAppSignalDetection :
89+ """Tests for WhatsApp vs Signal detection logic."""
90+
91+ def test_real_whatsapp_ios (self ) -> None :
92+ """Real WhatsApp iOS should return 'whatsapp'."""
93+ assert _detect_whatsapp_or_signal ("WhatsApp/2.23.18.78 i" ) == "whatsapp"
94+
95+ def test_real_whatsapp_android (self ) -> None :
96+ """Real WhatsApp Android should return 'whatsapp'."""
97+ assert _detect_whatsapp_or_signal ("WhatsApp/2.21.22.23 A" ) == "whatsapp"
98+
99+ def test_real_whatsapp_cfnetwork (self ) -> None :
100+ """Real WhatsApp with CFNetwork should return 'whatsapp'."""
101+ assert _detect_whatsapp_or_signal ("WhatsApp/2.18.31.32 CFNetwork/894 Darwin/17.4.0" ) == "whatsapp"
102+
103+ def test_signal_simple (self ) -> None :
104+ """Signal's simple WhatsApp UA should return 'signal'."""
105+ assert _detect_whatsapp_or_signal ("WhatsApp" ) == "signal"
106+
107+ def test_signal_with_major_version (self ) -> None :
108+ """Signal's WhatsApp/2 should return 'signal'."""
109+ assert _detect_whatsapp_or_signal ("WhatsApp/2" ) == "signal"
110+
111+ def test_signal_with_two_part_version (self ) -> None :
112+ """Signal's WhatsApp/2.1 (only 2 parts) should return 'signal'."""
113+ assert _detect_whatsapp_or_signal ("WhatsApp/2.1" ) == "signal"
114+
115+ def test_non_whatsapp_returns_none (self ) -> None :
116+ """Non-WhatsApp User-Agent should return None."""
117+ assert _detect_whatsapp_or_signal ("Twitterbot/1.0" ) is None
118+ assert _detect_whatsapp_or_signal ("Mozilla/5.0" ) is None
119+ assert _detect_whatsapp_or_signal ("" ) is None
120+
121+ def test_case_insensitive (self ) -> None :
122+ """Should handle case-insensitive matching."""
123+ assert _detect_whatsapp_or_signal ("WHATSAPP/2.23.18.78" ) == "whatsapp"
124+ assert _detect_whatsapp_or_signal ("whatsapp/2.23.18.78" ) == "whatsapp"
125+ assert _detect_whatsapp_or_signal ("WHATSAPP" ) == "signal"
65126
66127
67128class TestTrackOgImage :
0 commit comments