Exemple #1
0
class GCMRouterTestCase(unittest.TestCase):

    @patch("gcmclient.gcm.GCM", spec=gcmclient.gcm.GCM)
    def setUp(self, fgcm):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        self.gcm_config = {'s3_bucket': 'None',
                           'max_data': 32,
                           'senderid_list': {'test123':
                                             {"auth": "12345678abcdefg"}}}
        self.gcm = fgcm
        self.router = GCMRouter(settings, self.gcm_config)
        self.headers = {"content-encoding": "aesgcm",
                        "encryption": "test",
                        "encryption-key": "test"}
        # Data will most likely be binary values.
        self.notif = Notification(10, "\xab\xad\x1d\xea", dummy_chid,
                                  self.headers, 200)
        self.router_data = dict(
            router_data=dict(
                token="connect_data",
                creds=dict(senderID="test123", auth="12345678abcdefg")))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        fgcm.send.return_value = mock_result

    def tearDown(self):
        self.router.senderIDs.stop()

    def _check_error_call(self, exc, code):
        ok_(isinstance(exc, RouterException))
        eq_(exc.status_code, code)
        assert(self.router.gcm.send.called)
        self.flushLoggedErrors()

    def test_init(self):
        self.router.senderIDs.get_ID = Mock()

        def throw_ex():
            raise AttributeError
        fsenderids = Mock()
        fsenderids.choose_ID.side_effect = throw_ex
        self.assertRaises(IOError, GCMRouter, {}, {"senderIDs": fsenderids})

    def test_register(self):
        result = self.router.register("uaid", {"token": "connect_data"})
        # Check the information that will be recorded for this user
        eq_(result, {"token": "connect_data",
                     "creds": {"senderID": "test123",
                               "auth": "12345678abcdefg"}})

    def test_register_bad(self):
        self.assertRaises(RouterException, self.router.register, "uaid", {})

    def test_invalid_token(self):
        self.router.gcm = self.gcm

        (t, v) = self.router.check_token("test123")
        ok_(t)
        eq_(v, self.gcm_config['senderid_list'].keys()[0])

        (t, v) = self.router.check_token("invalid")
        eq_(t, False)
        eq_(v, self.gcm_config['senderid_list'].keys()[0])

    def test_route_notification(self):
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm.send.call_args[0][0].data
            eq_(data['body'], 'q60d6g==')
            eq_(data['enc'], 'test')
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')
        d.addCallback(check_results)
        return d

    def test_long_data(self):
        self.router.gcm = self.gcm
        badNotif = Notification(
            10, "\x01abcdefghijklmnopqrstuvwxyz0123456789", dummy_chid,
            self.headers, 200)
        d = self.router.route_notification(badNotif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 413)
            eq_(result.value.errno, 104)

        d.addBoth(check_results)
        return d

    def test_route_crypto_notification(self):
        self.router.gcm = self.gcm
        del(self.notif.headers['encryption-key'])
        self.notif.headers['crypto-key'] = 'crypto'
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_missing_enc_key_header(self):
        self.router.gcm = self.gcm
        del(self.notif.headers['encryption-key'])
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 400)

        d.addBoth(check_results)
        return d

    def test_router_bogus_headers(self):
        self.router.gcm = self.gcm
        self.notif.headers['crypto-key'] = "crypto"
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 400)

        d.addBoth(check_results)
        return d

    def test_router_missing_enc_header(self):
        self.router.gcm = self.gcm
        del(self.notif.headers['encryption'])
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 400)

        d.addBoth(check_results)
        return d

    def test_router_missing_cont_enc_header(self):
        self.router.gcm = self.gcm
        del(self.notif.headers['content-encoding'])
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 400)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        def throw_auth(arg):
            raise gcmclient.GCMAuthenticationError()
        self.gcm.send.side_effect = throw_auth
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        def throw_other(arg):
            raise Exception("oh my!")
        self.gcm.send.side_effect = throw_other
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.mock_result.canonical["old"] = "new"
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict(token="new"))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.mock_result.not_registered = {"connect_data": True}
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict())
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.mock_result.failed = dict(connect_data=True)
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.mock_result.needs_retry.return_value = True
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_no_auth(self):
        d = self.router.route_notification(self.notif,
                                           {"router_data": {"token": "abc"}})

        def check_results(fail):
            eq_(fail.value.status_code, 500)
        d.addBoth(check_results)
        return d

    def test_ammend(self):
        self.router.register("uaid", {"token": "connect_data"})
        resp = {"key": "value"}
        result = self.router.amend_msg(resp,
                                       self.router_data.get('router_data'))
        eq_({"key": "value", "senderid": "test123"},
            result)
