1+ import unittest
2+ from unittest .mock import MagicMock , patch , call
3+ import requests
4+ import zulip
5+
6+
7+ def make_client () -> zulip .Client :
8+ with patch .object (zulip .Client , "call_endpoint" ) as mock_call :
9+ mock_call .return_value = {
10+ "result" : "success" ,
11+ "zulip_version" : "9.0.0" ,
12+ "zulip_feature_level" : 300 ,
13+ "msg" : "" ,
14+ }
15+ client = zulip .Client (
16+ email = "test@example.com" ,
17+ api_key = "deadbeef" ,
18+ site = "https://testserver" ,
19+ )
20+ return client
21+
22+
23+ class TestStaleConnectionRetry (unittest .TestCase ):
24+ def test_stale_connection_resets_session (self ) -> None :
25+ client = make_client ()
26+
27+ # Simulate a session that has already been used (has_connected = True)
28+ stale_session = MagicMock ()
29+ client .session = stale_session
30+ client .has_connected = True
31+
32+ # First request raises ConnectionError (stale socket),
33+ # second request succeeds
34+ success_response = MagicMock ()
35+ success_response .status_code = 200
36+ success_response .json .return_value = {"result" : "success" , "msg" : "" }
37+
38+ stale_session .request .side_effect = requests .exceptions .ConnectionError ("stale" )
39+
40+ fresh_session = MagicMock ()
41+ fresh_session .request .return_value = success_response
42+
43+ original_ensure_session = zulip .Client .ensure_session
44+
45+ call_count = 0
46+
47+ def mock_ensure_session (self : zulip .Client ) -> None :
48+ nonlocal call_count
49+ call_count += 1
50+ if call_count == 1 :
51+ # First call: leave the stale session in place
52+ self .session = stale_session
53+ else :
54+ # Subsequent calls: provide a fresh session
55+ self .session = fresh_session
56+
57+ with patch .object (zulip .Client , "ensure_session" , mock_ensure_session ):
58+ result = client .do_api_query ({}, "/api/v1/messages" , method = "POST" )
59+
60+ # The stale session should have been closed
61+ stale_session .close .assert_called_once ()
62+
63+ # The result should come from the fresh session
64+ self .assertEqual (result , {"result" : "success" , "msg" : "" })
65+
66+ # ensure_session should have been called twice (once per loop iteration)
67+ self .assertEqual (call_count , 2 )
68+
69+
70+ if __name__ == "__main__" :
71+ unittest .main ()
0 commit comments