From b7f354ef802b33cf6e0d09cd3ede2c6f1f9fed6f Mon Sep 17 00:00:00 2001 From: Oliver Steele Date: Fri, 4 May 2018 11:11:43 -0400 Subject: [PATCH 1/4] Add more event tests Starts #103. Creates stubs for #75 (marked in `test_events.py` by a TODO). Issues #218, #217, #216 and ,#215 should add to these tests. --- tests/context.py | 2 +- tests/test_app.py | 26 +---------- tests/test_events.py | 104 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 25 deletions(-) create mode 100644 tests/test_events.py diff --git a/tests/context.py b/tests/context.py index e6eee524..39ab9dcc 100644 --- a/tests/context.py +++ b/tests/context.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 """Provide context for imports of ABE. Referenced from The Hitchhiker's Guide to Python. http://docs.python-guide.org/en/latest/writing/structure/#test-suite @@ -10,6 +9,7 @@ MONGODB_TEST_DB_NAME = "abe-unittest" os.environ["DB_NAME"] = MONGODB_TEST_DB_NAME +os.environ["INTRANET_IPS"] = "127.0.0.1/24" os.environ["MONGO_URI"] = "" os.environ['ABE_EMAIL_USERNAME'] = 'email-test-user' diff --git a/tests/test_app.py b/tests/test_app.py index aac55a61..4ce2d3d3 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -38,7 +38,7 @@ def test_add_sample_events(self): data=flask.json.dumps(event), # use flask.json for datetimes content_type='application/json' ) - self.assertEqual(response._status_code, 201) # check only status code + self.assertEqual(response.status_code, 201) # check only status code def test_add_sample_labels(self): """Adds the sample labels to the database""" @@ -48,26 +48,4 @@ def test_add_sample_labels(self): data=flask.json.dumps(event), content_type='application/json' ) - self.assertEqual(response._status_code, 201) - - def test_date_range(self): - from abe import database as db - sample_data.load_data(db) - - with self.subTest("a six-month query returns some events"): - response = self.app.get('/events/?start=2017-01-01&end=2017-07-01') - self.assertEqual(response._status_code, 200) - self.assertEqual(len(flask.json.loads(response.data)), 25) - - with self.subTest("a one-year query returns all events"): - response = self.app.get('/events/?start=2017-01-01&end=2018-01-01') - self.assertEqual(response._status_code, 200) - self.assertEqual(len(flask.json.loads(response.data)), 69) - - with self.subTest("a two-year query is too long"): - response = self.app.get('/events/?start=2017-01-01&end=2019-01-01') - self.assertEqual(response._status_code, 404) - - with self.subTest("a one-year query works for leap years"): - response = self.app.get('/events/?start=2020-01-01&end=2021-01-01') - self.assertEqual(response._status_code, 200) + self.assertEqual(response.status_code, 201) diff --git a/tests/test_events.py b/tests/test_events.py new file mode 100644 index 00000000..10d042f3 --- /dev/null +++ b/tests/test_events.py @@ -0,0 +1,104 @@ +from unittest import skip + +import flask +import isodate + +from . import abe_unittest +from .context import abe # noqa: F401 + +# These imports have to happen after .context sets the environment variables +import abe.app # isort:skip +from abe import sample_data # isort:skip + + +class AbeTestCase(abe_unittest.TestCase): + + def setUp(self): + super().setUp() + abe.app.app.debug = True # enable debug to prevent https redirects + self.app = abe.app.app.test_client() + sample_data.load_data(self.db) + + def test_get_events(self): + with self.subTest("a six-month query returns some events"): + response = self.app.get('/events/?start=2017-01-01&end=2017-07-01') + self.assertEqual(response.status_code, 200) + self.assertEqual(len(flask.json.loads(response.data)), 25) + + with self.subTest("a one-year query returns all events"): + response = self.app.get('/events/?start=2017-01-01&end=2018-01-01') + self.assertEqual(response.status_code, 200) + self.assertEqual(len(flask.json.loads(response.data)), 69) + + with self.subTest("a two-year query is too long"): + response = self.app.get('/events/?start=2017-01-01&end=2019-01-01') + self.assertEqual(response.status_code, 404) + + with self.subTest("a one-year query works for leap years"): + response = self.app.get('/events/?start=2020-01-01&end=2021-01-01') + self.assertEqual(response.status_code, 200) + + with self.subTest("an unauthenticated sender retrieves only public events"): + event_response = self.app.get('/events/?start=2017-01-01&end=2017-07-01') + # TODO: change the following when #75 is implemented + self.assertEqual(len(flask.json.loads(event_response.data)), 25) + + def test_post(self): + event = { + 'title': 'test_post', + 'start': isodate.parse_datetime('2018-05-04T09:00:00') + } + + with self.subTest("succeeds when required fields are present"): + response = self.app.post( + '/events/', + data=flask.json.dumps(event), + content_type='application/json' + ) + self.assertEqual(response.status_code, 201) + + with self.subTest("fails when fields are missing"): + evt = event.copy() + del evt['title'] + response = self.app.post( + '/events/', + data=flask.json.dumps(evt), + content_type='application/json' + ) + self.assertEqual(response.status_code, 400) + self.assertRegex(flask.json.loads(response.data)['error_message'], r"^ValidationError.*'title'") + + evt = event.copy() + del evt['start'] + response = self.app.post( + '/events/', + data=flask.json.dumps(evt), + content_type='application/json' + ) + self.assertEqual(response.status_code, 400) + self.assertRegex(flask.json.loads(response.data)['error_message'], r"^ValidationError.*'start'") + + with self.subTest("fails when the client is not authorized"): + response = self.app.post( + '/events/', + data=flask.json.dumps(event), + content_type='application/json', + headers={'X-Forwarded-For': '192.168.1.1'} + ) + self.assertEqual(response.status_code, 401) + + @skip("Unimplemented test") + def test_put(self): + # TODO: test success + # TODO: test invalid id + # TODO: test invalid data + # TODO: test unauthorized user + pass + + @skip("Unimplemented test") + def test_delete(self): + # TODO: test success + # TODO: test invalid id + # TODO: test invalid data + # TODO: test unauthorized user + pass From 937445b8d456f0db018ffac2ade407178aef5765 Mon Sep 17 00:00:00 2001 From: Oliver Steele Date: Fri, 4 May 2018 19:20:35 -0400 Subject: [PATCH 2/4] Minor improvements to test_events.py * Rename the test case. * it's no longer necessary to set app.debug --- tests/test_events.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_events.py b/tests/test_events.py index 10d042f3..073be34a 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -11,11 +11,10 @@ from abe import sample_data # isort:skip -class AbeTestCase(abe_unittest.TestCase): +class EventsTestCase(abe_unittest.TestCase): def setUp(self): super().setUp() - abe.app.app.debug = True # enable debug to prevent https redirects self.app = abe.app.app.test_client() sample_data.load_data(self.db) From 2adaea2c5abeb9bb83e1ca94e26b421e61c1dd79 Mon Sep 17 00:00:00 2001 From: Oliver Steele Date: Fri, 4 May 2018 19:22:35 -0400 Subject: [PATCH 3/4] Add test cases for /labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also in label_resources.py: * Change a logger.warning to a logging.debug, since it's not a server issue if this happens (and was chattering at the test suite) * Letter logger.{debug,info} do the string formatting instead of passing it formatted strings — for style and efficiency --- abe/resource_models/label_resources.py | 12 ++-- tests/test_labels.py | 96 ++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 tests/test_labels.py diff --git a/abe/resource_models/label_resources.py b/abe/resource_models/label_resources.py index eb7cc01a..cbbb3516 100644 --- a/abe/resource_models/label_resources.py +++ b/abe/resource_models/label_resources.py @@ -32,7 +32,7 @@ class LabelApi(Resource): def get(self, label_name=None): """Retrieve labels""" if label_name: # use label name/object id if present - logging.debug('Label requested: ' + label_name) + logging.debug('Label requested: %s', label_name) search_fields = ['name', 'id'] result = multi_search(db.Label, label_name, search_fields) if not result: @@ -52,12 +52,12 @@ def get(self, label_name=None): def post(self): """Create new label with parameters passed in through args or form""" received_data = request_to_dict(request) - logging.debug("Received POST data: {}".format(received_data)) + logging.debug("Received POST data: %s", received_data) try: new_label = db.Label(**received_data) new_label.save() except ValidationError as error: - logging.warning("POST request rejected: {}".format(str(error))) + logging.debug("POST request rejected: %s", error) return {'error_type': 'validation', 'validation_errors': [str(err) for err in error.errors], 'error_message': error.message}, 400 @@ -69,7 +69,7 @@ def post(self): def put(self, label_name): """Modify individual label""" received_data = request_to_dict(request) - logging.debug("Received PUT data: {}".format(received_data)) + logging.debug("Received PUT data: %s", received_data) search_fields = ['name', 'id'] result = multi_search(db.Label, label_name, search_fields) if not result: @@ -88,14 +88,14 @@ def put(self, label_name): @edit_auth_required def delete(self, label_name): """Delete individual label""" - logging.debug('Label requested: ' + label_name) + logging.debug('Label requested: %s', label_name) search_fields = ['name', 'id'] result = multi_search(db.Label, label_name, search_fields) if not result: return "Label not found with identifier '{}'".format(label_name), 404 received_data = request_to_dict(request) - logging.debug("Received DELETE data: {}".format(received_data)) + logging.debug("Received DELETE data: %s", received_data) result.delete() return mongo_to_dict(result) diff --git a/tests/test_labels.py b/tests/test_labels.py new file mode 100644 index 00000000..e01d2770 --- /dev/null +++ b/tests/test_labels.py @@ -0,0 +1,96 @@ +from unittest import skip + +import flask + +from . import abe_unittest +from .context import abe # noqa: F401 + +# These imports have to happen after .context sets the environment variables +import abe.app # isort:skip +from abe import sample_data # isort:skip + + +class LabelsTestCase(abe_unittest.TestCase): + + def setUp(self): + super().setUp() + self.app = abe.app.app.test_client() + sample_data.load_data(self.db) + + def test_get(self): + response = self.app.get('/labels/') + self.assertEqual(response.status_code, 200) + labels = flask.json.loads(response.data) + self.assertEqual(len(labels), 15) + label = next((l for l in labels if l['name'] == 'library'), None) + self.assertIsNotNone(label) + self.assertEqual(label['name'], 'library') + self.assertEqual(label['color'], '#26AAA5') + self.assertRegex(label['description'], r'relating to the Olin Library') + self.assertEqual(label['url'], 'http://library.olin.edu/') + + def test_post(self): + label1 = { + 'name': 'label-test', + } + with self.subTest("succeeds when required fields are present"): + response = self.app.post( + '/labels/', + data=flask.json.dumps(label1), + content_type='application/json' + ) + self.assertEqual(response.status_code, 201) + + # FIXME: + # with self.subTest("fails on a duplicate label name"): + # response = self.app.post( + # '/labels/', + # data=flask.json.dumps(label1), + # content_type='application/json' + # ) + # self.assertEqual(response.status_code, 400) + + with self.subTest("fails when fields are missing"): + response = self.app.post( + '/labels/', + data=flask.json.dumps({}), + content_type='application/json' + ) + self.assertEqual(response.status_code, 400) + self.assertRegex(flask.json.loads(response.data)['error_message'], r"^ValidationError.*'name'") + + with self.subTest("fails when the client is not authorized"): + label = { + 'name': 'label-test-2', + } + response = self.app.post( + '/labels/', + data=flask.json.dumps(label), + content_type='application/json', + headers={'X-Forwarded-For': '192.168.1.1'} + ) + self.assertEqual(response.status_code, 401) + + def test_put(self): + # TODO: test success + # TODO: test invalid id + # TODO: test invalid data + with self.subTest("fails when the client is not authorized"): + response = self.app.put( + '/labels/library', + data=flask.json.dumps({'description': 'new description'}), + content_type='application/json', + headers={'X-Forwarded-For': '192.168.1.1'} + ) + self.assertEqual(response.status_code, 401) + + def test_delete(self): + # TODO: test success + # TODO: test invalid id + # TODO: test invalid data + with self.subTest("fails when the client is not authorized"): + response = self.app.delete( + '/labels/library', + headers={'X-Forwarded-For': '192.168.1.1'} + ) + self.assertEqual(response.status_code, 401) From 5eebfda2964602ff83eab77a52c10f3a0a5b920b Mon Sep 17 00:00:00 2001 From: songbird175 Date: Fri, 4 May 2018 23:15:50 -0400 Subject: [PATCH 4/4] Remove unused import --- tests/test_labels.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_labels.py b/tests/test_labels.py index e01d2770..4a2a3d4a 100644 --- a/tests/test_labels.py +++ b/tests/test_labels.py @@ -1,5 +1,3 @@ -from unittest import skip - import flask from . import abe_unittest