class GCMRouterTestCase(unittest.TestCase):
    def setUp(self):
        conf = AutopushConfig(
            hostname="localhost",
            statsd_host=None,
        )
        self.gcm_config = {
            'max_data': 32,
            'ttl': 60,
            'senderIDs': {
                'test123': {
                    "auth": "12345678abcdefg"
                }
            }
        }
        self.response = Mock(spec=requests.Response)
        self.response.status_code = 200
        self.response.headers = dict()
        self.response.content = json.dumps({
            "multicast_id":
            5174939174563864884,
            "success":
            1,
            "failure":
            0,
            "canonical_ids":
            0,
            "results": [{
                "message_id": "0:1510011451922224%7a0e7efbaab8b7cc"
            }]
        })
        self.gcm = gcmclient.GCM(api_key="SomeKey")
        self.gcm._sender = Mock(return_value=self.response)
        self.router = GCMRouter(conf, self.gcm_config, SinkMetrics())
        self.router.gcm['test123'] = self.gcm
        self.headers = {
            "content-encoding": "aesgcm",
            "encryption": "test",
            "encryption-key": "test"
        }
        # Payloads are Base64-encoded.
        self.notif = WebPushNotification(
            uaid=uuid.UUID(dummy_uaid),
            channel_id=uuid.UUID(dummy_chid),
            data="q60d6g",
            headers=self.headers,
            ttl=200,
            message_id=10,
        )
        self.notif.cleanup_headers()
        self.router_data = dict(router_data=dict(
            token="connect_data",
            creds=dict(senderID="test123", auth="12345678abcdefg")))

    def _check_error_call(self, exc, code, response=None, errno=None):
        assert isinstance(exc, RouterException)
        assert exc.status_code == code
        if errno is not None:
            assert exc.errno == errno
        assert self.gcm._sender.called
        if response:
            assert exc.response_body == response
        self.flushLoggedErrors()

    def test_init(self):
        conf = AutopushConfig(
            hostname="localhost",
            statsd_host=None,
        )
        with pytest.raises(IOError):
            GCMRouter(conf, {"senderIDs": {}}, SinkMetrics())

    def test_register(self):
        router_data = {"token": "test123"}
        self.router.register("uaid", router_data=router_data, app_id="test123")
        # Check the information that will be recorded for this user
        assert router_data == {
            "token": "test123",
            "creds": {
                "senderID": "test123",
                "auth": "12345678abcdefg"
            }
        }

    def test_register_bad(self):
        with pytest.raises(RouterException):
            self.router.register("uaid", router_data={}, app_id="")
        with pytest.raises(RouterException):
            self.router.register("uaid", router_data={}, app_id=None)
        with pytest.raises(RouterException):
            self.router.register("uaid",
                                 router_data={"token": "abcd1234"},
                                 app_id="invalid123")

    def test_route_notification(self):
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert self.gcm._sender.called
            # Make sure the data was encoded as base64
            payload = json.loads(self.gcm._sender.call_args[1]['data'])
            data = payload['data']
            assert data['body'] == 'q60d6g'
            assert data['enc'] == 'test'
            assert data['chid'] == dummy_chid
            assert data['enckey'] == 'test'
            assert data['con'] == 'aesgcm'

        d.addCallback(check_results)
        return d

    def test_ttl_none(self):
        self.router.gcm['test123'] = self.gcm
        self.notif = WebPushNotification(uaid=uuid.UUID(dummy_uaid),
                                         channel_id=uuid.UUID(dummy_chid),
                                         data="q60d6g",
                                         headers=self.headers,
                                         ttl=None)
        self.notif.cleanup_headers()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert result.status_code == 201
            assert result.logged_status == 200
            assert "TTL" in result.headers
            assert self.gcm._sender.called
            # Make sure the data was encoded as base64
            payload = json.loads(self.gcm._sender.call_args[1]['data'])
            data = payload['data']
            assert data['body'] == 'q60d6g'
            assert data['enc'] == 'test'
            assert data['chid'] == dummy_chid
            assert data['enckey'] == 'test'
            assert data['con'] == 'aesgcm'
            # use the defined min TTL
            assert payload['time_to_live'] == 60

        d.addCallback(check_results)
        return d

    def test_ttl_high(self):
        self.router.gcm['test123'] = self.gcm
        self.notif = WebPushNotification(uaid=uuid.UUID(dummy_uaid),
                                         channel_id=uuid.UUID(dummy_chid),
                                         data="q60d6g",
                                         headers=self.headers,
                                         ttl=5184000)
        self.notif.cleanup_headers()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert self.gcm._sender.called
            # Make sure the data was encoded as base64
            payload = json.loads(self.gcm._sender.call_args[1]['data'])
            data = payload['data']
            assert data['body'] == 'q60d6g'
            assert data['enc'] == 'test'
            assert data['chid'] == dummy_chid
            assert data['enckey'] == 'test'
            assert data['con'] == 'aesgcm'
            # use the defined min TTL
            assert payload['time_to_live'] == 2419200

        d.addCallback(check_results)
        return d

    def test_long_data(self):
        self.router.gcm['test123'] = self.gcm
        bad_notif = WebPushNotification(
            uaid=uuid.UUID(dummy_uaid),
            channel_id=uuid.UUID(dummy_chid),
            data="\x01abcdefghijklmnopqrstuvwxyz0123456789",
            headers=self.headers,
            ttl=200)

        d = self.router.route_notification(bad_notif, self.router_data)

        def check_results(result):
            assert isinstance(result.value, RouterException)
            assert result.value.status_code == 413
            assert result.value.errno == 104

        d.addBoth(check_results)
        return d

    def test_route_crypto_notification(self):
        del (self.notif.headers['encryption_key'])
        self.notif.headers['crypto_key'] = 'crypto'
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert self.gcm._sender.called

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        self.response.status_code = 401
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500, "Server error", 901)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        self.gcm._sender.side_effect = Exception
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500, "Server error")

        d.addBoth(check_results)
        return d

    def test_router_notification_connection_error(self):
        from requests.exceptions import ConnectionError

        def throw_other(*args, **kwargs):
            raise ConnectionError("oh my!")

        self.gcm._sender.side_effect = throw_other
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 502, "Server error", 902)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.response.content = json.dumps({
            "multicast_id":
            5174939174563864884,
            "success":
            1,
            "failure":
            0,
            "canonical_ids":
            1,
            "results": [{
                "message_id": "0:1510011451922224%7a0e7efbaab8b7cc",
                "registration_id": "new",
            }]
        })
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert result.router_data == dict(token="new")
            assert self.router.metrics.increment.call_args[0][0] == (
                'notification.bridge.error')
            self.router.metrics.increment.call_args[1]['tags'].sort()
            assert self.router.metrics.increment.call_args[1]['tags'] == [
                'platform:gcm', 'reason:reregister'
            ]
            assert self.gcm._sender.called

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.response.content = json.dumps({
            "multicast_id":
            5174939174563864884,
            "success":
            1,
            "failure":
            1,
            "canonical_ids":
            0,
            "results": [{
                "error": "NotRegistered"
            }]
        })
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            assert isinstance(result, RouterResponse)
            assert result.router_data == dict()
            assert self.router.metrics.increment.call_args[0][0] == (
                'notification.bridge.error')
            self.router.metrics.increment.call_args[1]['tags'].sort()
            assert self.router.metrics.increment.call_args[1]['tags'] == [
                'platform:gcm', 'reason:unregistered'
            ]
            assert self.gcm._sender.called

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.response.content = json.dumps({
            "multicast_id":
            5174939174563864884,
            "success":
            1,
            "failure":
            1,
            "canonical_ids":
            0,
            "results": [{
                "error": "InvalidRegistration"
            }]
        })
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            assert self.router.metrics.increment.called
            assert self.router.metrics.increment.call_args[0][0] == (
                'notification.bridge.error')
            self.router.metrics.increment.call_args[1]['tags'].sort()
            assert self.router.metrics.increment.call_args[1]['tags'] == [
                'platform:gcm', 'reason:failure'
            ]
            assert fail.value.message == 'GCM unable to deliver'
            self._check_error_call(fail.value, 410)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.response.headers['Retry-After'] = "123"
        self.response.status_code = 500
        self.response.content = ""
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            assert self.router.metrics.increment.called
            assert self.router.metrics.increment.call_args[0][0] == (
                'notification.bridge.error')
            self.router.metrics.increment.call_args[1]['tags'].sort()
            assert self.router.metrics.increment.call_args[1]['tags'] == [
                'platform:gcm', 'reason:retry'
            ]
            assert fail.value.message == 'GCM failure to deliver, retry'
            self._check_error_call(fail.value, 503)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_no_auth(self):
        d = self.router.route_notification(self.notif,
                                           {"router_data": {
                                               "token": "abc"
                                           }})

        def check_results(fail):
            assert isinstance(fail.value, RouterException)
            assert fail.value.message == "Server error"
            assert fail.value.status_code == 500
            assert fail.value.errno == 900

        d.addBoth(check_results)
        return d

    def test_amend(self):
        router_data = {"token": "test123"}
        self.router.register("uaid", router_data=router_data, app_id="test123")
        resp = {"key": "value"}
        self.router.amend_endpoint_response(
            resp, self.router_data.get('router_data'))
        assert {"key": "value", "senderid": "test123"} == resp

    def test_register_invalid_token(self):
        with pytest.raises(RouterException):
            self.router.register(uaid="uaid",
                                 router_data={"token": "invalid"},
                                 app_id="invalid")
