77from labthings .server .labthing import LabThing
88from labthings .server .view import View
99
10+ from werkzeug .test import EnvironBuilder
1011from flask .testing import FlaskClient
1112
1213
14+ class FakeWebsocket :
15+ def __init__ (self , message : str , recieve_once = True ):
16+ self .message = message
17+ self .responses = []
18+ self .closed = False
19+ self .recieve_once = recieve_once
20+
21+ # I mean screw whoever is responsible for this having to be a thing...
22+ self .receive = self .recieve
23+
24+ def recieve (self ):
25+ # Get message
26+ message_to_send = self .message
27+ # If only sending a message to the server once
28+ if self .recieve_once :
29+ # Clear our message
30+ self .message = None
31+ return message_to_send
32+
33+ @property
34+ def response (self ):
35+ return self .responses [- 1 ]
36+
37+ def send (self , response ):
38+ self .responses .append (response )
39+ self .closed = True
40+ return response
41+
42+
1343class JsonClient (FlaskClient ):
1444 def open (self , * args , ** kwargs ):
1545 kwargs .setdefault (
@@ -20,6 +50,57 @@ def open(self, *args, **kwargs):
2050 return super ().open (* args , ** kwargs )
2151
2252
53+ class SocketClient (FlaskClient ):
54+ def __init__ (self , app , response_wrapper , * args , ** kwargs ):
55+ super ().__init__ (app , response_wrapper , * args , ** kwargs )
56+ self .app = app
57+ self .response_wrapper = response_wrapper
58+ self .socket = FakeWebsocket (message = None )
59+ self .environ_base = {
60+ "HTTP_UPGRADE" : "websocket" ,
61+ "wsgi.websocket" : self .socket ,
62+ }
63+
64+ def connect (self , * args , message = None , ** kwargs ):
65+ kwargs .setdefault ("environ_overrides" , {})[
66+ "flask._preserve_context"
67+ ] = self .preserve_context
68+ kwargs .setdefault ("environ_base" , self .environ_base )
69+ builder = EnvironBuilder (* args , ** kwargs )
70+
71+ try :
72+ environ = builder .get_environ ()
73+ finally :
74+ builder .close ()
75+
76+ self .socket .message = message
77+
78+ with self .app .app_context ():
79+ run_wsgi_app (self .app , environ )
80+
81+ # Once the connection has been closed, return responses
82+ return self .socket .responses
83+
84+
85+ def run_wsgi_app (app , environ , buffered = False ):
86+ response = []
87+ buffer = []
88+
89+ def start_response (status , headers , exc_info = None ):
90+ if exc_info :
91+ try :
92+ raise exc_info [1 ].with_traceback (exc_info [2 ])
93+ finally :
94+ exc_info = None
95+ response [:] = [status , headers ]
96+ return buffer .append
97+
98+ # Return value from the wsgi_app call
99+ # In the case of our SocketMiddleware, will return []
100+ app_rv = app (environ , start_response )
101+ return app_rv
102+
103+
23104@pytest .fixture
24105def empty_view_cls ():
25106 class EmptyViewClass (View ):
@@ -146,6 +227,12 @@ def client(app):
146227 return app .test_client ()
147228
148229
230+ @pytest .fixture
231+ def ws_client (app ):
232+ app .test_client_class = SocketClient
233+ return app .test_client ()
234+
235+
149236@pytest .fixture
150237def thing_client (thing ):
151238 thing .app .test_client_class = JsonClient
@@ -167,28 +254,6 @@ def extensions_path(app):
167254 return os .path .join (os .path .dirname (__file__ ), "extensions" )
168255
169256
170- class FakeWebsocket :
171- def __init__ (self , message : str , recieve_once = True ):
172- self .message = message
173- self .response = None
174- self .closed = False
175- self .recieve_once = recieve_once
176-
177- def receive (self ):
178- # Get message
179- message_to_send = self .message
180- # If only sending a message to the server once
181- if self .recieve_once :
182- # Clear our message
183- self .message = None
184- return message_to_send
185-
186- def send (self , response ):
187- self .response = response
188- self .closed = True
189- return response
190-
191-
192257@pytest .fixture
193258def fake_websocket ():
194259 """
0 commit comments