Total Complexity | 40 |
Total Lines | 261 |
Duplicated Lines | 0 % |
Complex classes like browsepy.tests.TestApp often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | #!/usr/bin/env python |
||
99 | class TestApp(unittest.TestCase): |
||
100 | module = browsepy |
||
101 | list_page_class = ListPage |
||
102 | confirm_page_class = ConfirmPage |
||
103 | page_exceptions = { |
||
104 | 404: Page404Exception, |
||
105 | None: PageException |
||
106 | } |
||
107 | |||
108 | def setUp(self): |
||
109 | self.app = self.module.app |
||
110 | self.base = tempfile.mkdtemp() |
||
111 | self.start = os.path.join(self.base, 'start') |
||
112 | self.remove = os.path.join(self.base, 'remove') |
||
113 | self.upload = os.path.join(self.base, 'upload') |
||
114 | |||
115 | os.mkdir(self.start) |
||
116 | os.mkdir(self.remove) |
||
117 | os.mkdir(self.upload) |
||
118 | |||
119 | open(os.path.join(self.start, 'testfile.txt'), 'w').close() |
||
120 | open(os.path.join(self.remove, 'testfile.txt'), 'w').close() |
||
121 | |||
122 | self.app.config.update( |
||
123 | directory_base = self.base, |
||
124 | directory_start = self.start, |
||
125 | directory_remove = self.remove, |
||
126 | directory_upload = self.upload, |
||
127 | SERVER_NAME = 'test', |
||
128 | ) |
||
129 | |||
130 | self.base_directories = [ |
||
131 | self.url_for('browse', path='remove'), |
||
132 | self.url_for('browse', path='start'), |
||
133 | self.url_for('browse', path='upload'), |
||
134 | ] |
||
135 | self.start_files = [self.url_for('open', path='start/testfile.txt')] |
||
136 | self.remove_files = [self.url_for('open', path='remove/testfile.txt')] |
||
137 | self.upload_files = [] |
||
138 | |||
139 | def clear(self, path): |
||
140 | assert path.startswith(self.base + os.sep), 'Cannot clear directories out of base' |
||
141 | |||
142 | for sub in os.listdir(path): |
||
143 | sub = os.path.join(path, sub) |
||
144 | if os.path.isdir(sub): |
||
145 | shutil.rmtree(sub) |
||
146 | else: |
||
147 | os.remove(sub) |
||
148 | |||
149 | def tearDown(self): |
||
150 | shutil.rmtree(self.base) |
||
151 | |||
152 | def get(self, endpoint, **kwargs): |
||
153 | if endpoint in ('index', 'browse'): |
||
154 | page_class = self.list_page_class |
||
155 | elif endpoint == 'remove': |
||
156 | page_class = self.confirm_page_class |
||
157 | else: |
||
158 | page_class = None |
||
159 | |||
160 | with self.app.test_client() as client: |
||
161 | response = client.get(self.url_for(endpoint, **kwargs)) |
||
162 | if response.status_code != 200: |
||
163 | raise self.page_exceptions.get(response.status_code, self.page_exceptions[None])(response.status_code) |
||
164 | result = response.data if page_class is None else page_class.from_source(response.data) |
||
165 | response.close() |
||
166 | return result |
||
167 | |||
168 | def post(self, endpoint, **kwargs): |
||
169 | data = kwargs.pop('data') if 'data' in kwargs else {} |
||
170 | with self.app.test_client() as client: |
||
171 | response = client.post(self.url_for(endpoint, **kwargs), data=data, follow_redirects=True) |
||
172 | if response.status_code != 200: |
||
173 | raise self.page_exceptions.get(response.status_code, self.page_exceptions[None])(response.status_code) |
||
174 | return self.list_page_class.from_source(response.data) |
||
175 | |||
176 | def url_for(self, endpoint, **kwargs): |
||
177 | with self.app.app_context(): |
||
178 | return flask.url_for(endpoint, _external=False, **kwargs) |
||
179 | |||
180 | def test_index(self): |
||
181 | page = self.get('index') |
||
182 | self.assertEqual(page.path, '%s/start' % os.path.basename(self.base)) |
||
183 | |||
184 | self.app.config['directory_start'] = os.path.join(self.base, '..') |
||
185 | |||
186 | self.assertRaises( |
||
187 | Page404Exception, |
||
188 | self.get, 'index' |
||
189 | ) |
||
190 | |||
191 | self.app.config['directory_start'] = self.start |
||
192 | |||
193 | def test_browse(self): |
||
194 | basename = os.path.basename(self.base) |
||
195 | page = self.get('browse') |
||
196 | self.assertEqual(page.path, basename) |
||
197 | self.assertEqual(page.directories, self.base_directories) |
||
198 | self.assertFalse(page.removable) |
||
199 | self.assertFalse(page.upload) |
||
200 | |||
201 | page = self.get('browse', path='start') |
||
202 | self.assertEqual(page.path, '%s/start' % basename) |
||
203 | self.assertEqual(page.files, self.start_files) |
||
204 | self.assertFalse(page.removable) |
||
205 | self.assertFalse(page.upload) |
||
206 | |||
207 | page = self.get('browse', path='remove') |
||
208 | self.assertEqual(page.path, '%s/remove' % basename) |
||
209 | self.assertEqual(page.files, self.remove_files) |
||
210 | self.assertTrue(page.removable) |
||
211 | self.assertFalse(page.upload) |
||
212 | |||
213 | page = self.get('browse', path='upload') |
||
214 | self.assertEqual(page.path, '%s/upload' % basename) |
||
215 | self.assertEqual(page.files, self.upload_files) |
||
216 | self.assertFalse(page.removable) |
||
217 | self.assertTrue(page.upload) |
||
218 | |||
219 | self.assertRaises( |
||
220 | Page404Exception, |
||
221 | self.get, 'browse', path='..' |
||
222 | ) |
||
223 | |||
224 | def test_open(self): |
||
225 | content = b'hello world' |
||
226 | with open(os.path.join(self.start, 'testfile3.txt'), 'wb') as f: |
||
227 | f.write(content) |
||
228 | |||
229 | data = self.get('open', path='start/testfile3.txt') |
||
230 | self.assertEqual(data, content) |
||
231 | |||
232 | self.assertRaises( |
||
233 | Page404Exception, |
||
234 | self.get, 'open', path='../shall_not_pass.txt' |
||
235 | ) |
||
236 | |||
237 | def test_remove(self): |
||
238 | open(os.path.join(self.remove, 'testfile2.txt'), 'w').close() |
||
239 | page = self.get('remove', path='remove/testfile2.txt') |
||
240 | self.assertEqual(page.name, 'testfile2.txt') |
||
241 | self.assertEqual(page.path, 'remove/testfile2.txt') |
||
242 | self.assertEqual(page.back, self.url_for('browse', path='remove')) |
||
243 | |||
244 | basename = os.path.basename(self.base) |
||
245 | page = self.post('remove', path='remove/testfile2.txt') |
||
246 | self.assertEqual(page.path, '%s/remove' % basename) |
||
247 | self.assertEqual(page.files, self.remove_files) |
||
248 | |||
249 | os.mkdir(os.path.join(self.remove, 'directory')) |
||
250 | page = self.post('remove', path='remove/directory') |
||
251 | self.assertEqual(page.path, '%s/remove' % basename) |
||
252 | self.assertEqual(page.files, self.remove_files) |
||
253 | |||
254 | self.assertRaises( |
||
255 | Page404Exception, |
||
256 | self.get, 'remove', path='start/testfile.txt' |
||
257 | ) |
||
258 | |||
259 | self.assertRaises( |
||
260 | Page404Exception, |
||
261 | self.post, 'remove', path='start/testfile.txt' |
||
262 | ) |
||
263 | |||
264 | self.app.config['directory_remove'] = None |
||
265 | |||
266 | self.assertRaises( |
||
267 | Page404Exception, |
||
268 | self.get, 'remove', path='remove/testfile.txt' |
||
269 | ) |
||
270 | |||
271 | self.app.config['directory_remove'] = self.remove |
||
272 | |||
273 | self.assertRaises( |
||
274 | Page404Exception, |
||
275 | self.get, 'remove', path='../shall_not_pass.txt' |
||
276 | ) |
||
277 | |||
278 | def test_download_file(self): |
||
279 | binfile = os.path.join(self.base, 'testfile.bin') |
||
280 | bindata = bytes(range(256)) |
||
281 | |||
282 | with open(binfile, 'wb') as f: |
||
283 | f.write(bindata) |
||
284 | data = self.get('download_file', path='testfile.bin') |
||
285 | os.remove(binfile) |
||
286 | |||
287 | self.assertEqual(data, bindata) |
||
288 | |||
289 | self.assertRaises( |
||
290 | Page404Exception, |
||
291 | self.get, 'download_file', path='../shall_not_pass.txt' |
||
292 | ) |
||
293 | |||
294 | def test_download_directory(self): |
||
295 | binfile = os.path.join(self.start, 'testfile.bin') |
||
296 | bindata = bytes(range(256)) |
||
297 | |||
298 | with open(binfile, 'wb') as f: |
||
299 | f.write(bindata) |
||
300 | data = self.get('download_directory', path='start') |
||
301 | os.remove(binfile) |
||
302 | |||
303 | iodata = io.BytesIO(data) |
||
304 | with tarfile.open('start.tgz', mode="r:gz", fileobj=iodata) as tgz: |
||
305 | tgz_files = [member.name for member in tgz.getmembers() if member.name] |
||
306 | tgz_files.sort() |
||
307 | |||
308 | self.assertEqual(tgz_files, ['testfile.bin', 'testfile.txt',]) |
||
309 | |||
310 | self.assertRaises( |
||
311 | Page404Exception, |
||
312 | self.get, 'download_directory', path='../../shall_not_pass' |
||
313 | ) |
||
314 | |||
315 | def test_upload(self): |
||
316 | c = unichr if PY_LEGACY else chr |
||
317 | |||
318 | files = { |
||
319 | 'testfile.txt': io.BytesIO(''.join(map(c, range(127))).encode('ascii')), |
||
320 | 'testfile.bin': io.BytesIO(''.join(map(c, range(255))).encode('utf-8')), |
||
321 | } |
||
322 | output = self.post('upload', |
||
323 | path='upload', |
||
324 | data={'file%d' % n: (data, name) for n, (name, data) in enumerate(files.items())} |
||
325 | ) |
||
326 | expected_links = sorted(self.url_for('open', path='upload/%s' % i) for i in files) |
||
327 | self.assertEqual(sorted(output.files), expected_links) |
||
328 | self.clear(self.upload) |
||
329 | |||
330 | def test_upload_duplicate(self): |
||
331 | c = unichr if PY_LEGACY else chr |
||
332 | |||
333 | files = ( |
||
334 | ('testfile.txt', 'something'), |
||
335 | ('testfile.txt', 'something_new'), |
||
336 | ) |
||
337 | output = self.post('upload', |
||
338 | path='upload', |
||
339 | data={ |
||
340 | 'file%d' % n: (io.BytesIO(data.encode('ascii')), name) |
||
341 | for n, (name, data) in enumerate(files) |
||
342 | } |
||
343 | ) |
||
344 | |||
345 | self.assertEqual(len(files), len(output.files)) |
||
346 | |||
347 | first_file_url = self.url_for('open', path='upload/%s' % files[0][0]) |
||
348 | self.assertIn(first_file_url, output.files) |
||
349 | |||
350 | file_contents = [] |
||
351 | for filename in os.listdir(self.upload): |
||
352 | with open(os.path.join(self.upload, filename), 'r') as f: |
||
353 | file_contents.append(f.read()) |
||
354 | file_contents.sort() |
||
355 | |||
356 | expected_file_contents = sorted(content for filename, content in files) |
||
357 | |||
358 | self.assertEqual(file_contents, expected_file_contents) |
||
359 | self.clear(self.upload) |
||
360 | |||
619 |