Exemple #3
0
class GCMRouterTestCase(unittest.TestCase):

    @patch("gcmclient.gcm.GCM", spec=gcmclient.gcm.GCM)
    def setUp(self, fgcm):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        gcm_config = {'s3_bucket': 'None',
                      'senderid_list': {'test123':
                                        {"auth": "12345678abcdefg"}}}
        self.gcm = fgcm
        self.router = GCMRouter(settings, gcm_config)
        self.notif = Notification(10, "data", dummy_chid, None, 200)
        self.router_data = dict(
            router_data=dict(
                token="connect_data",
                creds=dict(senderID="test123", auth="12345678abcdefg")))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        fgcm.send.return_value = mock_result

    def tearDown(self):
        self.router.senderIDs.stop()

    def _check_error_call(self, exc, code):
        ok_(isinstance(exc, RouterException))
        eq_(exc.status_code, code)
        assert(self.router.gcm.send.called)
        self.flushLoggedErrors()

    def test_init(self):
        self.router.senderIDs.get_ID = Mock()

        def throw_ex():
            raise AttributeError
        fsenderids = Mock()
        fsenderids.choose_ID.side_effect = throw_ex
        self.assertRaises(IOError, GCMRouter, {}, {"senderIDs": fsenderids})

    def test_register(self):
        result = self.router.register("uaid", {"token": "connect_data"})
        # Check the information that will be recorded for this user
        eq_(result, {"token": "connect_data",
                     "creds": {"senderID": "test123",
                               "auth": "12345678abcdefg"}})

    def test_register_bad(self):
        self.assertRaises(RouterException, self.router.register, "uaid", {})

    def test_route_notification(self):
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        def throw_auth(arg):
            raise gcmclient.GCMAuthenticationError()
        self.gcm.send.side_effect = throw_auth
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        def throw_other(arg):
            raise Exception("oh my!")
        self.gcm.send.side_effect = throw_other
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.mock_result.canonical["old"] = "new"
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict(token="new"))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.mock_result.not_registered = {"connect_data": True}
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict())
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.mock_result.failed = dict(connect_data=True)
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.mock_result.needs_retry.return_value = True
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_no_auth(self):
        d = self.router.route_notification(self.notif,
                                           {"router_data": {"token": "abc"}})

        def check_results(fail):
            eq_(fail.value.status_code, 500)
        d.addBoth(check_results)
        return d

    def test_ammend(self):
        self.router.register("uaid", {"token": "connect_data"})
        resp = {"key": "value"}
        eq_({"key": "value", "senderid": "test123"},
            self.router.amend_msg(resp))
