66from django .db import transaction
77from django .utils import timezone
88
9- from parameterized import parameterized
9+ from parameterized import parameterized , parameterized_class
1010from rest_framework import status
1111
1212from posthog .models import ActivityLog , Comment , Organization , User
1313from posthog .models .person import Person
14+ from posthog .personhog_client .test_helpers import PersonhogTestMixin
1415
1516from products .conversations .backend .models import Ticket , TicketAssignment
1617from products .conversations .backend .models .constants import Channel , ChannelDetail , Priority , Status
@@ -99,61 +100,6 @@ def test_retrieve_ticket_marks_as_read(self, mock_on_commit):
99100 self .ticket .refresh_from_db ()
100101 self .assertEqual (self .ticket .unread_team_count , 0 )
101102
102- def test_retrieve_ticket_includes_person_data (self , mock_on_commit ):
103- """Test that retrieve includes person data when person exists."""
104- person = Person .objects .create (
105- team = self .team ,
106- distinct_ids = ["user-123" , "user@example.com" , "another-id" ],
107- properties = {"email" : "test@example.com" , "name" : "Test User" },
108- )
109-
110- response = self .client .get (f"/api/projects/{ self .team .id } /conversations/tickets/{ self .ticket .id } /" )
111- self .assertEqual (response .status_code , status .HTTP_200_OK )
112- self .assertIn ("person" , response .json ())
113- self .assertIsNotNone (response .json ()["person" ])
114- self .assertEqual (response .json ()["person" ]["id" ], str (person .uuid ))
115- self .assertEqual (response .json ()["person" ]["properties" ]["email" ], "test@example.com" )
116- # Verify all distinct_ids are returned, not just the ticket's
117- self .assertCountEqual (
118- response .json ()["person" ]["distinct_ids" ],
119- ["user-123" , "user@example.com" , "another-id" ],
120- )
121-
122- def test_retrieve_ticket_person_null_when_no_person (self , mock_on_commit ):
123- """Test that person is null when no person exists for distinct_id."""
124- response = self .client .get (f"/api/projects/{ self .team .id } /conversations/tickets/{ self .ticket .id } /" )
125- self .assertEqual (response .status_code , status .HTTP_200_OK )
126- self .assertIn ("person" , response .json ())
127- self .assertIsNone (response .json ()["person" ])
128-
129- def test_list_tickets_includes_person_data (self , mock_on_commit ):
130- """Test that list includes person data for tickets with persons."""
131- Person .objects .create (
132- team = self .team ,
133- distinct_ids = ["user-123" , "user@example.com" ],
134- properties = {"email" : "test@example.com" },
135- )
136-
137- response = self .client .get (f"/api/projects/{ self .team .id } /conversations/tickets/" )
138- self .assertEqual (response .status_code , status .HTTP_200_OK )
139- self .assertEqual (response .json ()["count" ], 1 )
140- self .assertIn ("person" , response .json ()["results" ][0 ])
141- self .assertIsNotNone (response .json ()["results" ][0 ]["person" ])
142- self .assertEqual (response .json ()["results" ][0 ]["person" ]["properties" ]["email" ], "test@example.com" )
143- # Verify all distinct_ids are returned, not just the ticket's
144- self .assertCountEqual (
145- response .json ()["results" ][0 ]["person" ]["distinct_ids" ],
146- ["user-123" , "user@example.com" ],
147- )
148-
149- def test_list_tickets_person_null_when_no_person (self , mock_on_commit ):
150- """Test that person is null in list when no person exists."""
151- response = self .client .get (f"/api/projects/{ self .team .id } /conversations/tickets/" )
152- self .assertEqual (response .status_code , status .HTTP_200_OK )
153- self .assertEqual (response .json ()["count" ], 1 )
154- self .assertIn ("person" , response .json ()["results" ][0 ])
155- self .assertIsNone (response .json ()["results" ][0 ]["person" ])
156-
157103 def test_retrieve_ticket_includes_anonymous_traits (self , mock_on_commit ):
158104 """Test that retrieve includes anonymous_traits."""
159105 self .ticket .anonymous_traits = {"name" : "John Doe" , "email" : "john@example.com" }
@@ -177,21 +123,6 @@ def test_list_tickets_includes_anonymous_traits(self, mock_on_commit):
177123 self .assertEqual (response .json ()["results" ][0 ]["anonymous_traits" ]["name" ], "Jane Doe" )
178124 self .assertEqual (response .json ()["results" ][0 ]["anonymous_traits" ]["company" ], "ACME" )
179125
180- def test_person_data_scoped_to_team (self , mock_on_commit ):
181- """Test that person lookup is scoped to team (no cross-team leakage)."""
182- # Create person in different team
183- other_team = self .organization .teams .create (name = "Other Team" )
184- Person .objects .create (
185- team = other_team ,
186- distinct_ids = ["user-123" ], # Same distinct_id as ticket
187- properties = {"email" : "other@example.com" },
188- )
189-
190- response = self .client .get (f"/api/projects/{ self .team .id } /conversations/tickets/{ self .ticket .id } /" )
191- self .assertEqual (response .status_code , status .HTTP_200_OK )
192- # Should be null because person is in different team
193- self .assertIsNone (response .json ()["person" ])
194-
195126 def test_update_sla_due_at (self , mock_on_commit ):
196127 sla_time = timezone .now () + timedelta (hours = 5 )
197128 response = self .client .patch (
@@ -1100,3 +1031,79 @@ def test_auto_increments_ticket_number(self):
11001031
11011032 self .assertEqual (ticket1 .ticket_number , 1 )
11021033 self .assertEqual (ticket2 .ticket_number , 2 )
1034+
1035+
1036+ @parameterized_class (("personhog" ,), [(False ,), (True ,)])
1037+ @patch .object (transaction , "on_commit" , side_effect = immediate_on_commit )
1038+ class TestTicketPersonData (PersonhogTestMixin , APIBaseTest ):
1039+ """Tests that ticket person enrichment produces identical results
1040+ via the ORM and personhog paths."""
1041+
1042+ def setUp (self ):
1043+ super ().setUp ()
1044+ self .ticket = Ticket .objects .create_with_number (
1045+ team = self .team ,
1046+ channel_source = Channel .WIDGET ,
1047+ widget_session_id = "test-session-123" ,
1048+ distinct_id = "user-123" ,
1049+ status = Status .NEW ,
1050+ )
1051+
1052+ def test_retrieve_ticket_includes_person_data (self , mock_on_commit ):
1053+ person = self ._seed_person (
1054+ team = self .team ,
1055+ distinct_ids = ["user-123" , "user@example.com" , "another-id" ],
1056+ properties = {"email" : "test@example.com" , "name" : "Test User" },
1057+ )
1058+
1059+ response = self .client .get (f"/api/projects/{ self .team .id } /conversations/tickets/{ self .ticket .id } /" )
1060+
1061+ assert response .status_code == status .HTTP_200_OK
1062+ person_data = response .json ()["person" ]
1063+ assert person_data is not None
1064+ assert person_data ["id" ] == str (person .uuid )
1065+ assert person_data ["properties" ]["email" ] == "test@example.com"
1066+ assert set (person_data ["distinct_ids" ]) == {"user-123" , "user@example.com" , "another-id" }
1067+
1068+ def test_retrieve_ticket_person_null_when_no_person (self , mock_on_commit ):
1069+ response = self .client .get (f"/api/projects/{ self .team .id } /conversations/tickets/{ self .ticket .id } /" )
1070+
1071+ assert response .status_code == status .HTTP_200_OK
1072+ assert response .json ()["person" ] is None
1073+
1074+ def test_list_tickets_includes_person_data (self , mock_on_commit ):
1075+ self ._seed_person (
1076+ team = self .team ,
1077+ distinct_ids = ["user-123" , "user@example.com" ],
1078+ properties = {"email" : "test@example.com" },
1079+ )
1080+
1081+ response = self .client .get (f"/api/projects/{ self .team .id } /conversations/tickets/" )
1082+
1083+ assert response .status_code == status .HTTP_200_OK
1084+ assert response .json ()["count" ] == 1
1085+ person_data = response .json ()["results" ][0 ]["person" ]
1086+ assert person_data is not None
1087+ assert person_data ["properties" ]["email" ] == "test@example.com"
1088+ assert set (person_data ["distinct_ids" ]) == {"user-123" , "user@example.com" }
1089+ self ._assert_personhog_called ("get_persons_by_distinct_ids_in_team" )
1090+
1091+ def test_list_tickets_person_null_when_no_person (self , mock_on_commit ):
1092+ response = self .client .get (f"/api/projects/{ self .team .id } /conversations/tickets/" )
1093+
1094+ assert response .status_code == status .HTTP_200_OK
1095+ assert response .json ()["count" ] == 1
1096+ assert response .json ()["results" ][0 ]["person" ] is None
1097+
1098+ def test_person_data_scoped_to_team (self , mock_on_commit ):
1099+ other_team = self .organization .teams .create (name = "Other Team" )
1100+ self ._seed_person (
1101+ team = other_team ,
1102+ distinct_ids = ["user-123" ],
1103+ properties = {"email" : "other@example.com" },
1104+ )
1105+
1106+ response = self .client .get (f"/api/projects/{ self .team .id } /conversations/tickets/{ self .ticket .id } /" )
1107+
1108+ assert response .status_code == status .HTTP_200_OK
1109+ assert response .json ()["person" ] is None
0 commit comments