class UploadFileRequestHandlerTest(tornado.testing.AsyncHTTPTestCase): """Tests the /upload_file endpoint.""" def get_app(self): self.file_mgr = UploadedFileManager() self._get_session_info = lambda x: True return tornado.web.Application( [ ( UPLOAD_FILE_ROUTE, UploadFileRequestHandler, dict( file_mgr=self.file_mgr, get_session_info=self._get_session_info, ), ), ] ) def _upload_files(self, params): # We use requests.Request to construct our multipart/form-data request # here, because they are absurdly fiddly to compose, and Tornado # doesn't include a utility for building them. We then use self.fetch() # to actually send the request to the test server. req = requests.Request( method="POST", url=self.get_url("/upload_file"), files=params ).prepare() return self.fetch( "/upload_file", method=req.method, headers=req.headers, body=req.body ) def test_upload_one_file(self): """Uploading a file should populate our file_mgr.""" file = MockFile("filename", b"123") params = { file.name: file.data, "sessionId": (None, "fooReport"), "widgetId": (None, "barWidget"), "totalFiles": (None, "1"), } response = self._upload_files(params) self.assertEqual(200, response.code) self.assertEqual( [(file.name, file.data)], [ (rec.name, rec.data) for rec in self.file_mgr.get_files("fooReport", "barWidget") ], ) def test_upload_multiple_files(self): file_1 = MockFile("file1", b"123") file_2 = MockFile("file2", b"456") file_3 = MockFile("file3", b"789") params = { file_1.name: file_1.data, file_2.name: file_2.data, file_3.name: file_3.data, "sessionId": (None, "fooReport"), "widgetId": (None, "barWidget"), "totalFiles": (None, "1"), } response = self._upload_files(params) self.assertEqual(200, response.code) self.assertEqual( sorted([file_1, file_2, file_3]), sorted( [ (rec.name, rec.data) for rec in self.file_mgr.get_files("fooReport", "barWidget") ] ), ) def test_upload_missing_params(self): """Missing params in the body should fail with 400 status.""" params = { "image.png": ("image.png", b"1234"), "sessionId": (None, "fooReport"), # "widgetId": (None, 'barWidget'), "totalFiles": (None, "1"), } response = self._upload_files(params) self.assertEqual(400, response.code) self.assertIn("Missing 'widgetId'", response.reason) def test_upload_missing_file(self): """Missing file should fail with 400 status.""" params = { # "image.png": ("image.png", b"1234"), "sessionId": (None, "fooReport"), "widgetId": (None, "barWidget"), "totalFiles": (None, "1"), } response = self._upload_files(params) self.assertEqual(400, response.code) self.assertIn("Expected at least 1 file, but got 0", response.reason) def test_delete_file(self): """File should be able to be deleted successfully""" file1 = UploadedFileRec("1234", "name", "type", b"1234") file2 = UploadedFileRec("4567", "name", "type", b"1234") self.file_mgr.add_files("session1", "widget1", [file1]) self.file_mgr.add_files("session2", "widget2", [file2]) self.file_mgr.update_file_count("session1", "widget1", 1) self.file_mgr.update_file_count("session2", "widget2", 1) response = self.fetch(f"/upload_file/session1/widget1/1234", method="DELETE") self.assertEqual(200, response.code) self.assertFalse(len(self.file_mgr.get_files("session1", "widget1"))) self.assertTrue(len(self.file_mgr.get_files("session2", "widget2"))) def test_delete_file_across_sessions(self): """Deleting file param mismatch should fail with 404 status.""" file1 = UploadedFileRec("1234", "name", "type", b"1234") file2 = UploadedFileRec("4567", "name", "type", b"1234") self.file_mgr.add_files("session1", "widget1", [file1]) self.file_mgr.add_files("session2", "widget2", [file2]) response = self.fetch(f"/upload_file/session2/widget1/1234", method="DELETE") self.assertEqual(404, response.code) self.assertTrue(len(self.file_mgr.get_files("session1", "widget1"))) self.assertTrue(len(self.file_mgr.get_files("session2", "widget2"))) @parameterized.expand( [ (None, "widget_id", "123"), ("session_id", None, "123"), ("session_id", "widget_id", None), ] ) def test_delete_missing_param(self, session_id, widget_id, file_id): """Missing param should fail with 404 status.""" response = self.fetch( f"/upload_file/{session_id}/{widget_id}/{file_id}", method="DELETE" ) self.assertEqual(404, response.code)
class UploadedFileManagerTest(unittest.TestCase): def setUp(self): self.mgr = UploadedFileManager() self.filemgr_events = [] self.mgr.on_files_updated.connect(self._on_files_updated) def _on_files_updated(self, file_list, **kwargs): self.filemgr_events.append(file_list) def test_add_file(self): self.assertIsNone(self.mgr.get_files("non-report", "non-widget")) self.mgr.update_file_count("session", "widget", 1) self.mgr.add_files("session", "widget", [file1]) self.assertEqual([file1], self.mgr.get_files("session", "widget")) self.assertEqual(len(self.filemgr_events), 1) # Add another file with the same ID self.mgr.update_file_count("session", "widget", 2) self.mgr.add_files("session", "widget", [file2]) self.assertEqual([file1, file2], self.mgr.get_files("session", "widget")) self.assertEqual(len(self.filemgr_events), 2) def test_remove_file(self): # This should not error. self.mgr.remove_files("non-report", "non-widget") self.mgr.add_files("session", "widget", [file1]) self.mgr.remove_file("session", "widget", file1.id) self.assertEqual([], self.mgr.get_files("session", "widget")) self.mgr.remove_file("session", "widget", file1.id) self.assertEqual([], self.mgr.get_files("session", "widget")) self.mgr.add_files("session", "widget", [file1]) self.mgr.add_files("session", "widget", [file2]) self.mgr.remove_file("session", "widget", file1.id) self.assertEqual([file2], self.mgr.get_files("session", "widget")) def test_remove_widget_files(self): # This should not error. self.mgr.remove_session_files("non-report") # Add two files with different session IDs, but the same widget ID. self.mgr.add_files("session1", "widget", [file1]) self.mgr.add_files("session2", "widget", [file1]) self.mgr.remove_files("session1", "widget") self.assertIsNone(self.mgr.get_files("session1", "widget")) self.assertEqual([file1], self.mgr.get_files("session2", "widget")) def test_remove_session_files(self): # This should not error. self.mgr.remove_session_files("non-report") # Add two files with different session IDs, but the same widget ID. self.mgr.add_files("session1", "widget1", [file1]) self.mgr.add_files("session1", "widget2", [file1]) self.mgr.add_files("session2", "widget", [file1]) self.mgr.remove_session_files("session1") self.assertIsNone(self.mgr.get_files("session1", "widget1")) self.assertIsNone(self.mgr.get_files("session1", "widget2")) self.assertEqual([file1], self.mgr.get_files("session2", "widget"))
class UploadFileRequestHandlerInvalidSessionTest(tornado.testing.AsyncHTTPTestCase): """Tests the /upload_file endpoint.""" def get_app(self): self.file_mgr = UploadedFileManager() self._get_session_info = lambda x: None return tornado.web.Application( [ ( UPLOAD_FILE_ROUTE, UploadFileRequestHandler, dict( file_mgr=self.file_mgr, get_session_info=self._get_session_info, ), ), ] ) def _upload_files(self, params): # We use requests.Request to construct our multipart/form-data request # here, because they are absurdly fiddly to compose, and Tornado # doesn't include a utility for building them. We then use self.fetch() # to actually send the request to the test server. req = requests.Request( method="POST", url=self.get_url("/upload_file"), files=params ).prepare() return self.fetch( "/upload_file", method=req.method, headers=req.headers, body=req.body ) def test_upload_one_file(self): """Uploading a file should populate our file_mgr.""" file = MockFile("filename", b"123") params = { file.name: file.data, "sessionId": (None, "fooReport"), "widgetId": (None, "barWidget"), "totalFiles": (None, "1"), } response = self._upload_files(params) self.assertEqual(400, response.code) self.assertIsNone(self.file_mgr.get_files("fooReport", "barWidget")) def test_upload_multiple_files(self): file_1 = MockFile("file1", b"123") file_2 = MockFile("file2", b"456") file_3 = MockFile("file3", b"789") params = { file_1.name: file_1.data, file_2.name: file_2.data, file_3.name: file_3.data, "sessionId": (None, "fooReport"), "widgetId": (None, "barWidget"), "totalFiles": (None, "1"), } response = self._upload_files(params) self.assertEqual(400, response.code) self.assertIsNone(self.file_mgr.get_files("fooReport", "barWidget")) def test_delete_file(self): """File should be able to be deleted successfully""" file1 = UploadedFileRec("1234", "name", "type", b"1234") file2 = UploadedFileRec("4567", "name", "type", b"1234") self.file_mgr.add_files("session1", "widget1", [file1]) self.file_mgr.add_files("session2", "widget2", [file2]) self.file_mgr.update_file_count("session1", "widget1", 1) self.file_mgr.update_file_count("session2", "widget2", 1) response = self.fetch(f"/upload_file/session1/widget1/1234", method="DELETE") self.assertEqual(404, response.code)