Exemple #4
0
class GCMRouterTestCase(unittest.TestCase):

    @patch("gcmclient.gcm.GCM", spec=gcmclient.gcm.GCM)
    def setUp(self, fgcm):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        self.gcm_config = {'s3_bucket': 'None',
                           'max_data': 32,
                           'ttl': 60,
                           'senderid_list': {'test123':
                                             {"auth": "12345678abcdefg"}}}
        self.gcm = fgcm
        self.router = GCMRouter(settings, self.gcm_config)
        self.headers = {"content-encoding": "aesgcm",
                        "encryption": "test",
                        "encryption-key": "test"}
        # Payloads are Base64-encoded.
        self.notif = Notification(10, "q60d6g", dummy_chid, self.headers,
                                  200)
        self.router_data = dict(
            router_data=dict(
                token="connect_data",
                creds=dict(senderID="test123", auth="12345678abcdefg")))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        fgcm.send.return_value = mock_result

    def tearDown(self):
        self.router.senderIDs.stop()

    def _check_error_call(self, exc, code):
        ok_(isinstance(exc, RouterException))
        eq_(exc.status_code, code)
        assert(self.router.gcm.send.called)
        self.flushLoggedErrors()

    def test_init(self):
        self.router.senderIDs.get_ID = Mock()

        def throw_ex():
            raise AttributeError
        fsenderids = Mock()
        fsenderids.choose_ID.side_effect = throw_ex
        self.assertRaises(IOError, GCMRouter, {}, {"senderIDs": fsenderids})

    def test_register(self):
        result = self.router.register("uaid", {"token": "connect_data"})
        # Check the information that will be recorded for this user
        eq_(result, {"token": "connect_data",
                     "creds": {"senderID": "test123",
                               "auth": "12345678abcdefg"}})

    def test_register_bad(self):
        self.assertRaises(RouterException, self.router.register, "uaid", {})

    def test_invalid_token(self):
        self.router.gcm = self.gcm

        (t, v) = self.router.check_token("test123")
        ok_(t)
        eq_(v, self.gcm_config['senderid_list'].keys()[0])

        (t, v) = self.router.check_token("invalid")
        eq_(t, False)
        eq_(v, self.gcm_config['senderid_list'].keys()[0])

    def test_route_notification(self):
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm.send.call_args[0][0].data
            eq_(data['body'], 'q60d6g')
            eq_(data['enc'], 'test')
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')
        d.addCallback(check_results)
        return d

    def test_ttl_none(self):
        self.router.gcm = self.gcm
        self.notif = Notification(version=10,
                                  data="q60d6g",
                                  channel_id=dummy_chid,
                                  headers=self.headers,
                                  ttl=None)
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm.send.call_args[0][0].data
            options = self.router.gcm.send.call_args[0][0].options
            eq_(data['body'], 'q60d6g')
            eq_(data['enc'], 'test')
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')
            # use the defined min TTL
            eq_(options['time_to_live'], 60)
        d.addCallback(check_results)
        return d

    def test_long_data(self):
        self.router.gcm = self.gcm
        badNotif = Notification(
            10, "\x01abcdefghijklmnopqrstuvwxyz0123456789", dummy_chid,
            self.headers, 200)
        d = self.router.route_notification(badNotif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 413)
            eq_(result.value.errno, 104)

        d.addBoth(check_results)
        return d

    def test_route_crypto_notification(self):
        self.router.gcm = self.gcm
        del(self.notif.headers['encryption-key'])
        self.notif.headers['crypto-key'] = 'crypto'
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        def throw_auth(arg):
            raise gcmclient.GCMAuthenticationError()
        self.gcm.send.side_effect = throw_auth
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        def throw_other(arg):
            raise Exception("oh my!")
        self.gcm.send.side_effect = throw_other
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.mock_result.canonical["old"] = "new"
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict(token="new"))
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.mock_result.not_registered = {"connect_data": True}
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict())
            assert(self.router.gcm.send.called)
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.mock_result.failed = dict(connect_data=True)
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.mock_result.needs_retry.return_value = True
        self.router.gcm = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_no_auth(self):
        d = self.router.route_notification(self.notif,
                                           {"router_data": {"token": "abc"}})

        def check_results(fail):
            eq_(fail.value.status_code, 500)
        d.addBoth(check_results)
        return d

    def test_ammend(self):
        self.router.register("uaid", {"token": "connect_data"})
        resp = {"key": "value"}
        result = self.router.amend_msg(resp,
                                       self.router_data.get('router_data'))
        eq_({"key": "value", "senderid": "test123"},
            result)
