| 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 |