def test_ssl_should_be_boolean(self):
        Config(app_id=u'4', key=u'key', secret=u'secret', ssl=False)
        Config(app_id=u'4', key=u'key', secret=u'secret', ssl=True)

        self.assertRaises(
            TypeError,
            lambda: Config(app_id=u'4', key=u'key', secret=u'secret', ssl=4))
    def test_host_behaviour(self):
        conf = Config(app_id=u'4', key=u'key', secret=u'secret', ssl=True)
        self.assertEqual(conf.host, u'api.pusherapp.com',
                         u'default host should be correct')

        conf = Config(app_id=u'4',
                      key=u'key',
                      secret=u'secret',
                      ssl=True,
                      cluster=u'eu')
        self.assertEqual(conf.host, u'api-eu.pusher.com',
                         u'host should be overriden by cluster setting')

        conf = Config(app_id=u'4',
                      key=u'key',
                      secret=u'secret',
                      ssl=True,
                      host=u'foo')
        self.assertEqual(conf.host, u'foo',
                         u'host should be overriden by host setting')

        conf = Config(app_id=u'4',
                      key=u'key',
                      secret=u'secret',
                      ssl=True,
                      cluster=u'eu',
                      host=u'plah')
        self.assertEqual(conf.host, u'plah',
                         u'host should be used in preference to cluster')
 def test_secret_should_be_text(self):
     self.assertRaises(
         TypeError,
         lambda: Config(app_id=u'4', key=u'key', secret=4, ssl=False))
     self.assertRaises(
         TypeError, lambda: Config(
             app_id=u'4', key=u'key', secret=b'secret', ssl=False))
    def test_port_should_be_number(self):
        Config(app_id=u'4', key=u'key', secret=u'secret', ssl=True, port=400)

        self.assertRaises(
            TypeError, lambda: Config(app_id=u'4',
                                      key=u'key',
                                      secret=u'secret',
                                      ssl=True,
                                      port=u'400'))
    def test_port_behaviour(self):
        conf = Config(app_id=u'4', key=u'key', secret=u'secret', ssl=True)
        self.assertEqual(conf.port, 443, u'port should be 443 for ssl')

        conf = Config(app_id=u'4', key=u'key', secret=u'secret', ssl=False)
        self.assertEqual(conf.port, 80, u'port should be 80 for non ssl')

        conf = Config(app_id=u'4',
                      key=u'key',
                      secret=u'secret',
                      ssl=False,
                      port=4000)
        self.assertEqual(conf.port, 4000,
                         u'the port setting override the default')
    def test_host_should_be_text(self):
        Config(app_id=u'4',
               key=u'key',
               secret=u'secret',
               ssl=True,
               host=u'foo')

        self.assertRaises(
            TypeError, lambda: Config(app_id=u'4',
                                      key=u'key',
                                      secret=u'secret',
                                      ssl=True,
                                      host=b'foo'))
        self.assertRaises(
            TypeError, lambda: Config(
                app_id=u'4', key=u'key', secret=u'secret', ssl=True, host=4))
    def test_initialize_from_env(self):
        with mock.patch.object(os, "environ", new={"PUSHER_URL": "https://*****:*****@somehost/apps/42"}):
            conf = Config.from_env()
            self.assertEqual(conf.ssl, True)
            self.assertEqual(conf.key, u"plah")
            self.assertEqual(conf.secret, u"bob")
            self.assertEqual(conf.host, u"somehost")
            self.assertEqual(conf.app_id, u"42")

        with mock.patch.object(os, "environ", new={"PUSHER_DSN": "https://*****:*****@somehost/apps/42"}):
            conf = Config.from_env("PUSHER_DSN")
            self.assertEqual(conf.ssl, True)
            self.assertEqual(conf.key, u"plah")
            self.assertEqual(conf.secret, u"bob")
            self.assertEqual(conf.host, u"somehost")
            self.assertEqual(conf.app_id, u"42")
    def test_validate_webhook_bad_types(self):
        conf = Config.from_url(u'http://*****:*****@host/apps/4')

        conf.validate_webhook(u'key', u'signature', u'body')

        # These things are meant to be human readable, so enforcing being text is
        # sensible.

        with mock.patch('time.time') as time_mock:
            self.assertRaises(
                TypeError,
                lambda: conf.validate_webhook(4, u'signature', u'body'))
            self.assertRaises(
                TypeError,
                lambda: conf.validate_webhook(b'test', u'signature', u'body'))
            self.assertRaises(
                TypeError, lambda: conf.validate_webhook(u'key', 4, u'body'))
            self.assertRaises(
                TypeError,
                lambda: conf.validate_webhook(u'key', b'signature', u'body'))
            self.assertRaises(
                TypeError,
                lambda: conf.validate_webhook(u'key', u'signature', 4))
            self.assertRaises(
                TypeError,
                lambda: conf.validate_webhook(u'key', u'signature', b'body'))

        time_mock.assert_not_called()
    def test_cluster_should_be_text(self):
        Config(app_id=u'4',
               key=u'key',
               secret=u'secret',
               ssl=True,
               cluster=u'eu')

        self.assertRaises(
            TypeError, lambda: Config(app_id=u'4',
                                      key=u'key',
                                      secret=u'secret',
                                      ssl=True,
                                      cluster=b'eu'))
        self.assertRaises(
            TypeError, lambda: Config(
                app_id=u'4', key=u'key', secret=u'secret', ssl=True, cluster=4)
        )
    def test_validate_webhook_bad_time(self):
        conf = Config.from_url(u"http://*****:*****@host/apps/4")

        body = u'{"time_ms": 1000000}'
        signature = six.text_type(hmac.new(conf.secret.encode("utf8"), body.encode("utf8"), hashlib.sha256).hexdigest())

        with mock.patch("time.time", return_value=1301):
            self.assertEqual(conf.validate_webhook(conf.key, signature, body), None)
    def test_initialize_from_url(self):
        self.assertRaises(TypeError, lambda: Config.from_url(4))
        self.assertRaises(TypeError, lambda: Config.from_url(b"http://*****:*****@host/apps/4"))
        self.assertRaises(Exception, lambda: Config.from_url(u"httpsahsutaeh"))

        conf = Config.from_url(u"http://*****:*****@host/apps/4")
        self.assertEqual(conf.ssl, False)
        self.assertEqual(conf.key, u"foo")
        self.assertEqual(conf.secret, u"bar")
        self.assertEqual(conf.host, u"host")
        self.assertEqual(conf.app_id, u"4")

        conf = Config.from_url(u"https://*****:*****@host/apps/4")
        self.assertEqual(conf.ssl, True)
        self.assertEqual(conf.key, u"foo")
        self.assertEqual(conf.secret, u"bar")
        self.assertEqual(conf.host, u"host")
        self.assertEqual(conf.app_id, u"4")
    def test_initialize_from_url(self):
        self.assertRaises(TypeError, lambda: Config.from_url(4))
        self.assertRaises(
            TypeError, lambda: Config.from_url(b'http://*****:*****@host/apps/4'))
        self.assertRaises(Exception, lambda: Config.from_url(u'httpsahsutaeh'))

        conf = Config.from_url(u'http://*****:*****@host/apps/4')
        self.assertEqual(conf.ssl, False)
        self.assertEqual(conf.key, u'foo')
        self.assertEqual(conf.secret, u'bar')
        self.assertEqual(conf.host, u'host')
        self.assertEqual(conf.app_id, u'4')

        conf = Config.from_url(u'https://*****:*****@host/apps/4')
        self.assertEqual(conf.ssl, True)
        self.assertEqual(conf.key, u'foo')
        self.assertEqual(conf.secret, u'bar')
        self.assertEqual(conf.host, u'host')
        self.assertEqual(conf.app_id, u'4')
    def test_validate_webhook_bad_signature(self):
        conf = Config.from_url(u"http://*****:*****@host/apps/4")

        body = u"some body"
        signature = u"some signature"

        with mock.patch("time.time") as time_mock:
            self.assertEqual(conf.validate_webhook(conf.key, signature, body), None)

        time_mock.assert_not_called()
    def test_validate_webhook_bad_time(self):
        conf = Config.from_url(u'http://*****:*****@host/apps/4')

        body = u'{"time_ms": 1000000}'
        signature = six.text_type(
            hmac.new(conf.secret.encode('utf8'), body.encode('utf8'),
                     hashlib.sha256).hexdigest())

        with mock.patch('time.time', return_value=1301):
            self.assertEqual(conf.validate_webhook(conf.key, signature, body),
                             None)
    def test_authenticate_subscription_for_private_channels(self):
        conf = Config.from_url(u'http://*****:*****@host/apps/4')

        expected = {
            u'auth':
            u"foo:076740bd063f0299742a73bc5aac88900e5f35cb0185a1facbf45d326b5b204b"
        }

        self.assertEqual(
            conf.authenticate_subscription(u'private-channel', u'34523'),
            expected)
    def test_validate_webhook_bad_signature(self):
        conf = Config.from_url(u'http://*****:*****@host/apps/4')

        body = u'some body'
        signature = u'some signature'

        with mock.patch('time.time') as time_mock:
            self.assertEqual(conf.validate_webhook(conf.key, signature, body),
                             None)

        time_mock.assert_not_called()
    def test_validate_webhook_bad_key(self):
        conf = Config.from_url(u"http://*****:*****@host/apps/4")

        body = u"some body"
        signature = six.text_type(
            hmac.new(conf.secret.encode(u"utf8"), body.encode(u"utf8"), hashlib.sha256).hexdigest()
        )

        with mock.patch("time.time") as time_mock:
            self.assertEqual(conf.validate_webhook(u"badkey", signature, body), None)

        time_mock.assert_not_called()
    def test_authenticate_subscription_types(self):
        conf = Config.from_url(u'http://*****:*****@host/apps/4')

        self.assertRaises(
            TypeError,
            lambda: conf.authenticate_subscription(b'plah', u'34554'))
        self.assertRaises(
            TypeError,
            lambda: conf.authenticate_subscription(u'plah', b'324435'))
        self.assertRaises(
            ValueError,
            lambda: conf.authenticate_subscription(u'::', u'345345'))
    def test_validate_webhook_bad_key(self):
        conf = Config.from_url(u'http://*****:*****@host/apps/4')

        body = u'some body'
        signature = six.text_type(
            hmac.new(conf.secret.encode(u'utf8'), body.encode(u'utf8'),
                     hashlib.sha256).hexdigest())

        with mock.patch('time.time') as time_mock:
            self.assertEqual(conf.validate_webhook(u'badkey', signature, body),
                             None)

        time_mock.assert_not_called()
    def test_initialize_from_env(self):
        with mock.patch.object(
                os,
                'environ',
                new={'PUSHER_URL': 'https://*****:*****@somehost/apps/42'}):
            conf = Config.from_env()
            self.assertEqual(conf.ssl, True)
            self.assertEqual(conf.key, u'plah')
            self.assertEqual(conf.secret, u'bob')
            self.assertEqual(conf.host, u'somehost')
            self.assertEqual(conf.app_id, u'42')

        with mock.patch.object(
                os,
                'environ',
                new={'PUSHER_DSN': 'https://*****:*****@somehost/apps/42'}):
            conf = Config.from_env('PUSHER_DSN')
            self.assertEqual(conf.ssl, True)
            self.assertEqual(conf.key, u'plah')
            self.assertEqual(conf.secret, u'bob')
            self.assertEqual(conf.host, u'somehost')
            self.assertEqual(conf.app_id, u'42')
    def test_get_signature_generation(self):
        conf = Config.from_url(u'http://*****:*****@somehost/apps/4')

        expected = {
            u'auth_key': u'key',
            u'auth_signature': u'5c49f04a95eedc9028b1e0e8de7c2c7ad63504a0e3b5c145d2accaef6c14dbac',
            u'auth_timestamp': u'1000',
            u'auth_version': u'1.0',
            u'body_md5': u'd41d8cd98f00b204e9800998ecf8427e',
            u'foo': u'bar'
        }

        with mock.patch('time.time', return_value=1000):
            req = Request(conf, u'GET', u'/some/obscure/api', {u'foo': u'bar'})
            self.assertEqual(req.query_params, expected)
    def test_authenticate_subscription_for_presence_channels(self):
        conf = Config.from_url(u"http://*****:*****@host/apps/4")

        custom_data = {u"user_id": u"fred", u"user_info": {u"key": u"value"}}

        expected = {
            u"auth": u"foo:fbbc6d8acc85fc807bba060e2df45aba33deb8ad44cbee1633675b3ce73f4817",
            u"channel_data": u'{"user_id":"fred","user_info":{"key":"value"}}',
        }

        with mock.patch("json.dumps", return_value=expected[u"channel_data"]) as dumps_mock:
            actual = conf.authenticate_subscription(u"presence-channel", u"34543245", custom_data)

        self.assertEqual(actual, expected)
        dumps_mock.assert_called_once_with(custom_data)
    def test_get_signature_generation(self):
        conf = Config.from_url(u'http://*****:*****@somehost/apps/4')

        expected = {
            u'auth_key': u'key',
            u'auth_signature':
            u'5c49f04a95eedc9028b1e0e8de7c2c7ad63504a0e3b5c145d2accaef6c14dbac',
            u'auth_timestamp': u'1000',
            u'auth_version': u'1.0',
            u'body_md5': u'd41d8cd98f00b204e9800998ecf8427e',
            u'foo': u'bar'
        }

        with mock.patch('time.time', return_value=1000):
            req = Request(conf, u'GET', u'/some/obscure/api', {u'foo': u'bar'})
            self.assertEqual(req.query_params, expected)
    def test_validate_webhook_bad_types(self):
        conf = Config.from_url(u"http://*****:*****@host/apps/4")

        conf.validate_webhook(u"key", u"signature", u"body")

        # These things are meant to be human readable, so enforcing being text is
        # sensible.

        with mock.patch("time.time") as time_mock:
            self.assertRaises(TypeError, lambda: conf.validate_webhook(4, u"signature", u"body"))
            self.assertRaises(TypeError, lambda: conf.validate_webhook(b"test", u"signature", u"body"))
            self.assertRaises(TypeError, lambda: conf.validate_webhook(u"key", 4, u"body"))
            self.assertRaises(TypeError, lambda: conf.validate_webhook(u"key", b"signature", u"body"))
            self.assertRaises(TypeError, lambda: conf.validate_webhook(u"key", u"signature", 4))
            self.assertRaises(TypeError, lambda: conf.validate_webhook(u"key", u"signature", b"body"))

        time_mock.assert_not_called()
    def test_post_signature_generation(self):
        conf = Config.from_url(u'http://*****:*****@somehost/apps/4')

        expected = {
            u'auth_key': u'key',
            u'auth_signature': u'e05fa4cafee86311746ee3981d5581a5e4e87c27bbab0aeb1059e2df5c90258b',
            u'auth_timestamp': u'1000',
            u'auth_version': u'1.0',
            u'body_md5': u'94232c5b8fc9272f6f73a1e36eb68fcf'
        }

        with mock.patch('time.time', return_value=1000):
            # patching this, because json can be unambiguously parsed, but not 
            # unambiguously generated (think whitespace).
            with mock.patch('json.dumps', return_value='{"foo": "bar"}') as json_dumps_mock:
                req = Request(conf, u'POST', u'/some/obscure/api', {u'foo': u'bar'})
                self.assertEqual(req.query_params, expected)

            json_dumps_mock.assert_called_once({u"foo": u"bar"})
    def test_authenticate_subscription_for_presence_channels(self):
        conf = Config.from_url(u'http://*****:*****@host/apps/4')

        custom_data = {u'user_id': u'fred', u'user_info': {u'key': u'value'}}

        expected = {
            u'auth':
            u"foo:fbbc6d8acc85fc807bba060e2df45aba33deb8ad44cbee1633675b3ce73f4817",
            u'channel_data':
            u"{\"user_id\":\"fred\",\"user_info\":{\"key\":\"value\"}}"
        }

        with mock.patch('json.dumps',
                        return_value=expected[u'channel_data']) as dumps_mock:
            actual = conf.authenticate_subscription(u'presence-channel',
                                                    u'34543245', custom_data)

        self.assertEqual(actual, expected)
        dumps_mock.assert_called_once_with(custom_data)
    def test_post_signature_generation(self):
        conf = Config.from_url(u'http://*****:*****@somehost/apps/4')

        expected = {
            u'auth_key': u'key',
            u'auth_signature':
            u'e05fa4cafee86311746ee3981d5581a5e4e87c27bbab0aeb1059e2df5c90258b',
            u'auth_timestamp': u'1000',
            u'auth_version': u'1.0',
            u'body_md5': u'94232c5b8fc9272f6f73a1e36eb68fcf'
        }

        with mock.patch('time.time', return_value=1000):
            # patching this, because json can be unambiguously parsed, but not
            # unambiguously generated (think whitespace).
            with mock.patch('json.dumps',
                            return_value='{"foo": "bar"}') as json_dumps_mock:
                req = Request(conf, u'POST', u'/some/obscure/api',
                              {u'foo': u'bar'})
                self.assertEqual(req.query_params, expected)

            json_dumps_mock.assert_called_once({u"foo": u"bar"})
 def setUp(self):
     self.pusher = Pusher(config=Config.from_url(u'http://*****:*****@somehost/apps/4'))
    def test_authenticate_subscription_types(self):
        conf = Config.from_url(u"http://*****:*****@host/apps/4")

        self.assertRaises(TypeError, lambda: conf.authenticate_subscription(b"plah", u"34554"))
        self.assertRaises(TypeError, lambda: conf.authenticate_subscription(u"plah", b"324435"))
        self.assertRaises(ValueError, lambda: conf.authenticate_subscription(u"::", u"345345"))
 def test_should_be_constructable(self):
     Config(app_id=u'4', key=u'key', secret=u'secret', ssl=False)
    def test_authenticate_subscription_for_private_channels(self):
        conf = Config.from_url(u"http://*****:*****@host/apps/4")

        expected = {u"auth": u"foo:076740bd063f0299742a73bc5aac88900e5f35cb0185a1facbf45d326b5b204b"}

        self.assertEqual(conf.authenticate_subscription(u"private-channel", u"34523"), expected)
 def test_ssl_should_be_required(self):
     self.assertRaises(
         TypeError,
         lambda: Config(app_id=u'4', key=u'key', secret=b'secret'))
 def setUp(self):
     self.pusher = Pusher(
         config=Config.from_url(u'http://*****:*****@somehost/apps/4'))