Exemple #5
0
class GCMRouterTestCase(unittest.TestCase):
    @patch("gcmclient.gcm.GCM", spec=gcmclient.gcm.GCM)
    def setUp(self, fgcm):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        self.gcm_config = {
            'max_data': 32,
            'ttl': 60,
            'senderIDs': {
                'test123': {
                    "auth": "12345678abcdefg"
                }
            }
        }
        self.gcm = fgcm
        self.router = GCMRouter(settings, self.gcm_config)
        self.headers = {
            "content-encoding": "aesgcm",
            "encryption": "test",
            "encryption-key": "test"
        }
        # Payloads are Base64-encoded.
        self.notif = WebPushNotification(
            uaid=uuid.UUID(dummy_uaid),
            channel_id=uuid.UUID(dummy_chid),
            data="q60d6g",
            headers=self.headers,
            ttl=200,
            message_id=10,
        )
        self.notif.cleanup_headers()
        self.router_data = dict(router_data=dict(
            token="connect_data",
            creds=dict(senderID="test123", auth="12345678abcdefg")))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        fgcm.send.return_value = mock_result

    def _check_error_call(self, exc, code, response=None):
        ok_(isinstance(exc, RouterException))
        eq_(exc.status_code, code)
        ok_(self.router.gcm['test123'].send.called)
        if response:
            eq_(exc.response_body, response)
        self.flushLoggedErrors()

    def test_init(self):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        with assert_raises(IOError):
            GCMRouter(settings, {"senderIDs": {}})

    def test_register(self):
        router_data = {"token": "test123"}
        self.router.register("uaid", router_data=router_data, app_id="test123")
        # Check the information that will be recorded for this user
        eq_(
            router_data, {
                "token": "test123",
                "creds": {
                    "senderID": "test123",
                    "auth": "12345678abcdefg"
                }
            })

    def test_register_bad(self):
        with assert_raises(RouterException):
            self.router.register("uaid", router_data={}, app_id="")
        with assert_raises(RouterException):
            self.router.register("uaid", router_data={}, app_id='')
        with assert_raises(RouterException):
            self.router.register("uaid",
                                 router_data={"token": "abcd1234"},
                                 app_id="invalid123")

    @patch("gcmclient.GCM")
    def test_gcmclient_fail(self, fgcm):
        fgcm.side_effect = Exception
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        with assert_raises(IOError):
            GCMRouter(settings, {"senderIDs": {"test123": {"auth": "abcd"}}})

    def test_route_notification(self):
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            ok_(self.router.gcm['test123'].send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm['test123'].send.call_args[0][0].data
            eq_(data['body'], 'q60d6g')
            eq_(data['enc'], 'test')
            eq_(data['chid'], dummy_chid)
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')

        d.addCallback(check_results)
        return d

    def test_ttl_none(self):
        self.router.gcm['test123'] = self.gcm
        self.notif = WebPushNotification(uaid=uuid.UUID(dummy_uaid),
                                         channel_id=uuid.UUID(dummy_chid),
                                         data="q60d6g",
                                         headers=self.headers,
                                         ttl=None)
        self.notif.cleanup_headers()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.status_code, 201)
            eq_(result.logged_status, 200)
            ok_("TTL" in result.headers)
            ok_(self.router.gcm['test123'].send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm['test123'].send.call_args[0][0].data
            options = self.router.gcm['test123'].send.call_args[0][0].options
            eq_(data['body'], 'q60d6g')
            eq_(data['enc'], 'test')
            eq_(data['chid'], dummy_chid)
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')
            # use the defined min TTL
            eq_(options['time_to_live'], 60)

        d.addCallback(check_results)
        return d

    def test_ttl_high(self):
        self.router.gcm['test123'] = self.gcm
        self.notif = WebPushNotification(uaid=uuid.UUID(dummy_uaid),
                                         channel_id=uuid.UUID(dummy_chid),
                                         data="q60d6g",
                                         headers=self.headers,
                                         ttl=5184000)
        self.notif.cleanup_headers()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            ok_(self.router.gcm['test123'].send.called)
            # Make sure the data was encoded as base64
            data = self.router.gcm['test123'].send.call_args[0][0].data
            options = self.router.gcm['test123'].send.call_args[0][0].options
            eq_(data['body'], 'q60d6g')
            eq_(data['enc'], 'test')
            eq_(data['chid'], dummy_chid)
            eq_(data['enckey'], 'test')
            eq_(data['con'], 'aesgcm')
            # use the defined min TTL
            eq_(options['time_to_live'], 2419200)

        d.addCallback(check_results)
        return d

    def test_long_data(self):
        self.router.gcm['test123'] = self.gcm
        bad_notif = WebPushNotification(
            uaid=uuid.UUID(dummy_uaid),
            channel_id=uuid.UUID(dummy_chid),
            data="\x01abcdefghijklmnopqrstuvwxyz0123456789",
            headers=self.headers,
            ttl=200)

        d = self.router.route_notification(bad_notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result.value, RouterException))
            eq_(result.value.status_code, 413)
            eq_(result.value.errno, 104)

        d.addBoth(check_results)
        return d

    def test_route_crypto_notification(self):
        self.router.gcm['test123'] = self.gcm
        del (self.notif.headers['encryption_key'])
        self.notif.headers['crypto_key'] = 'crypto'
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            ok_(self.router.gcm['test123'].send.called)

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        def throw_auth(arg):
            raise gcmclient.GCMAuthenticationError()

        self.gcm.send.side_effect = throw_auth
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500, "Server error")

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        def throw_other(arg):
            raise Exception("oh my!")

        self.gcm.send.side_effect = throw_other
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500, "Server error")

        d.addBoth(check_results)
        return d

    def test_router_notification_connection_error(self):
        from requests.exceptions import ConnectionError

        def throw_other(*args, **kwargs):
            raise ConnectionError("oh my!")

        self.gcm.send.side_effect = throw_other
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 502, "Server error")

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.mock_result.canonical["old"] = "new"
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict(token="new"))
            ok_(self.router.gcm['test123'].send.called)

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.mock_result.not_registered = {"connect_data": True}
        self.router.gcm['test123'] = self.gcm
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict())
            ok_(self.router.gcm['test123'].send.called)

        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.mock_result.failed = dict(connect_data=True)
        self.router.gcm['test123'] = self.gcm
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            ok_(self.router.metrics.increment.called)
            eq_(self.router.metrics.increment.call_args[0][0],
                'updates.client.bridge.gcm.failed.failure')
            eq_(fail.value.message, 'GCM unable to deliver')
            self._check_error_call(fail.value, 410)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.mock_result.needs_retry.return_value = True
        self.router.gcm['test123'] = self.gcm
        self.router.metrics = Mock()
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            ok_(self.router.metrics.increment.called)
            eq_(self.router.metrics.increment.call_args[0][0],
                'updates.client.bridge.gcm.failed.retry')
            eq_(fail.value.message, 'GCM failure to deliver, retry')
            self._check_error_call(fail.value, 503)

        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_no_auth(self):
        d = self.router.route_notification(self.notif,
                                           {"router_data": {
                                               "token": "abc"
                                           }})

        def check_results(fail):
            eq_(fail.value.status_code, 500, "Server error")

        d.addBoth(check_results)
        return d

    def test_amend(self):
        router_data = {"token": "test123"}
        self.router.register("uaid", router_data=router_data, app_id="test123")
        resp = {"key": "value"}
        self.router.amend_endpoint_response(
            resp, self.router_data.get('router_data'))
        eq_({"key": "value", "senderid": "test123"}, resp)

    def test_register_invalid_token(self):
        with assert_raises(RouterException):
            self.router.register(uaid="uaid",
                                 router_data={"token": "invalid"},
                                 app_id="invalid")
