def setUp(self): twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, bear_hash_key='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB=', ) self.fernet_mock = conf.fernet = Mock(spec=Fernet) self.db = db = test_db() db.router = Mock(spec=Router) db.router.register_user.return_value = (True, {}, {}) db.router.get_uaid.return_value = { "router_type": "test", "router_data": dict() } db.create_initial_message_tables() self.routers = routers = routers_from_config(conf, db, Mock()) routers["test"] = Mock(spec=IRouter) app = EndpointHTTPFactory(conf, db=db, routers=routers) self.client = Client(app) self.request_mock = Mock(body=b'', arguments={}, headers={}) self.reg = NewRegistrationHandler(app, self.request_mock) self.auth = ("WebPush %s" % generate_hash(conf.bear_hash_key[0], dummy_uaid.hex)) self.conf = conf
def setUp(self): twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, crypto_key='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', ) db = test_db() self.message_mock = db._message = Mock(spec=Message) self.fernet_mock = conf.fernet = Mock(spec=Fernet) app = EndpointHTTPFactory.for_handler(MessageHandler, conf, db=db) self.client = Client(app)
def setUp(self): twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, ) self.logs = _TestingLogObserver() begin_or_register(self.logs, discardBuffer=True) self.addCleanup(globalLogPublisher.removeObserver, self.logs) app = EndpointHTTPFactory.for_handler(LogCheckHandler, conf) self.client = Client(app)
class HealthTestCase(unittest.TestCase): def setUp(self): self.timeout = 4 twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, router_table=DDBTableConfig(tablename="router_test"), message_table=DDBTableConfig(tablename="message_int_test"), ) db = DatabaseManager.from_config( conf, resource=autopush.tests.boto_resource) db.setup_tables() # ignore logging logs = _TestingLogObserver() begin_or_register(logs) self.addCleanup(globalLogPublisher.removeObserver, logs) app = EndpointHTTPFactory.for_handler(HealthHandler, conf, db=db) self.message = app.db.message self.client = Client(app) @inlineCallbacks def test_healthy(self): yield self._assert_reply({ "status": "OK", "version": __version__, "clients": 0, "storage": {"status": "OK"}, "router_test": {"status": "OK"} }) @inlineCallbacks def test_nonexistent_table(self): self.client.app.db.message.table.delete() yield self._assert_reply({ "status": "NOT OK", "version": __version__, "clients": 0, "router_test": {"status": "OK"}, "storage": { "status": "NOT OK", "error": "Nonexistent table" } }, MissingTableException) @inlineCallbacks def _assert_reply(self, reply, exception=None): resp = yield self.client.get('/health') if exception: assert resp.get_status() == 503 self.flushLoggedErrors(exception) payload = json.loads(resp.content) assert payload == reply
def setUp(self): from autopush.web.webpush import WebPushHandler self.conf = conf = AutopushConfig( hostname="localhost", statsd_host=None, use_cryptography=True, ) self.fernet_mock = conf.fernet = Mock(spec=Fernet) self.db = db = test_db() self.message_mock = db.message = Mock(spec=Message) self.message_mock.all_channels.return_value = (True, [dummy_chid]) app = EndpointHTTPFactory.for_handler(WebPushHandler, conf, db=db) self.wp_router_mock = app.routers["webpush"] = Mock(spec=IRouter) self.client = Client(app)
class LogCheckTestCase(unittest.TestCase): def setUp(self): twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, ) self.logs = _TestingLogObserver() begin_or_register(self.logs, discardBuffer=True) self.addCleanup(globalLogPublisher.removeObserver, self.logs) app = EndpointHTTPFactory.for_handler(LogCheckHandler, conf) self.client = Client(app) @inlineCallbacks def test_get_err(self): resp = yield self.client.get('/v1/err') assert len(self.logs) == 2 assert self.logs.logged( lambda e: (e['log_level'].name == 'error' and e['log_format'] == 'Test Error Message' and e['status_code'] == 418)) payload = json.loads(resp.content) assert payload.get('code') == 418 assert payload.get('message') == "ERROR:Success" @inlineCallbacks def test_get_crit(self): resp = yield self.client.get('/v1/err/crit') assert len(self.logs) == 2 assert self.logs.logged( lambda e: (e['log_level'].name == 'critical' and e['log_failure'] and e['log_format'] == 'Test Critical Message' and e[ 'status_code'] == 418)) payload = json.loads(resp.content) assert payload.get('code') == 418 assert payload.get('error') == "Test Failure" self.flushLoggedErrors() @inlineCallbacks def test_invalid(self): resp = yield self.client.get('/v1/err/bogus') assert resp.get_status() == 404
def test_decorator(self): from autopush.http import EndpointHTTPFactory from autopush.web.base import BaseWebHandler, threaded_validate from autopush.tests.client import Client schema = self._make_basic_schema() class AHandler(BaseWebHandler): def authenticate_peer_cert(self): pass @threaded_validate(schema) def get(self): self.write("done") self.finish() app = EndpointHTTPFactory(Mock(), db=test_db(), routers=None, handlers=[('/test', AHandler)]) client = Client(app) resp = yield client.get('/test') assert resp.content == "done"
def setUp(self): self.timeout = 4 twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, ) # ignore logging logs = _TestingLogObserver() begin_or_register(logs) self.addCleanup(globalLogPublisher.removeObserver, logs) lb_app = EndpointHTTPFactory.for_handler( LBHeartbeatHandler, conf, db=None ) ver_app = EndpointHTTPFactory.for_handler( VersionHandler, conf, db=None ) self.lb_client = Client(lb_app) self.ver_client = Client(ver_app)
def setUp(self): self.timeout = 0.5 twisted.internet.base.DelayedCall.debug = True self.mock_dynamodb2 = mock_dynamodb2() self.mock_dynamodb2.start() self.addCleanup(self.mock_dynamodb2.stop) settings = AutopushSettings( hostname="localhost", statsd_host=None, ) # ignore logging logs = TestingLogObserver() begin_or_register(logs) self.addCleanup(globalLogPublisher.removeObserver, logs) app = EndpointHTTPFactory.for_handler(HealthHandler, settings) self.router_table = app.db.router.table self.storage_table = app.db.storage.table self.client = Client(app)
def setUp(self): self.timeout = 0.5 twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, ) db = DatabaseManager.from_config(conf) db.client = autopush.db.g_client db.setup_tables() # ignore logging logs = TestingLogObserver() begin_or_register(logs) self.addCleanup(globalLogPublisher.removeObserver, logs) app = EndpointHTTPFactory.for_handler(HealthHandler, conf, db=db) self.router_table = app.db.router.table self.message = app.db.message self.client = Client(app)
class HealthTestCase(unittest.TestCase): def setUp(self): self.timeout = 0.5 twisted.internet.base.DelayedCall.debug = True self.mock_dynamodb2 = mock_dynamodb2() self.mock_dynamodb2.start() self.addCleanup(self.mock_dynamodb2.stop) settings = AutopushSettings( hostname="localhost", statsd_host=None, ) # ignore logging logs = TestingLogObserver() begin_or_register(logs) self.addCleanup(globalLogPublisher.removeObserver, logs) app = EndpointHTTPFactory.for_handler(HealthHandler, settings) self.router_table = app.db.router.table self.storage_table = app.db.storage.table self.client = Client(app) @inlineCallbacks def test_healthy(self): yield self._assert_reply({ "status": "OK", "version": __version__, "clients": 0, "storage": { "status": "OK" }, "router": { "status": "OK" } }) @inlineCallbacks def test_aws_error(self): def raise_error(*args, **kwargs): raise InternalServerError(None, None) self.router_table.connection.list_tables = Mock( side_effect=raise_error) self.storage_table.connection.list_tables = Mock( return_value={"TableNames": ["storage"]}) yield self._assert_reply( { "status": "NOT OK", "version": __version__, "clients": 0, "storage": { "status": "OK" }, "router": { "status": "NOT OK", "error": "Server error" } }, InternalServerError) @inlineCallbacks def test_nonexistent_table(self): no_tables = Mock(return_value={"TableNames": []}) self.storage_table.connection.list_tables = no_tables self.router_table.connection.list_tables = no_tables yield self._assert_reply( { "status": "NOT OK", "version": __version__, "clients": 0, "storage": { "status": "NOT OK", "error": "Nonexistent table" }, "router": { "status": "NOT OK", "error": "Nonexistent table" } }, MissingTableException) @inlineCallbacks def test_internal_error(self): def raise_error(*args, **kwargs): raise Exception("synergies not aligned") self.router_table.connection.list_tables = Mock( return_value={"TableNames": ["router"]}) self.storage_table.connection.list_tables = Mock( side_effect=raise_error) yield self._assert_reply( { "status": "NOT OK", "version": __version__, "clients": 0, "storage": { "status": "NOT OK", "error": "Internal error" }, "router": { "status": "OK" } }, Exception) @inlineCallbacks def _assert_reply(self, reply, exception=None): resp = yield self.client.get('/health') if exception: eq_(resp.get_status(), 503) self.flushLoggedErrors(exception) payload = json.loads(resp.content) eq_(payload, reply)
class DockerflowTestCase(unittest.TestCase): def setUp(self): self.timeout = 4 twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, ) # ignore logging logs = _TestingLogObserver() begin_or_register(logs) self.addCleanup(globalLogPublisher.removeObserver, logs) lb_app = EndpointHTTPFactory.for_handler( LBHeartbeatHandler, conf, db=None ) ver_app = EndpointHTTPFactory.for_handler( VersionHandler, conf, db=None ) self.lb_client = Client(lb_app) self.ver_client = Client(ver_app) @inlineCallbacks def test_lbheartbeat(self): resp = yield self.lb_client.get("/__lbheartbeat__") assert resp.get_status() == 200 @patch('autopush.web.dockerflow.open') def test_version(self, mopen): version = """{ "source" : "https://github.com/mozilla-services/autopush", "version": "devel", "commit" : "", "build" : "" } """ reader = Mock() reader.read.return_value = version mopen.__enter__.return_value = reader mopen.return_value = mopen resp = yield self.ver_client.get("/__version__") assert resp.get_status() == 200 assert resp.content == version @patch('autopush.web.dockerflow.open') def test_bad_version(self, mopen): reader = Mock() reader.read.side_effect = IOError mopen.__enter__.return_value = reader mopen.return_value = mopen conf = AutopushConfig( hostname="localhost", statsd_host=None, ) self.request_mock = Mock() bad_ver = VersionHandler( EndpointHTTPFactory(conf, db=None, routers=None), self.request_mock ) with pytest.raises(IOError): bad_ver._get_version()
def test_create_client(self): app = mock_app_builder() client = Client(app) self.assertTrue(client.app)
def setUp(self): self.app = mock_app_builder() self.client = Client(self.app)
class TestClient(unittest.TestCase): def setUp(self): self.app = mock_app_builder() self.client = Client(self.app) def test_create_client(self): app = mock_app_builder() client = Client(app) self.assertTrue(client.app) @inlineCallbacks def test_get_request(self): response = yield self.client.get("/testing/") self.assertEqual(response.content, "Something") self.assertTrue(len(response.headers) > 3) @inlineCallbacks def test_get_request_with_params(self): response = yield self.client.get("/testing/", {"q": "query"}) self.assertEqual(response.content, "Something") self.assertTrue(len(response.headers) > 3) @inlineCallbacks def test_post_request(self): response = yield self.client.post("/testing/") self.assertEqual(response.content, "Something posted") self.assertTrue(len(response.headers) > 3) @inlineCallbacks def test_put_request(self): response = yield self.client.put("/testing/") self.assertEqual(response.content, "Something put") self.assertTrue(len(response.headers) > 3) @inlineCallbacks def test_head_request(self): response = yield self.client.head("/testing/") self.assertEqual(response.content, "") self.assertTrue(len(response.headers) > 3) @inlineCallbacks def test_delete_request(self): response = yield self.client.delete("/testing/") self.assertEqual(response.content, "") self.assertTrue(len(response.headers) > 3) @inlineCallbacks def test_get_deferred_request(self): response = yield self.client.get("/deferred_testing/") self.assertEqual(response.content, "Something...done!") self.assertTrue(len(response.headers) > 3) @inlineCallbacks def test_cookies(self): response = yield self.client.get("/cookie_testing/") self.assertEqual( self.client.cookies.get_secure_cookie("test_cookie"), "test_value" ) response = yield self.client.post("/cookie_testing/") self.assertEqual(response.content, "test_value")
class MessageTestCase(unittest.TestCase): def setUp(self): twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, crypto_key='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', ) db = test_db() self.message_mock = db._message = Mock(spec=Message) self.fernet_mock = conf.fernet = Mock(spec=Fernet) app = EndpointHTTPFactory.for_handler(MessageHandler, conf, db=db) self.client = Client(app) def url(self, **kwargs): return '/m/{message_id}'.format(**kwargs) @inlineCallbacks def test_delete_token_invalid(self): self.fernet_mock.configure_mock(**{ "decrypt.side_effect": InvalidToken}) resp = yield self.client.delete(self.url(message_id='%20')) assert resp.get_status() == 400 @inlineCallbacks def test_delete_token_wrong_components(self): self.fernet_mock.decrypt.return_value = "123:456" resp = yield self.client.delete(self.url(message_id="ignored")) assert resp.get_status() == 400 @inlineCallbacks def test_delete_token_wrong_kind(self): tok = ":".join(["r", dummy_uaid.hex, str(dummy_chid)]) self.fernet_mock.decrypt.return_value = tok resp = yield self.client.delete(self.url(message_id='ignored')) assert resp.get_status() == 400 @inlineCallbacks def test_delete_invalid_timestamp_token(self): tok = ":".join(["02", str(dummy_chid)]) self.fernet_mock.decrypt.return_value = tok resp = yield self.client.delete(self.url(message_id='ignored')) assert resp.get_status() == 400 @inlineCallbacks def test_delete_success(self): tok = ":".join(["m", dummy_uaid.hex, str(dummy_chid)]) self.fernet_mock.decrypt.return_value = tok self.message_mock.configure_mock(**{ "delete_message.return_value": True}) resp = yield self.client.delete(self.url(message_id="123-456")) self.message_mock.delete_message.assert_called() assert resp.get_status() == 204 @inlineCallbacks def test_delete_topic_success(self): tok = ":".join(["01", dummy_uaid.hex, str(dummy_chid), "Inbox"]) self.fernet_mock.decrypt.return_value = tok self.message_mock.configure_mock(**{ "delete_message.return_value": True}) resp = yield self.client.delete(self.url(message_id="123-456")) self.message_mock.delete_message.assert_called() assert resp.get_status() == 204 @inlineCallbacks def test_delete_topic_error_parts(self): tok = ":".join(["01", dummy_uaid.hex, str(dummy_chid)]) self.fernet_mock.decrypt.return_value = tok self.message_mock.configure_mock(**{ "delete_message.return_value": True}) resp = yield self.client.delete(self.url(message_id="123-456")) assert resp.get_status() == 400 @inlineCallbacks def test_delete_db_error(self): tok = ":".join(["m", dummy_uaid.hex, str(dummy_chid)]) self.fernet_mock.decrypt.return_value = tok self.message_mock.configure_mock(**{"delete_message.return_value": False}) resp = yield self.client.delete(self.url(message_id="ignored")) assert 204 == resp.get_status()
class TestWebpushHandler(unittest.TestCase): def setUp(self): import autopush from autopush.web.webpush import WebPushHandler self.conf = conf = AutopushConfig( hostname="localhost", statsd_host=None, use_cryptography=True, ) self.fernet_mock = conf.fernet = Mock(spec=Fernet) self.db = db = test_db() self.message_mock = db._message = Mock(spec=Message) self.db.message_table = Mock(return_value=self.message_mock) self.message_mock.all_channels.return_value = (True, [dummy_chid]) app = EndpointHTTPFactory.for_handler(WebPushHandler, conf, db=db) self.wp_router_mock = app.routers["webpush"] = Mock(spec=IRouter) self.db.router = Mock(spec=autopush.db.Router) self.client = Client(app) def url(self, **kwargs): return '/wpush/{api_ver}/{token}'.format(**kwargs) @inlineCallbacks def test_router_needs_update(self): self.conf.parse_endpoint = Mock(return_value=dict( uaid=dummy_uaid, chid=dummy_chid, public_key="asdfasdf", )) self.fernet_mock.decrypt.return_value = dummy_token self.db.router.get_uaid = Mock(return_value=dict( router_type="webpush", router_data=dict(), uaid=dummy_uaid, current_month=self.db.current_msg_month, )) self.db.router.register_user = Mock(return_value=False) self.wp_router_mock.route_notification.return_value = RouterResponse( status_code=503, router_data=dict(token="new_connect"), ) self.db.message_tables.append(self.db.current_msg_month) resp = yield self.client.post( self.url(api_ver="v1", token=dummy_token), ) assert resp.get_status() == 503 ru = self.db.router.register_user assert ru.called assert 'webpush' == ru.call_args[0][0].get('router_type') @inlineCallbacks def test_router_returns_data_without_detail(self): self.conf.parse_endpoint = Mock(return_value=dict( uaid=dummy_uaid, chid=dummy_chid, public_key="asdfasdf", )) self.fernet_mock.decrypt.return_value = dummy_token self.db.router.get_uaid.return_value = dict( uaid=dummy_uaid, router_type="webpush", router_data=dict(uaid="uaid"), current_month=self.db.current_msg_month, ) self.wp_router_mock.route_notification.return_value = RouterResponse( status_code=503, router_data=dict(), ) self.db.message_tables.append(self.db.current_msg_month) resp = yield self.client.post( self.url(api_ver="v1", token=dummy_token), ) assert resp.get_status() == 503 assert self.db.router.drop_user.called @inlineCallbacks def test_request_bad_ckey(self): self.fernet_mock.decrypt.return_value = 'invalid key' resp = yield self.client.post(self.url(api_ver="v1", token='ignored'), headers={'crypto-key': 'dummy_key'}) assert resp.get_status() == 404 @inlineCallbacks def test_request_bad_v1_id(self): self.fernet_mock.decrypt.return_value = 'tooshort' resp = yield self.client.post(self.url(api_ver="v1", token='ignored'), ) assert resp.get_status() == 404 @inlineCallbacks def test_request_bad_v2_id_short(self): self.fernet_mock.decrypt.return_value = 'tooshort' resp = yield self.client.post( self.url(api_ver='v2', token='ignored'), headers={'authorization': 'vapid t=dummy_key,k=aaa'}) assert resp.get_status() == 404 @inlineCallbacks def test_request_bad_draft02_auth(self): resp = yield self.client.post(self.url(api_ver='v2', token='ignored'), headers={'authorization': 'vapid foo'}) assert resp.get_status() == 401 @inlineCallbacks def test_request_bad_draft02_missing_key(self): self.fernet_mock.decrypt.return_value = 'a' * 64 resp = yield self.client.post( self.url(api_ver='v2', token='ignored'), headers={'authorization': 'vapid t=dummy.key.value,k='}) assert resp.get_status() == 401 @inlineCallbacks def test_request_bad_draft02_bad_pubkey(self): self.fernet_mock.decrypt.return_value = 'a' * 64 resp = yield self.client.post( self.url(api_ver='v2', token='ignored'), headers={'authorization': 'vapid t=dummy.key.value,k=!aaa'}) assert resp.get_status() == 401 @inlineCallbacks def test_request_bad_v2_id_missing_pubkey(self): self.fernet_mock.decrypt.return_value = 'a' * 64 resp = yield self.client.post(self.url(api_ver='v2', token='ignored'), headers={ 'crypto-key': 'key_id=dummy_key', 'authorization': 'dummy_key' }) assert resp.get_status() == 401 @inlineCallbacks def test_request_v2_id_variant_pubkey(self): self.fernet_mock.decrypt.return_value = 'a' * 32 variant_key = base64.urlsafe_b64encode("0V0" + ('a' * 85)) self.db.router.get_uaid.return_value = dict( uaid=dummy_uaid, chid=dummy_chid, router_type="gcm", router_data=dict(creds=dict(senderID="bogus")), ) resp = yield self.client.post(self.url(api_ver='v1', token='ignored'), headers={ 'crypto-key': 'p256ecdsa=' + variant_key, 'authorization': 'webpush dummy.key' }) assert resp.get_status() == 401 @inlineCallbacks def test_request_v2_id_no_crypt_auth(self): self.fernet_mock.decrypt.return_value = 'a' * 32 self.db.router.get_uaid.return_value = dict( uaid=dummy_uaid, chid=dummy_chid, router_type="gcm", router_data=dict(creds=dict(senderID="bogus")), ) resp = yield self.client.post( self.url(api_ver='v1', token='ignored'), headers={'authorization': 'webpush dummy.key'}) assert resp.get_status() == 401 @inlineCallbacks def test_request_no_router_type(self): self.fernet_mock.decrypt.return_value = 'a' * 32 self.db.router.get_uaid.return_value = dict( uaid=dummy_uaid, chid=dummy_chid, ) resp = yield self.client.post( self.url(api_ver='v1', token='ignored'), headers={'authorization': 'webpush dummy.key'}) assert resp.get_status() == 410 @inlineCallbacks def test_request_bad_v2_id_bad_pubkey(self): self.fernet_mock.decrypt.return_value = 'a' * 64 resp = yield self.client.post(self.url(api_ver='v2', token='ignored'), headers={ 'crypto-key': 'p256ecdsa=Invalid!', 'authorization': 'dummy_key' }) assert resp.get_status() == 401
class RegistrationTestCase(unittest.TestCase): CORS_HEAD = "POST" def setUp(self): twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, bear_hash_key='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB=', ) self.fernet_mock = conf.fernet = Mock(spec=Fernet) self.db = db = test_db() db.router = Mock(spec=Router) db.router.register_user.return_value = (True, {}, {}) db.router.get_uaid.return_value = { "router_type": "test", "router_data": dict() } db.create_initial_message_tables() self.routers = routers = routers_from_config(conf, db, Mock()) routers["test"] = Mock(spec=IRouter) app = EndpointHTTPFactory(conf, db=db, routers=routers) self.client = Client(app) self.request_mock = Mock(body=b'', arguments={}, headers={}) self.reg = NewRegistrationHandler(app, self.request_mock) self.auth = ("WebPush %s" % generate_hash(conf.bear_hash_key[0], dummy_uaid.hex)) self.conf = conf def url(self, router_token='test', **kwargs): urlfmt = '/v1/{router_type}/{router_token}/registration' result = urlfmt.format(router_token=router_token, **kwargs) if kwargs.get('uaid'): result += '/' + kwargs.get('uaid') if kwargs.get('chid'): result += '/subscription/' + kwargs.get('chid') return result def patch(self, *args, **kwargs): """Patch an object only for the duration of a test""" patch_obj = patch(*args, **kwargs) patch_obj.__enter__() self.addCleanup(patch_obj.__exit__) def test_base_tags(self): self.reg._base_tags = [] self.reg.request = Mock(headers={'user-agent': 'test'}, host='example.com:8080') tags = self.reg.base_tags() assert tags == ['user_agent:test', 'host:example.com:8080'] # previously failed tags = self.reg.base_tags() assert tags == ['user_agent:test', 'host:example.com:8080'] def _check_error(self, resp, code, errno, error, message=None): d = json.loads(resp.content) assert d.get("code") == code assert d.get("errno") == errno assert d.get("error") == error def test_init_info(self): h = self.request_mock.headers h["user-agent"] = "myself" d = self.reg._init_info() assert d["user_agent"] == "myself" self.request_mock.remote_ip = "local1" d = self.reg._init_info() assert d["remote_ip"] == "local1" self.request_mock.headers["x-forwarded-for"] = "local2" d = self.reg._init_info() assert d["remote_ip"] == "local2" def test_conf_crypto_key(self): fake = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' conf = AutopushConfig(crypto_key=fake) assert conf.fernet._fernets[0]._encryption_key == ( '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') fake2 = 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=' conf = AutopushConfig(crypto_key=[fake, fake2]) assert conf.fernet._fernets[0]._encryption_key == ( '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') assert conf.fernet._fernets[1]._encryption_key == ( '\x10A\x04\x10A\x04\x10A\x04\x10A\x04\x10A\x04\x10') def test_cors(self): ch1 = "Access-Control-Allow-Origin" ch2 = "Access-Control-Allow-Methods" reg = self.reg reg.conf.cors = False assert reg._headers.get(ch1) != "*" assert reg._headers.get(ch2) != self.CORS_HEAD reg.clear_header(ch1) reg.clear_header(ch2) reg.conf.cors = True reg.prepare() assert reg._headers[ch1] == "*" assert reg._headers[ch2] == self.CORS_HEAD def test_cors_head(self): ch1 = "Access-Control-Allow-Origin" ch2 = "Access-Control-Allow-Methods" reg = self.reg reg.conf.cors = True reg.prepare() reg.head(None) assert reg._headers[ch1] == "*" assert reg._headers[ch2] == self.CORS_HEAD def test_cors_options(self): ch1 = "Access-Control-Allow-Origin" ch2 = "Access-Control-Allow-Methods" reg = self.reg reg.conf.cors = True reg.prepare() reg.options(None) assert reg._headers[ch1] == "*" assert reg._headers[ch2] == self.CORS_HEAD @inlineCallbacks def test_post(self): self.patch('uuid.uuid4', return_value=dummy_uaid) self.fernet_mock.configure_mock(**{ 'encrypt.return_value': 'abcd123', }) resp = yield self.client.post( self.url(router_type='webpush', router_token='yyy'), headers={"Authorization": self.auth}, body=json.dumps(dict( type="webpush", channelID=str(dummy_chid), data={}, )) ) assert resp.get_status() == 200 payload = json.loads(resp.content) assert payload["uaid"] == dummy_uaid.hex assert payload["channelID"] == dummy_chid.hex assert payload["endpoint"] == "http://localhost/wpush/v1/abcd123" assert "secret" in payload @inlineCallbacks def test_post_gcm(self): self.patch('uuid.uuid4', side_effect=(uuid.uuid4(), dummy_uaid, dummy_chid)) from autopush.router.gcm import GCMRouter sids = {"182931248179192": {"auth": "aailsjfilajdflijdsilfjsliaj"}} gcm = GCMRouter( self.conf, {"dryrun": True, "senderIDs": sids, "endpoint": "gcm-http.googleapis.com/gcm/send"}, SinkMetrics() ) self.routers["gcm"] = gcm self.fernet_mock.configure_mock(**{ 'encrypt.return_value': 'abcd123', }) resp = yield self.client.post( self.url(router_type="gcm", router_token="182931248179192"), headers={"Authorization": self.auth}, body=json.dumps(dict( channelID=str(dummy_chid), token="182931248179192", )) ) assert resp.get_status() == 200 payload = json.loads(resp.content) assert payload["uaid"] == dummy_uaid.hex assert payload["channelID"] == dummy_chid.hex assert payload["endpoint"] == "http://localhost/wpush/v1/abcd123" calls = self.db.router.register_user.call_args call_args = calls[0][0] assert has_connected_this_month(call_args) is True assert "secret" in payload @inlineCallbacks def test_post_invalid_args(self, *args): resp = yield self.client.post( self.url(router_type="foo"), headers={"Authorization": self.auth}, body=json.dumps(dict( type="invalid", data={}, )) ) self._check_error(resp, 400, 108, "Bad Request") @inlineCallbacks def test_post_bad_router_type(self): self.patch('uuid.uuid4', return_value=dummy_uaid) resp = yield self.client.post( self.url(router_type="simplepush"), headers={"Authorization": self.auth}, body=json.dumps(dict( type="invalid", channelID=str(dummy_chid), data={}, )) ) self._check_error(resp, 400, 108, "Bad Request") @inlineCallbacks def test_post_bad_router_register(self, *args): router = self.routers["webpush"] rexc = RouterException("invalid", status_code=402, errno=107) router.register = Mock(side_effect=rexc) resp = yield self.client.post( self.url(router_type="webpush"), headers={"Authorization": self.auth}, body=json.dumps(dict( type="webpush", channelID=str(dummy_chid), data={}, )) ) self._check_error(resp, rexc.status_code, rexc.errno, "") @inlineCallbacks def test_post_existing_uaid(self): self.patch('uuid.uuid4', return_value=dummy_chid) self.fernet_mock.configure_mock(**{ 'encrypt.return_value': 'abcd123', }) resp = yield self.client.post( self.url(router_type="test", uaid=dummy_uaid.hex) + "/subscription", headers={"Authorization": self.auth}, body=json.dumps(dict( channelID=str(dummy_chid), )) ) payload = json.loads(resp.content) assert payload["channelID"] == dummy_chid.hex assert payload["endpoint"] == "http://localhost/wpush/v1/abcd123" @inlineCallbacks def test_no_uaid(self): self.db.router.get_uaid.side_effect = ItemNotFound resp = yield self.client.delete( self.url(router_type="webpush", uaid=dummy_uaid.hex, chid=str(dummy_chid)) ) self._check_error(resp, 410, 103, "") @inlineCallbacks def test_no_auth(self): resp = yield self.client.delete( self.url(router_type="webpush", uaid=dummy_uaid.hex, chid=str(dummy_chid)), ) self._check_error(resp, 401, 109, "Unauthorized") @inlineCallbacks def test_bad_body(self): url = self.url(router_type="webpush", uaid=dummy_uaid.hex) resp = yield self.client.put(url, body="{invalid") self._check_error(resp, 400, 108, "Bad Request") @inlineCallbacks def test_post_bad_params(self): self.patch('uuid.uuid4', return_value=dummy_uaid) resp = yield self.client.delete( self.url(router_type="webpush", uaid=dummy_uaid.hex, chid=str(dummy_chid)), headers={"Authorization": "WebPush Invalid"}, body=json.dumps(dict( channelID=str(dummy_chid), )) ) self._check_error(resp, 401, 109, 'Unauthorized') @inlineCallbacks def test_post_nochid(self, *args): self.patch('uuid.uuid4', return_value=dummy_chid) self.fernet_mock.configure_mock(**{ 'encrypt.return_value': 'abcd123', }) resp = yield self.client.post( self.url(router_type="webpush", uaid=dummy_uaid.hex) + "/subscription", headers={"Authorization": self.auth}, body=json.dumps(dict( type="webpush", data={}, )) ) payload = json.loads(resp.content) assert payload["channelID"] == dummy_chid.hex assert payload["endpoint"] == "http://localhost/wpush/v1/abcd123" @inlineCallbacks def test_post_with_app_server_key(self, *args): self.patch('uuid.uuid4', return_value=dummy_chid) dummy_key = "RandomKeyString" def mock_encrypt(cleartext): assert len(cleartext) == 64 # dummy_uaid assert cleartext[0:16] == ( 'abad1dea00000000aabbccdd00000000'.decode('hex')) # dummy_chid assert cleartext[16:32] == ( 'deadbeef00000000decafbad00000000'.decode('hex')) # sha256(dummy_key).digest() assert cleartext[32:] == ( '47aedd050b9e19171f0fa7b8b65ca670' '28f0bc92cd3f2cd3682b1200ec759007').decode('hex') return 'abcd123' self.fernet_mock.configure_mock(**{ 'encrypt.side_effect': mock_encrypt, }) resp = yield self.client.post( self.url(router_type="webpush", uaid=dummy_uaid.hex) + "/subscription", headers={"Authorization": self.auth}, body=json.dumps(dict( type="webpush", key=utils.base64url_encode(dummy_key), data={}, )) ) payload = json.loads(resp.content) assert payload["channelID"] == dummy_chid.hex assert payload["endpoint"] == "http://localhost/wpush/v2/abcd123" @inlineCallbacks def test_put(self, *args): self.patch('uuid.uuid4', return_value=dummy_uaid) data = dict(token="some_token") frouter = self.routers["test"] frouter.register = Mock() frouter.register.return_value = data uri = self.url(router_type='test', uaid=dummy_uaid.hex) resp = yield self.client.put( uri, headers={"Authorization": self.auth}, body=json.dumps(data), ) payload = json.loads(resp.content) assert payload == {} frouter.register.assert_called_with( uaid="", router_data=data, app_id='test', ) user_data = self.db.router.register_user.call_args[0][0] assert user_data['uaid'] == dummy_uaid.hex assert user_data['router_type'] == 'test' assert user_data['router_data']['token'] == 'some_token' @inlineCallbacks def test_put_bad_auth(self, *args): self.patch('uuid.uuid4', return_value=dummy_uaid) resp = yield self.client.put( self.url(router_type="test", uaid=dummy_uaid.hex), headers={"Authorization": "Fred Smith"}, body=json.dumps(dict(token="blah")) ) self._check_error(resp, 401, 109, "Unauthorized") @inlineCallbacks def test_put_bad_uaid_path(self, *args): self.patch('uuid.uuid4', return_value=dummy_uaid) resp = yield self.client.put( self.url(router_type="test", uaid="invalid"), headers={"Authorization": "Fred Smith"}, body=json.dumps(dict(token="blah")) ) assert resp.get_status() == 404 @inlineCallbacks def test_put_bad_arguments(self, *args): self.patch('uuid.uuid4', return_value=dummy_chid) resp = yield self.client.put( self.url(router_type='foo', uaid=dummy_uaid.hex), headers={"Authorization": self.auth}, body=json.dumps(dict( type="test", data=dict(token="some_token"), )) ) self._check_error(resp, 400, 108, "Bad Request") @inlineCallbacks def test_put_bad_router_register(self): frouter = self.routers["webpush"] rexc = RouterException("invalid", status_code=400, errno=108) frouter.register = Mock(side_effect=rexc) resp = yield self.client.put( self.url(router_type='simplepush', uaid=dummy_uaid.hex), headers={"Authorization": self.auth}, body=json.dumps(dict(token="blah")) ) self._check_error(resp, rexc.status_code, rexc.errno, "Bad Request") @inlineCallbacks def test_delete_success(self): notif = make_webpush_notification(dummy_uaid.hex, str(dummy_chid)) messages = self.db.message messages.register_channel(dummy_uaid.hex, str(dummy_chid)) messages.store_message(notif) yield self.client.delete( self.url(router_type="test", router_token="test", uaid=dummy_uaid.hex, chid=str(dummy_chid)), headers={"Authorization": self.auth}, ) @inlineCallbacks def test_delete_bad_chid_value(self): notif = make_webpush_notification(dummy_uaid.hex, str(dummy_chid)) messages = self.db.message messages.register_channel(dummy_uaid.hex, str(dummy_chid)) messages.store_message(notif) resp = yield self.client.delete( self.url(router_type="test", router_token="test", uaid=dummy_uaid.hex, chid=uuid.uuid4().hex), headers={"Authorization": self.auth}, ) self._check_error(resp, 410, 106, "") @inlineCallbacks def test_delete_no_such_chid(self): notif = make_webpush_notification(dummy_uaid.hex, str(dummy_chid)) messages = self.db.message messages.register_channel(dummy_uaid.hex, str(dummy_chid)) messages.store_message(notif) # Moto can't handle set operations of this nature so we have # to mock the reply self.patch('autopush.db.Message.unregister_channel', return_value=False) resp = yield self.client.delete( self.url(router_type="test", router_token="test", uaid=dummy_uaid.hex, chid=str(uuid.uuid4())), headers={"Authorization": self.auth} ) self._check_error(resp, 410, 106, "") @inlineCallbacks def test_delete_uaid(self): notif = make_webpush_notification(dummy_uaid.hex, str(dummy_chid)) notif2 = make_webpush_notification(dummy_uaid.hex, str(dummy_chid)) messages = self.db.message messages.store_message(notif) messages.store_message(notif2) self.db.router.drop_user.return_value = True yield self.client.delete( self.url(router_type="webpush", router_token="test", uaid=dummy_uaid.hex), headers={"Authorization": self.auth}, ) # Note: Router is mocked, so the UAID is never actually # dropped. assert self.db.router.drop_user.called assert self.db.router.drop_user.call_args_list[0][0] == ( dummy_uaid.hex,) @inlineCallbacks def test_delete_bad_uaid(self): # Return a 401 as the random UUID was never registered resp = yield self.client.delete( self.url(router_type="test", router_token="test", uaid=uuid.uuid4().hex), headers={"Authorization": self.auth}, ) assert resp.get_status() == 401 @inlineCallbacks def test_delete_orphans(self): self.db.router.drop_user.return_value = False resp = yield self.client.delete( self.url(router_type="test", router_token="test", uaid=dummy_uaid.hex), headers={"Authorization": self.auth}, ) assert resp.get_status() == 410 @inlineCallbacks def test_delete_bad_auth(self, *args): resp = yield self.client.delete( self.url(router_type="test", router_token="test", uaid=dummy_uaid.hex), headers={"Authorization": "Invalid"}, ) assert resp.get_status() == 401 @inlineCallbacks def test_delete_bad_router(self): resp = yield self.client.delete( self.url(router_type="invalid", router_token="test", uaid=dummy_uaid.hex), headers={"Authorization": self.auth}, ) assert resp.get_status() == 400 @inlineCallbacks def test_get(self): chids = [str(dummy_chid), str(dummy_uaid)] self.db.message.all_channels = Mock() self.db.message.all_channels.return_value = (True, chids) resp = yield self.client.get( self.url(router_type="test", router_token="test", uaid=dummy_uaid.hex), headers={"Authorization": self.auth} ) self.db.message.all_channels.assert_called_with(str(dummy_uaid)) payload = json.loads(resp.content) assert chids == payload['channelIDs'] assert dummy_uaid.hex == payload['uaid']
class HealthTestCase(unittest.TestCase): def setUp(self): self.timeout = 0.5 twisted.internet.base.DelayedCall.debug = True conf = AutopushConfig( hostname="localhost", statsd_host=None, ) db = DatabaseManager.from_config(conf) db.client = autopush.db.g_client db.setup_tables() # ignore logging logs = TestingLogObserver() begin_or_register(logs) self.addCleanup(globalLogPublisher.removeObserver, logs) app = EndpointHTTPFactory.for_handler(HealthHandler, conf, db=db) self.router_table = app.db.router.table self.message = app.db.message self.client = Client(app) @inlineCallbacks def test_healthy(self): yield self._assert_reply({ "status": "OK", "version": __version__, "clients": 0, "storage": { "status": "OK" }, "router": { "status": "OK" } }) @inlineCallbacks def test_aws_error(self): def raise_error(*args, **kwargs): raise InternalServerError(None, None) safe = self.client.app.db.client self.client.app.db.client = Mock() self.client.app.db.client.list_tables = Mock(side_effect=raise_error) yield self._assert_reply( { "status": "NOT OK", "version": __version__, "clients": 0, "storage": { "status": "NOT OK", "error": "Server error" }, "router": { "status": "NOT OK", "error": "Server error" } }, InternalServerError) self.client.app.db.client = safe @inlineCallbacks def test_nonexistent_table(self): no_tables = Mock(return_value={"TableNames": []}) safe = self.client.app.db.client self.client.app.db.client = Mock() self.client.app.db.client.list_tables = no_tables yield self._assert_reply( { "status": "NOT OK", "version": __version__, "clients": 0, "storage": { "status": "NOT OK", "error": "Nonexistent table" }, "router": { "status": "NOT OK", "error": "Nonexistent table" } }, MissingTableException) self.client.app.db.client = safe @inlineCallbacks def test_internal_error(self): def raise_error(*args, **kwargs): raise Exception("synergies not aligned") safe = self.client.app.db.client self.client.app.db.client = Mock() self.client.app.db.client.list_tables = Mock(side_effect=raise_error) yield self._assert_reply( { "status": "NOT OK", "version": __version__, "clients": 0, "storage": { "status": "NOT OK", "error": "Internal error" }, "router": { "status": "NOT OK", "error": "Internal error" } }, Exception) self.client.app.db.client = safe @inlineCallbacks def _assert_reply(self, reply, exception=None): resp = yield self.client.get('/health') if exception: assert resp.get_status() == 503 self.flushLoggedErrors(exception) payload = json.loads(resp.content) assert payload == reply