11"""Basic tests for the CherryPy core: request handling."""
22
33import logging
4- import os
5- from unittest import mock
64
5+ from cheroot .test import webtest
76import pytest
87import requests # FIXME: Temporary using it directly, better switch
98
109import cherrypy
11- from cherrypy ._cpcompat import ntou
12- from cherrypy .test import helper , logtest
10+ from cherrypy .test .logtest import LogCase
1311
14- localDir = os .path .dirname (__file__ )
15- access_log = os .path .join (localDir , 'access.log' )
16- error_log = os .path .join (localDir , 'error.log' )
1712
1813# Some unicode strings.
19- tartaros = ntou ( '\u03a4 \u1f71 \u03c1 \u03c4 \u03b1 \u03c1 \u03bf \u03c2 ' , 'escape' )
20- erebos = ntou ( '\u0388 \u03c1 \u03b5 \u03b2 \u03bf \u03c2 .com' , 'escape' )
14+ tartaros = u '\u03a4 \u1f71 \u03c1 \u03c4 \u03b1 \u03c1 \u03bf \u03c2 '
15+ erebos = u '\u0388 \u03c1 \u03b5 \u03b2 \u03bf \u03c2 .com'
2116
2217
2318@pytest .fixture
24- def server ():
25- setup_server ()
19+ def access_log_file (tmp_path_factory ):
20+ return tmp_path_factory .mktemp ('logs' ) / 'access.log'
21+
22+
23+ @pytest .fixture
24+ def error_log_file (tmp_path_factory ):
25+ return tmp_path_factory .mktemp ('logs' ) / 'access.log'
26+
27+
28+ @pytest .fixture
29+ def server (configure_server ):
2630 cherrypy .engine .start ()
31+ cherrypy .engine .wait (cherrypy .engine .states .STARTED )
2732
2833 yield
2934
@@ -32,14 +37,16 @@ def server():
3237
3338def shutdown_server ():
3439 cherrypy .engine .exit ()
40+ cherrypy .engine .block ()
3541
3642 servers_copy = list (getattr (cherrypy , 'servers' , {}).items ())
3743 for name , server in servers_copy :
3844 server .unsubscribe ()
3945 del cherrypy .servers [name ]
4046
4147
42- def setup_server ():
48+ @pytest .fixture
49+ def configure_server (access_log_file , error_log_file ):
4350 class Root :
4451
4552 @cherrypy .expose
@@ -79,130 +86,200 @@ def error(self):
7986
8087 root = Root ()
8188
89+ cherrypy .config .reset ()
90+ cherrypy .config .update ({
91+ 'server.socket_host' : webtest .WebCase .HOST ,
92+ 'server.socket_port' : webtest .WebCase .PORT ,
93+ 'server.protocol_version' : webtest .WebCase .PROTOCOL ,
94+ 'environment' : 'test_suite' ,
95+ })
8296 cherrypy .config .update ({
83- 'log.error_file' : error_log ,
84- 'log.access_file' : access_log ,
97+ 'log.error_file' : str ( error_log_file ) ,
98+ 'log.access_file' : str ( access_log_file ) ,
8599 })
86100 cherrypy .tree .mount (root )
87101
88102
89- class AccessLogTests (helper .CPWebCase , logtest .LogCase ):
90- setup_server = staticmethod (setup_server )
91-
92- logfile = access_log
93-
94- def testNormalReturn (self ):
95- self .markLog ()
96- self .getPage ('/as_string' ,
97- headers = [('Referer' , 'http://www.cherrypy.org/' ),
98- ('User-Agent' , 'Mozilla/5.0' )])
99- self .assertBody ('content' )
100- self .assertStatus (200 )
101-
102- intro = '%s - - [' % self .interface ()
103-
104- self .assertLog (- 1 , intro )
105-
106- if [k for k , v in self .headers if k .lower () == 'content-length' ]:
107- self .assertLog (- 1 , '] "GET %s/as_string HTTP/1.1" 200 7 '
108- '"http://www.cherrypy.org/" "Mozilla/5.0"'
109- % self .prefix ())
110- else :
111- self .assertLog (- 1 , '] "GET %s/as_string HTTP/1.1" 200 - '
112- '"http://www.cherrypy.org/" "Mozilla/5.0"'
113- % self .prefix ())
103+ @pytest .fixture
104+ def log_tracker (access_log_file ):
105+ class LogTracker (LogCase ):
106+ logfile = str (access_log_file )
107+ return LogTracker ()
108+
109+
110+ def test_normal_return (log_tracker , server ):
111+ log_tracker .markLog ()
112+ host = webtest .interface (webtest .WebCase .HOST )
113+ port = webtest .WebCase .PORT
114+ resp = requests .get (
115+ 'http://%s:%s/as_string' % (host , port ),
116+ headers = {
117+ 'Referer' : 'http://www.cherrypy.org/' ,
118+ 'User-Agent' : 'Mozilla/5.0' ,
119+ },
120+ )
121+ expected_body = 'content'
122+ assert resp .text == expected_body
123+ assert resp .status_code == 200
124+
125+ intro = '%s - - [' % host
126+
127+ log_tracker .assertLog (- 1 , intro )
128+
129+ content_length = len (expected_body )
130+ if not any (
131+ k for k , v in resp .headers .items ()
132+ if k .lower () == 'content-length'
133+ ):
134+ content_length = '-'
135+
136+ log_tracker .assertLog (
137+ - 1 ,
138+ '] "GET /as_string HTTP/1.1" 200 %s '
139+ '"http://www.cherrypy.org/" "Mozilla/5.0"'
140+ % content_length ,
141+ )
114142
115- def testNormalYield (self ):
116- self .markLog ()
117- self .getPage ('/as_yield' )
118- self .assertBody ('content' )
119- self .assertStatus (200 )
120143
121- intro = '%s - - [' % self .interface ()
144+ def test_normal_yield (log_tracker , server ):
145+ log_tracker .markLog ()
146+ host = webtest .interface (webtest .WebCase .HOST )
147+ port = webtest .WebCase .PORT
148+ resp = requests .get (
149+ 'http://%s:%s/as_yield' % (host , port ),
150+ headers = {
151+ 'User-Agent' : '' ,
152+ },
153+ )
154+ expected_body = 'content'
155+ assert resp .text == expected_body
156+ assert resp .status_code == 200
157+
158+ intro = '%s - - [' % host
159+
160+ log_tracker .assertLog (- 1 , intro )
161+ content_length = len (expected_body )
162+ if not any (
163+ k for k , v in resp .headers .items ()
164+ if k .lower () == 'content-length'
165+ ):
166+ content_length = '-'
167+
168+ log_tracker .assertLog (
169+ - 1 ,
170+ '] "GET /as_yield HTTP/1.1" 200 %s "" ""'
171+ % content_length ,
172+ )
122173
123- self .assertLog (- 1 , intro )
124- if [k for k , v in self .headers if k .lower () == 'content-length' ]:
125- self .assertLog (- 1 , '] "GET %s/as_yield HTTP/1.1" 200 7 "" ""' %
126- self .prefix ())
127- else :
128- self .assertLog (- 1 , '] "GET %s/as_yield HTTP/1.1" 200 - "" ""'
129- % self .prefix ())
130174
131- @mock .patch (
175+ def test_custom_log_format (log_tracker , monkeypatch , server ):
176+ """Test a customized access_log_format string, which is a
177+ feature of _cplogging.LogManager.access()."""
178+ monkeypatch .setattr (
132179 'cherrypy._cplogging.LogManager.access_log_format' ,
133180 '{h} {l} {u} {t} "{r}" {s} {b} "{f}" "{a}" {o}' ,
134181 )
135- def testCustomLogFormat (self ):
136- """Test a customized access_log_format string, which is a
137- feature of _cplogging.LogManager.access()."""
138- self .markLog ()
139- self .getPage ('/as_string' , headers = [('Referer' , 'REFERER' ),
140- ('User-Agent' , 'USERAGENT' ),
141- ('Host' , 'HOST' )])
142- self .assertLog (- 1 , '%s - - [' % self .interface ())
143- self .assertLog (- 1 , '] "GET /as_string HTTP/1.1" '
144- '200 7 "REFERER" "USERAGENT" HOST' )
145-
146- @mock .patch (
182+ log_tracker .markLog ()
183+ host = webtest .interface (webtest .WebCase .HOST )
184+ port = webtest .WebCase .PORT
185+ requests .get (
186+ 'http://%s:%s/as_string' % (host , port ),
187+ headers = {
188+ 'Referer' : 'REFERER' ,
189+ 'User-Agent' : 'USERAGENT' ,
190+ 'Host' : 'HOST' ,
191+ },
192+ )
193+ log_tracker .assertLog (- 1 , '%s - - [' % host )
194+ log_tracker .assertLog (
195+ - 1 ,
196+ '] "GET /as_string HTTP/1.1" '
197+ '200 7 "REFERER" "USERAGENT" HOST' ,
198+ )
199+
200+
201+ def test_timez_log_format (log_tracker , monkeypatch , server ):
202+ """Test a customized access_log_format string, which is a
203+ feature of _cplogging.LogManager.access()."""
204+ monkeypatch .setattr (
147205 'cherrypy._cplogging.LogManager.access_log_format' ,
148206 '{h} {l} {u} {z} "{r}" {s} {b} "{f}" "{a}" {o}' ,
149207 )
150- def testTimezLogFormat (self ):
151- """Test a customized access_log_format string, which is a
152- feature of _cplogging.LogManager.access()."""
153- self .markLog ()
154-
155- expected_time = str (cherrypy ._cplogging .LazyRfc3339UtcTime ())
156- with mock .patch (
157- 'cherrypy._cplogging.LazyRfc3339UtcTime' ,
158- lambda : expected_time ):
159- self .getPage ('/as_string' , headers = [('Referer' , 'REFERER' ),
160- ('User-Agent' , 'USERAGENT' ),
161- ('Host' , 'HOST' )])
162-
163- self .assertLog (- 1 , '%s - - ' % self .interface ())
164- self .assertLog (- 1 , expected_time )
165- self .assertLog (- 1 , ' "GET /as_string HTTP/1.1" '
166- '200 7 "REFERER" "USERAGENT" HOST' )
167-
168- @mock .patch (
208+ log_tracker .markLog ()
209+
210+ expected_time = str (cherrypy ._cplogging .LazyRfc3339UtcTime ())
211+ monkeypatch .setattr (
212+ 'cherrypy._cplogging.LazyRfc3339UtcTime' ,
213+ lambda : expected_time ,
214+ )
215+ host = webtest .interface (webtest .WebCase .HOST )
216+ port = webtest .WebCase .PORT
217+ requests .get (
218+ 'http://%s:%s/as_string' % (host , port ),
219+ headers = {
220+ 'Referer' : 'REFERER' ,
221+ 'User-Agent' : 'USERAGENT' ,
222+ 'Host' : 'HOST' ,
223+ },
224+ )
225+
226+ log_tracker .assertLog (- 1 , '%s - - ' % host )
227+ log_tracker .assertLog (- 1 , expected_time )
228+ log_tracker .assertLog (
229+ - 1 ,
230+ ' "GET /as_string HTTP/1.1" '
231+ '200 7 "REFERER" "USERAGENT" HOST' ,
232+ )
233+
234+
235+ def test_UUIDv4_parameter_log_format (log_tracker , monkeypatch , server ):
236+ """Test rendering of UUID4 within access log."""
237+ monkeypatch .setattr (
169238 'cherrypy._cplogging.LogManager.access_log_format' ,
170239 '{i}' ,
171240 )
172- def testUUIDv4ParameterLogFormat (self ):
173- """Test rendering of UUID4 within access log."""
174- self .markLog ()
175- self .getPage ('/as_string' )
176- self .assertValidUUIDv4 ()
177-
178- def testEscapedOutput (self ):
179- # Test unicode in access log pieces.
180- self .markLog ()
181- self .getPage ('/uni_code' )
182- self .assertStatus (200 )
183- # The repr of a bytestring includes a b'' prefix
184- self .assertLog (- 1 , repr (tartaros .encode ('utf8' ))[2 :- 1 ])
185- # Test the erebos value. Included inline for your enlightenment.
186- # Note the 'r' prefix--those backslashes are literals.
187- self .assertLog (- 1 , r'\xce\x88\xcf\x81\xce\xb5\xce\xb2\xce\xbf\xcf\x82' )
188-
189- # Test backslashes in output.
190- self .markLog ()
191- self .getPage ('/slashes' )
192- self .assertStatus (200 )
193- self .assertLog (- 1 , b'"GET /slashed\\ path HTTP/1.1"' )
194-
195- # Test whitespace in output.
196- self .markLog ()
197- self .getPage ('/whitespace' )
198- self .assertStatus (200 )
199- # Again, note the 'r' prefix.
200- self .assertLog (- 1 , r'"Browzuh (1.0\r\n\t\t.3)"' )
241+ log_tracker .markLog ()
242+ host = webtest .interface (webtest .WebCase .HOST )
243+ port = webtest .WebCase .PORT
244+ requests .get ('http://%s:%s/as_string' % (host , port ))
245+ log_tracker .assertValidUUIDv4 ()
246+
247+
248+ def test_escaped_output (log_tracker , server ):
249+ # Test unicode in access log pieces.
250+ log_tracker .markLog ()
251+ host = webtest .interface (webtest .WebCase .HOST )
252+ port = webtest .WebCase .PORT
253+ resp = requests .get ('http://%s:%s/uni_code' % (host , port ))
254+ assert resp .status_code == 200
255+ # The repr of a bytestring includes a b'' prefix
256+ log_tracker .assertLog (- 1 , repr (tartaros .encode ('utf8' ))[2 :- 1 ])
257+ # Test the erebos value. Included inline for your enlightenment.
258+ # Note the 'r' prefix--those backslashes are literals.
259+ log_tracker .assertLog (
260+ - 1 ,
261+ r'\xce\x88\xcf\x81\xce\xb5\xce\xb2\xce\xbf\xcf\x82' ,
262+ )
263+
264+ # Test backslashes in output.
265+ log_tracker .markLog ()
266+ resp = requests .get ('http://%s:%s/slashes' % (host , port ))
267+ assert resp .status_code == 200
268+ log_tracker .assertLog (- 1 , b'"GET /slashed\\ path HTTP/1.1"' )
269+
270+ # Test whitespace in output.
271+ log_tracker .markLog ()
272+ resp = requests .get ('http://%s:%s/whitespace' % (host , port ))
273+ assert resp .status_code == 200
274+ # Again, note the 'r' prefix.
275+ log_tracker .assertLog (- 1 , r'"Browzuh (1.0\r\n\t\t.3)"' )
201276
202277
203278def test_tracebacks (server , caplog ):
279+ host = webtest .interface (webtest .WebCase .HOST )
280+ port = webtest .WebCase .PORT
204281 with caplog .at_level (logging .ERROR , logger = 'cherrypy.error' ):
205- resp = requests .get ('http://127.0.0.1:54583 /error' )
282+ resp = requests .get ('http://%s:%s /error' % ( host , port ) )
206283
207284 rec = caplog .records [0 ]
208285 exc_cls , exc_msg = rec .exc_info [0 ], rec .message
0 commit comments