Exemple #6
0
class GCMRouterTestCase(unittest.TestCase):
    def setUp(self):
        settings = AutopushSettings(
            hostname="localhost",
            statsd_host=None,
        )
        # Mock out GCM client
        self._old_gcm = gcmclient.GCM
        gcmclient.GCM = Mock(spec=gcmclient.GCM)

        gcm_config = {'apikey': '12345678abcdefg'}
        self.router = GCMRouter(settings, gcm_config)
        self.notif = Notification(10, "data", dummy_chid)
        self.router_data = dict(router_data=dict(token="connect_data"))
        mock_result = Mock(spec=gcmclient.gcm.Result)
        mock_result.canonical = dict()
        mock_result.failed = dict()
        mock_result.not_registered = dict()
        mock_result.needs_retry.return_value = False
        self.mock_result = mock_result
        self.router.gcm.send.return_value = mock_result

    def tearDown(self):
        gcmclient.GCM = self._old_gcm

    def _check_error_call(self, exc, code):
        ok_(isinstance(exc, RouterException))
        eq_(exc.status_code, code)
        self.router.gcm.send.assert_called()
        self.flushLoggedErrors()

    def test_register(self):
        result = self.router.register("uaid", {"token": "connect_data"})
        eq_(result, {"token": "connect_data"})

    def test_register_bad(self):
        self.assertRaises(RouterException, self.router.register, "uaid", {})

    def test_router_notification(self):
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            self.router.gcm.send.assert_called()
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_auth_error(self):
        def throw_auth(arg):
            raise gcmclient.GCMAuthenticationError()
        self.router.gcm.send.side_effect = throw_auth
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_other_error(self):
        def throw_other(arg):
            raise Exception("oh my!")
        self.router.gcm.send.side_effect = throw_other
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 500)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_id_change(self):
        self.mock_result.canonical["old"] = "new"
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict(token="new"))
            self.router.gcm.send.assert_called()
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_not_regged(self):
        self.mock_result.not_registered = {"connect_data": True}
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(result):
            ok_(isinstance(result, RouterResponse))
            eq_(result.router_data, dict())
            self.router.gcm.send.assert_called()
        d.addCallback(check_results)
        return d

    def test_router_notification_gcm_failed_items(self):
        self.mock_result.failed = dict(connect_data=True)
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d

    def test_router_notification_gcm_needs_retry(self):
        self.mock_result.needs_retry.return_value = True
        d = self.router.route_notification(self.notif, self.router_data)

        def check_results(fail):
            self._check_error_call(fail.value, 503)
        d.addBoth(check_results)
        return d