Skip to content

Commit ee57350

Browse files
committed
flake8 - remove re.compile from method arg + test + doc
changed 2 methods defined like: def method(..., dre=re.compile(r'...')): moved re.compile to module variables and passed the var name def method(..., dre=var_name): while doing this I found out that a url of .../issue0001 will behave like .../issue1. Who knew. Documented in customizing. Tested same in test_liveserver. Added msg1 as well so I could verify msg0001 worked. Also added some range tests as well.
1 parent 7698b77 commit ee57350

File tree

3 files changed

+170
-8
lines changed

3 files changed

+170
-8
lines changed

doc/customizing.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2232,6 +2232,12 @@ for specific object of issue class with id 1 will look like:
22322232

22332233
8. ``/issue1``
22342234

2235+
.. _strip_zeros:
2236+
2237+
Note that a leading string of 0's will be stripped from the id part of
2238+
the object designator in the URL. E.G. ``/issue001`` is the same as
2239+
``/issue1``. Similarly for ``/file01`` etc. However you should
2240+
generate URL's without the extra zeros.
22352241

22362242
Determining web context
22372243
-----------------------
@@ -2262,6 +2268,7 @@ c. if there is something in the path (as in example 1, "issue"), it
22622268
identifies the tracker class to display.
22632269
d. if the path is an item designator (as in examples 2 and 4, "issue1"
22642270
and "file1"), then we're to display a specific item.
2271+
:ref:`Note. <strip_zeros>`
22652272
e. if the path starts with an item designator and is longer than one
22662273
entry (as in example 5, "file1/kitten.png"), then we're assumed to be
22672274
handling an item of a ``FileClass``, and the extra path information

roundup/cgi/client.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,7 +1179,8 @@ def determine_user(self):
11791179
raise LoginError("Token roles are invalid.")
11801180

