Skip to content

Commit e23ca94

Browse files
authored
chore: ensure session_id context works with page method (#269)
1 parent 5a7f324 commit e23ca94

File tree

2 files changed

+325
-0
lines changed

2 files changed

+325
-0
lines changed

posthog/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,9 @@ def page(
651651
require("distinct_id", distinct_id, ID_TYPES)
652652
require("properties", properties, dict)
653653

654+
if "$session_id" not in properties and get_context_session_id():
655+
properties["$session_id"] = get_context_session_id()
656+
654657
require("url", url, string_types)
655658
properties["$current_url"] = url
656659

posthog/test/test_client.py

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import unittest
33
from datetime import datetime
44
from uuid import uuid4
5+
from posthog.scopes import get_context_session_id, set_context_session, new_context
56

67
import mock
78
import six
@@ -939,6 +940,255 @@ def test_advanced_page(self):
939940
self.assertEqual(msg["uuid"], "new-uuid")
940941
self.assertEqual(msg["distinct_id"], "distinct_id")
941942

943+
@parameterized.expand(
944+
[
945+
# test_name, session_id, additional_properties, expected_properties
946+
("basic_session_id", "test-session-123", {}, {}),
947+
(
948+
"session_id_with_other_properties",
949+
"test-session-456",
950+
{
951+
"custom_prop": "custom_value",
952+
"$process_person_profile": False,
953+
"$current_url": "https://example.com",
954+
},
955+
{
956+
"custom_prop": "custom_value",
957+
"$process_person_profile": False,
958+
"$current_url": "https://example.com",
959+
},
960+
),
961+
("session_id_uuid_format", str(uuid4()), {}, {}),
962+
("session_id_numeric_string", "1234567890", {}, {}),
963+
("session_id_empty_string", "", {}, {}),
964+
("session_id_with_special_chars", "session-123_test.id", {}, {}),
965+
]
966+
)
967+
def test_capture_with_session_id_variations(
968+
self, test_name, session_id, additional_properties, expected_properties
969+
):
970+
client = self.client
971+
972+
properties = {"$session_id": session_id, **additional_properties}
973+
success, msg = client.capture(
974+
"distinct_id", "python test event", properties=properties
975+
)
976+
client.flush()
977+
978+
self.assertTrue(success)
979+
self.assertFalse(self.failed)
980+
self.assertEqual(msg["event"], "python test event")
981+
self.assertEqual(msg["distinct_id"], "distinct_id")
982+
self.assertEqual(msg["properties"]["$session_id"], session_id)
983+
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
984+
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
985+
986+
# Check additional expected properties
987+
for key, value in expected_properties.items():
988+
self.assertEqual(msg["properties"][key], value)
989+
990+
def test_session_id_preserved_with_groups(self):
991+
client = self.client
992+
session_id = "group-session-101"
993+
994+
success, msg = client.capture(
995+
"distinct_id",
996+
"test_event",
997+
properties={"$session_id": session_id},
998+
groups={"company": "id:5", "instance": "app.posthog.com"},
999+
)
1000+
client.flush()
1001+
1002+
self.assertTrue(success)
1003+
self.assertEqual(msg["properties"]["$session_id"], session_id)
1004+
self.assertEqual(
1005+
msg["properties"]["$groups"],
1006+
{"company": "id:5", "instance": "app.posthog.com"},
1007+
)
1008+
1009+
def test_session_id_with_anonymous_event(self):
1010+
client = self.client
1011+
session_id = "anonymous-session-202"
1012+
1013+
success, msg = client.capture(
1014+
"distinct_id",
1015+
"anonymous_event",
1016+
properties={"$session_id": session_id, "$process_person_profile": False},
1017+
)
1018+
client.flush()
1019+
1020+
self.assertTrue(success)
1021+
self.assertEqual(msg["properties"]["$session_id"], session_id)
1022+
self.assertEqual(msg["properties"]["$process_person_profile"], False)
1023+
1024+
def test_page_with_session_id(self):
1025+
client = self.client
1026+
session_id = "page-session-303"
1027+
1028+
success, msg = client.page(
1029+
"distinct_id",
1030+
"https://posthog.com/contact",
1031+
properties={"$session_id": session_id, "page_type": "contact"},
1032+
)
1033+
client.flush()
1034+
1035+
self.assertTrue(success)
1036+
self.assertFalse(self.failed)
1037+
self.assertEqual(msg["event"], "$pageview")
1038+
self.assertEqual(msg["distinct_id"], "distinct_id")
1039+
self.assertEqual(msg["properties"]["$session_id"], session_id)
1040+
self.assertEqual(
1041+
msg["properties"]["$current_url"], "https://posthog.com/contact"
1042+
)
1043+
self.assertEqual(msg["properties"]["page_type"], "contact")
1044+
1045+
@parameterized.expand(
1046+
[
1047+
# test_name, event_name, session_id, additional_properties, expected_additional_properties
1048+
(
1049+
"screen_event",
1050+
"$screen",
1051+
"special-session-505",
1052+
{"$screen_name": "HomeScreen"},
1053+
{"$screen_name": "HomeScreen"},
1054+
),
1055+
(
1056+
"survey_event",
1057+
"survey sent",
1058+
"survey-session-606",
1059+
{
1060+
"$survey_id": "survey_123",
1061+
"$survey_questions": [
1062+
{"id": "q1", "question": "How likely are you to recommend us?"}
1063+
],
1064+
},
1065+
{"$survey_id": "survey_123"},
1066+
),
1067+
(
1068+
"complex_properties_event",
1069+
"complex_event",
1070+
"mixed-session-707",
1071+
{
1072+
"$current_url": "https://example.com/page",
1073+
"$process_person_profile": True,
1074+
"custom_property": "custom_value",
1075+
"numeric_property": 42,
1076+
"boolean_property": True,
1077+
},
1078+
{
1079+
"$current_url": "https://example.com/page",
1080+
"$process_person_profile": True,
1081+
"custom_property": "custom_value",
1082+
"numeric_property": 42,
1083+
"boolean_property": True,
1084+
},
1085+
),
1086+
(
1087+
"csp_violation",
1088+
"$csp_violation",
1089+
"csp-session-789",
1090+
{
1091+
"$csp_version": "1.0",
1092+
"$current_url": "https://example.com/page",
1093+
"$process_person_profile": False,
1094+
"$raw_user_agent": "Mozilla/5.0 Test Agent",
1095+
"$csp_document_url": "https://example.com/page",
1096+
"$csp_blocked_url": "https://malicious.com/script.js",
1097+
"$csp_violated_directive": "script-src",
1098+
},
1099+
{
1100+
"$csp_version": "1.0",
1101+
"$current_url": "https://example.com/page",
1102+
"$process_person_profile": False,
1103+
"$raw_user_agent": "Mozilla/5.0 Test Agent",
1104+
"$csp_document_url": "https://example.com/page",
1105+
"$csp_blocked_url": "https://malicious.com/script.js",
1106+
"$csp_violated_directive": "script-src",
1107+
},
1108+
),
1109+
]
1110+
)
1111+
def test_session_id_with_different_event_types(
1112+
self,
1113+
test_name,
1114+
event_name,
1115+
session_id,
1116+
additional_properties,
1117+
expected_additional_properties,
1118+
):
1119+
client = self.client
1120+
1121+
properties = {"$session_id": session_id, **additional_properties}
1122+
success, msg = client.capture("distinct_id", event_name, properties=properties)
1123+
client.flush()
1124+
1125+
self.assertTrue(success)
1126+
self.assertEqual(msg["event"], event_name)
1127+
self.assertEqual(msg["properties"]["$session_id"], session_id)
1128+
1129+
# Check additional expected properties
1130+
for key, value in expected_additional_properties.items():
1131+
self.assertEqual(msg["properties"][key], value)
1132+
1133+
# Verify system properties are still added
1134+
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
1135+
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
1136+
1137+
@parameterized.expand(
1138+
[
1139+
# test_name, super_properties, event_session_id, expected_session_id, expected_super_props
1140+
(
1141+
"super_properties_override_session_id",
1142+
{"$session_id": "super-session", "source": "test"},
1143+
"event-session-808",
1144+
"super-session",
1145+
{"source": "test"},
1146+
),
1147+
(
1148+
"no_super_properties_conflict",
1149+
{"source": "test", "version": "1.0"},
1150+
"event-session-909",
1151+
"event-session-909",
1152+
{"source": "test", "version": "1.0"},
1153+
),
1154+
(
1155+
"empty_super_properties",
1156+
{},
1157+
"event-session-111",
1158+
"event-session-111",
1159+
{},
1160+
),
1161+
(
1162+
"super_properties_with_other_dollar_props",
1163+
{"$current_url": "https://super.com", "source": "test"},
1164+
"event-session-222",
1165+
"event-session-222",
1166+
{"$current_url": "https://super.com", "source": "test"},
1167+
),
1168+
]
1169+
)
1170+
def test_session_id_with_super_properties_variations(
1171+
self,
1172+
test_name,
1173+
super_properties,
1174+
event_session_id,
1175+
expected_session_id,
1176+
expected_super_props,
1177+
):
1178+
client = Client(FAKE_TEST_API_KEY, super_properties=super_properties)
1179+
1180+
success, msg = client.capture(
1181+
"distinct_id", "test_event", properties={"$session_id": event_session_id}
1182+
)
1183+
client.flush()
1184+
1185+
self.assertTrue(success)
1186+
self.assertEqual(msg["properties"]["$session_id"], expected_session_id)
1187+
1188+
# Check expected super properties are present
1189+
for key, value in expected_super_props.items():
1190+
self.assertEqual(msg["properties"][key], value)
1191+
9421192
def test_flush(self):
9431193
client = self.client
9441194
# set up the consumer with more requests than a single batch will allow
@@ -1397,3 +1647,75 @@ def test_get_decide_returns_normalized_decide_response(self, patch_flags):
13971647
"errorsWhileComputingFlags": False,
13981648
"requestId": "test-id",
13991649
}
1650+
1651+
def test_set_context_session_with_capture(self):
1652+
with new_context():
1653+
set_context_session("context-session-123")
1654+
1655+
success, msg = self.client.capture(
1656+
"distinct_id", "test_event", {"custom_prop": "value"}
1657+
)
1658+
self.client.flush()
1659+
1660+
self.assertTrue(success)
1661+
self.assertEqual(msg["properties"]["$session_id"], "context-session-123")
1662+
1663+
def test_set_context_session_with_page(self):
1664+
with new_context():
1665+
set_context_session("page-context-session-456")
1666+
1667+
success, msg = self.client.page("distinct_id", "https://example.com/page")
1668+
self.client.flush()
1669+
1670+
self.assertTrue(success)
1671+
self.assertEqual(
1672+
msg["properties"]["$session_id"], "page-context-session-456"
1673+
)
1674+
1675+
def test_set_context_session_with_page_explicit_properties(self):
1676+
with new_context():
1677+
set_context_session("page-explicit-session-789")
1678+
1679+
properties = {
1680+
"$session_id": get_context_session_id(),
1681+
"page_type": "landing",
1682+
}
1683+
success, msg = self.client.page(
1684+
"distinct_id", "https://example.com/landing", properties
1685+
)
1686+
self.client.flush()
1687+
1688+
self.assertTrue(success)
1689+
self.assertEqual(
1690+
msg["properties"]["$session_id"], "page-explicit-session-789"
1691+
)
1692+
1693+
def test_set_context_session_override_in_capture(self):
1694+
"""Test that explicit session ID overrides context session ID in capture"""
1695+
from posthog.scopes import set_context_session, new_context
1696+
1697+
with new_context():
1698+
set_context_session("context-session-override")
1699+
1700+
success, msg = self.client.capture(
1701+
"distinct_id",
1702+
"test_event",
1703+
{"$session_id": "explicit-session-override", "custom_prop": "value"},
1704+
)
1705+
self.client.flush()
1706+
1707+
self.assertTrue(success)
1708+
self.assertEqual(
1709+
msg["properties"]["$session_id"], "explicit-session-override"
1710+
)
1711+
1712+
def test_set_context_session_with_identify(self):
1713+
with new_context(capture_exceptions=False):
1714+
set_context_session("identify-session-555")
1715+
1716+
success, msg = self.client.identify("distinct_id", {"trait": "value"})
1717+
self.client.flush()
1718+
1719+
self.assertTrue(success)
1720+
# In identify, the session ID is added to the $set payload
1721+
self.assertEqual(msg["$set"]["$session_id"], "identify-session-555")

0 commit comments

Comments
 (0)