11811181
# will be used later to override the get_roles method
1182-
override_get_roles = lambda self: iter_roles(
1182+
# having it defined as truthy allows it to be used.
1183+
override_get_roles = lambda self: iter_roles( # noqa: E731
11831184
','.join(token['roles']))
11841185

11851186
# if user was not set by http authorization, try session lookup
@@ -1632,7 +1633,13 @@ def opendb(self, username):
16321633
# we can no longer use it.
16331634
self.session_api = Session(self)
16341635

1635-
def determine_context(self, dre=re.compile(r'([^\d]+)0*(\d+)')):
1636+
# match designator in URL stripping leading 0's. So:
1637+
# https://issues.roundup-tracker.org/issue002551190 is the same as
1638+
# https://issues.roundup-tracker.org/issue2551190
1639+
# Note: id's are strings not numbers so "02" != "2" but 02 == 2
1640+
dre_url = re.compile(r'([^\d]+)0*(\d+)')
1641+
1642+
def determine_context(self, dre=dre_url):
16361643
"""Determine the context of this page from the URL:
16371644
16381645
The URL path after the instance identifier is examined. The path
@@ -1743,7 +1750,11 @@ def determine_context(self, dre=re.compile(r'([^\d]+)0*(\d+)')):
17431750
if template_override is not None:
17441751
self.template = template_override
17451752

1746-
def serve_file(self, designator, dre=re.compile(r'([^\d]+)(\d+)')):
1753+
# re for splitting designator, see also dre_url above this one
1754+
# doesn't strip leading 0's from the id. Why not??
1755+
dre = re.compile(r'([^\d]+)(\d+)')
1756+
1757+
def serve_file(self, designator, dre=dre):
17471758
""" Serve the file from the content property of the designated item.
17481759
"""
17491760
m = dre.match(str(designator))
@@ -2514,6 +2525,8 @@ def handle_range_header(self, length, etag):
25142525
first = self.http_strip(pos[0])
25152526
last = self.http_strip(pos[1])
25162527
# We do not handle suffix ranges.
2528+
# Note this also captures atempts to make first
2529+
# element of range a negative number.
25172530
if not first:
25182531
return None
25192532
# Convert the first and last positions to integers.
@@ -2527,6 +2540,8 @@ def handle_range_header(self, length, etag):
25272540
# The positions could not be parsed as integers.
25282541
return None
25292542
# Check that the range makes sense.
2543+
# Note, if range is -1-10, first = '', so this code will never
2544+
# be reached. if range = 1--10, this code is reached.
25302545
if (first < 0 or last < 0 or last < first):
25312546
return None
25322547
if last >= length:

test/test_liveserver.py

Lines changed: 145 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import shutil, errno, pytest, json, gzip, mimetypes, os, re
22

3+
from roundup import date as rdate
34
from roundup import i18n
45
from roundup import password
56
from roundup.anypy.strings import b2s
@@ -94,6 +95,11 @@ def setup_class(cls):
9495
# also used for text searching.
9596
result = cls.db.issue.create(title="foo bar RESULT")
9697

98+
# add a message to allow retrieval
99+
result = cls.db.msg.create(author = "1",
100+
content = "a message foo bar RESULT",
101+
date=rdate.Date(),
102+
messageid="test-msg-id")
97103
cls.db.commit()
98104
cls.db.close()
99105

@@ -191,12 +197,14 @@ def test_start_in_german(self):
191197
self.assertTrue(b'dauerhaft anmelden?' in f.content)
192198

193199
def test_byte_Ranges(self):
194-
""" Roundup only handles one simple two number range.
200+
""" Roundup only handles one simple two number range, or
201+
a single number to start from:
195202
Range: 10-20
203+
Range: 10-
196204
197-
The following are not supported.
205+
The following is not supported.
198206
Range: 10-20, 25-30
199-
Range: 10-
207+
Range: -10
200208
201209
Also If-Range only supports strong etags not dates or weak etags.
202210
@@ -220,7 +228,27 @@ def test_byte_Ranges(self):
220228
self.assertEqual(f.headers['content-range'],
221229
"bytes 0-10/%s"%expected_length)
222230

231+
# get bytes 11-21 unconditionally (0 index really??)
232+
hdrs = {"Range": "bytes=10-20"}
233+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
234+
self.assertEqual(f.status_code, 206)
235+
self.assertEqual(f.content, b"ge styles *")
236+
# compression disabled for length < 100, so we can use 11 here
237+
self.assertEqual(f.headers['content-length'], '11')
238+
self.assertEqual(f.headers['content-range'],
239+
"bytes 10-20/%s"%expected_length)
240+
241+
# get all bytest starting from 11
242+
hdrs = {"Range": "bytes=11-"}
243+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
244+
self.assertEqual(f.status_code, 206)
245+
self.assertEqual(f.headers['content-range'],
246+
"bytes 11-%s/%s"%(int(expected_length) - 1,
247+
expected_length))
248+
self.assertIn("SHA:", f.content) # detect sha sum at end of file
249+
223250
# conditional request 11 bytes since etag matches 206 code
251+
hdrs = {"Range": "bytes=0-10"}
224252
hdrs['If-Range'] = etag
225253
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
226254
self.assertEqual(f.status_code, 206)
@@ -262,7 +290,98 @@ def test_byte_Ranges(self):
262290
self.assertEqual(f.status_code, 416)
263291
self.assertEqual(f.headers['content-range'],
264292
"bytes */%s"%expected_length)
265-
293+
294+
# invalid range multiple ranges
295+
hdrs['Range'] = "bytes=0-10, 20-45"
296+
print(hdrs)
297+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
298+
self.assertEqual(f.status_code, 200)
299+
self.assertNotIn('content-range', f.headers,
300+
'content-range should not be present')
301+
self.assertIn("SHA:", f.content) # detect sha sum at end of file
302+
303+
# invalid range is single number not number followed by -
304+
hdrs['Range'] = "bytes=1"
305+
print(hdrs)
306+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
307+
self.assertEqual(f.status_code, 200)
308+
self.assertNotIn('content-range', f.headers,
309+
'content-range should not be present')
310+
self.assertIn("SHA:", f.content) # detect sha sum at end of file
311+
312+
# range is invalid first number not a number
313+
hdrs['Range'] = "bytes=boom-99" # bad first value
314+
print(hdrs)
315+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
316+
self.assertEqual(f.status_code, 200)
317+
self.assertNotIn('content-range', f.headers,
318+
'content-range should not be present')
319+
self.assertIn("SHA:", f.content) # detect sha sum at end of file
320+
321+
# range is invalid last number not a number
322+
hdrs['Range'] = "bytes=1-boom" # bad last value
323+
print(hdrs)
324+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
325+
self.assertEqual(f.status_code, 200)
326+
self.assertNotIn('content-range', f.headers,
327+
'content-range should not be present')
328+
self.assertIn("SHA:", f.content) # detect sha sum at end of file
329+
330+
# range is invalid first position empty
331+
hdrs['Range'] = "bytes=-11" # missing first value
332+
print(hdrs)
333+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
334+
self.assertEqual(f.status_code, 200)
335+
self.assertNotIn('content-range', f.headers,
336+
'content-range should not be present')
337+
self.assertIn("SHA:", f.content) # detect sha sum at end of file
338+
339+
# range is invalid #2 < #1
340+
hdrs['Range'] = "bytes=11-1" # inverted range
341+
print(hdrs)
342+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
343+
self.assertEqual(f.status_code, 200)
344+
self.assertNotIn('content-range', f.headers,
345+
'content-range should not be present')
346+
self.assertIn("SHA:", f.content) # detect sha sum at end of file
347+
348+
# range is invalid negative first number
349+
hdrs['Range'] = "bytes=-1-11" # negative first number
350+
print(hdrs)
351+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
352+
self.assertEqual(f.status_code, 200)
353+
self.assertNotIn('content-range', f.headers,
354+
'content-range should not be present')
355+
self.assertIn("SHA:", f.content) # detect sha sum at end of file
356+
357+
# range is invalid negative second number
358+
hdrs['Range'] = "bytes=1--11" # negative second number
359+
print(hdrs)
360+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
361+
self.assertEqual(f.status_code, 200)
362+
self.assertNotIn('content-range', f.headers,
363+
'content-range should not be present')
364+
self.assertIn("SHA:", f.content) # detect sha sum at end of file
365+
366+
# range is unsupported units
367+
hdrs['Range'] = "badunits=1-11"
368+
print(hdrs)
369+
f = requests.get(self.url_base() + "/@@file/style.css", headers=hdrs)
370+
self.assertEqual(f.status_code, 200)
371+
self.assertNotIn('content-range', f.headers,
372+
'content-range should not be present')
373+
self.assertIn("SHA:", f.content) # detect sha sum at end of file
374+
375+
376+
# valid range, invalid file
377+
hdrs['Range'] = "bytes=0-11"
378+
print(hdrs)
379+
f = requests.get(self.url_base() + "/@@file/style_nope.css",
380+
headers=hdrs)
381+
self.assertEqual(f.status_code, 404)
382+
self.assertNotIn('content-range', f.headers,
383+
'content-range should not be present')
384+
266385
def test_rest_preflight_collection(self):
267386
# no auth for rest csrf preflight
268387
f = requests.options(self.url_base() + '/rest/data/user',
@@ -562,7 +681,22 @@ def test_ims(self):
562681

563682

564683
def test_load_issue1(self):
565-
f = requests.get(self.url_base() + '/issue1>',
684+
import pdb; pdb.set_trace()
685+
for tail in [
686+
'/issue1', # normal url
687+
'/issue00001', # leading 0's should be stripped from id
688+
'/issue1>' # surprise this works too, should it??
689+
]:
690+
f = requests.get(self.url_base() + tail,
691+
headers = { 'Accept-Encoding': 'gzip',
692+
'Accept': '*/*'})
693+
694+
self.assertIn(b'foo bar RESULT', f.content)
695+
self.assertEqual(f.status_code, 200)
696+
697+
def test_load_msg1(self):
698+
# leading 0's should be stripped from id
699+
f = requests.get(self.url_base() + '/msg0001',
566700
headers = { 'Accept-Encoding': 'gzip',
567701
'Accept': '*/*'})
568702

@@ -1173,6 +1307,12 @@ def setup_class(cls):
11731307

11741308
result = cls.db.issue.create(title="foo bar RESULT")
11751309

1310+
# add a message to allow retrieval
1311+
result = cls.db.msg.create(author = "1",
1312+
content = "a message foo bar RESULT",
1313+
date=rdate.Date(),
1314+
messageid="test-msg-id")
1315+
11761316
cls.db.commit()
11771317
cls.db.close()
11781318

0 commit comments

Comments
 (0)