diff --git a/.pubnub.yml b/.pubnub.yml index 8a9e8341..b33a8d20 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,8 +1,13 @@ name: python -version: 4.1.2 +version: 4.1.3 schema: 1 scm: github.com/pubnub/python changelog: + - version: v4.1.3 + date: Feb 25, 2019 + changes: + - type: improvement + text: implement history Message Counts - version: v4.1.2 date: Sep 20, 2018 changes: @@ -140,6 +145,7 @@ features: - STORAGE-INCLUDE-TIMETOKEN - STORAGE-START-END - STORAGE-COUNT + - STORAGE-MESSAGE-COUNT time: - TIME-TIME subscribe: @@ -161,7 +167,6 @@ supported-platforms: - Windows 7 or later, amd64, 386 editors: - python 2.7.13 - - python 3.3.6 - python 3.4.5 - python 3.5.2 - python 3.6.0 @@ -175,7 +180,6 @@ supported-platforms: - Windows 7 or later, amd64, 386 editors: - python 2.7.13 - - python 3.3.6 - python 3.4.5 - python 3.5.2 - python 3.6.0 diff --git a/.travis.yml b/.travis.yml index 9b5c8e71..114bdb2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: python python: - "2.7" - - "3.3" - "3.4" - "3.5" - "3.6" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d8db3a1..3192a57a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [4.1.3](https://github.com/pubnub/python/tree/v4.1.3) + + [Full Changelog](https://github.com/pubnub/python/compare/v4.1.2...v4.1.3) + +- 🐛Implement history message counts ## [4.1.2](https://github.com/pubnub/python/tree/v4.1.2) diff --git a/DEVELOPER.md b/DEVELOPER.md index 04eb64b0..3fa58e18 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -1,7 +1,7 @@ # Developers manual ## Supported Python versions -We support Python 2.7 and >=3.3 +We support Python 2.7 and >=3.4 ## Supported platforms We maintain and test our SDK using Travis.CI and Ubuntu. diff --git a/README.md b/README.md index e7cef28b..ce184878 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![PyPI](https://img.shields.io/pypi/pyversions/pubnub.svg)](https://pypi.python.org/pypi/pubnub/) [![Docs](https://img.shields.io/badge/docs-online-blue.svg)](https://www.pubnub.com/docs/python/pubnub-python-sdk-v4) -The SDK supports Python 2.7, 3.3, 3.4, 3.5, 3.6, 3.7 and pypy. +The SDK supports Python 2.7, 3.4, 3.5, 3.6, 3.7 and pypy. ## Documentation diff --git a/bandit.yml b/bandit.yml new file mode 100644 index 00000000..75d550c3 --- /dev/null +++ b/bandit.yml @@ -0,0 +1 @@ +skips: ['B101'] diff --git a/examples/twisted/basic_usage.py b/examples/twisted/basic_usage.py index c4821910..3b9fcd2d 100644 --- a/examples/twisted/basic_usage.py +++ b/examples/twisted/basic_usage.py @@ -15,7 +15,7 @@ def main(): def my_publish_callback(result, status): # Check whether request successfully completed or not if not status.is_error(): - envelope = result # NOQA:W292 + envelope = result # noqa pass # Message successfully published to specified channel. else: pass # Handle message publish error. Check 'category' property to find out possible issue diff --git a/pubnub/endpoints/channel_groups/add_channel_to_channel_group.py b/pubnub/endpoints/channel_groups/add_channel_to_channel_group.py index e9fda920..37c60831 100644 --- a/pubnub/endpoints/channel_groups/add_channel_to_channel_group.py +++ b/pubnub/endpoints/channel_groups/add_channel_to_channel_group.py @@ -34,8 +34,8 @@ def custom_params(self): return {'add': utils.join_items(self._channels)} def build_path(self): - return AddChannelToChannelGroup.ADD_PATH % ( - self.pubnub.config.subscribe_key, utils.url_encode(self._channel_group)) + return AddChannelToChannelGroup.ADD_PATH % ( + self.pubnub.config.subscribe_key, utils.url_encode(self._channel_group)) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/channel_groups/list_channels_in_channel_group.py b/pubnub/endpoints/channel_groups/list_channels_in_channel_group.py index dfd36e26..fea060e7 100644 --- a/pubnub/endpoints/channel_groups/list_channels_in_channel_group.py +++ b/pubnub/endpoints/channel_groups/list_channels_in_channel_group.py @@ -25,8 +25,8 @@ def custom_params(self): return {} def build_path(self): - return ListChannelsInChannelGroup.LIST_PATH % ( - self.pubnub.config.subscribe_key, utils.url_encode(self._channel_group)) + return ListChannelsInChannelGroup.LIST_PATH % ( + self.pubnub.config.subscribe_key, utils.url_encode(self._channel_group)) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/channel_groups/remove_channel_from_channel_group.py b/pubnub/endpoints/channel_groups/remove_channel_from_channel_group.py index 0a4a0a97..85f878a3 100644 --- a/pubnub/endpoints/channel_groups/remove_channel_from_channel_group.py +++ b/pubnub/endpoints/channel_groups/remove_channel_from_channel_group.py @@ -34,8 +34,8 @@ def custom_params(self): return {'remove': utils.join_items(self._channels)} def build_path(self): - return RemoveChannelFromChannelGroup.REMOVE_PATH % ( - self.pubnub.config.subscribe_key, utils.url_encode(self._channel_group)) + return RemoveChannelFromChannelGroup.REMOVE_PATH % ( + self.pubnub.config.subscribe_key, utils.url_encode(self._channel_group)) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/channel_groups/remove_channel_group.py b/pubnub/endpoints/channel_groups/remove_channel_group.py index 79a64ea9..903dbe67 100644 --- a/pubnub/endpoints/channel_groups/remove_channel_group.py +++ b/pubnub/endpoints/channel_groups/remove_channel_group.py @@ -25,8 +25,8 @@ def custom_params(self): return {} def build_path(self): - return RemoveChannelGroup.REMOVE_PATH % ( - self.pubnub.config.subscribe_key, utils.url_encode(self._channel_group)) + return RemoveChannelGroup.REMOVE_PATH % ( + self.pubnub.config.subscribe_key, utils.url_encode(self._channel_group)) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/endpoint.py b/pubnub/endpoints/endpoint.py index 35995801..9848d950 100644 --- a/pubnub/endpoints/endpoint.py +++ b/pubnub/endpoints/endpoint.py @@ -193,7 +193,7 @@ def validate_secret_key(self): raise PubNubException(pn_error=PNERR_SECRET_KEY_MISSING) def validate_channel(self): - if self._channel is None or len(self._channel) is 0: + if self._channel is None or len(self._channel) == 0: raise PubNubException(pn_error=PNERR_CHANNEL_MISSING) def validate_channels_and_groups(self): diff --git a/pubnub/endpoints/history.py b/pubnub/endpoints/history.py index a42baec2..ddb9c80c 100644 --- a/pubnub/endpoints/history.py +++ b/pubnub/endpoints/history.py @@ -71,10 +71,10 @@ def custom_params(self): return params def build_path(self): - return History.HISTORY_PATH % ( - self.pubnub.config.subscribe_key, - utils.url_encode(self._channel) - ) + return History.HISTORY_PATH % ( + self.pubnub.config.subscribe_key, + utils.url_encode(self._channel) + ) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/history_delete.py b/pubnub/endpoints/history_delete.py index 2bbe8d8a..6036b6f1 100644 --- a/pubnub/endpoints/history_delete.py +++ b/pubnub/endpoints/history_delete.py @@ -36,10 +36,10 @@ def custom_params(self): return params def build_path(self): - return HistoryDelete.HISTORY_DELETE_PATH % ( - self.pubnub.config.subscribe_key, - utils.url_encode(self._channel) - ) + return HistoryDelete.HISTORY_DELETE_PATH % ( + self.pubnub.config.subscribe_key, + utils.url_encode(self._channel) + ) def http_method(self): return HttpMethod.DELETE diff --git a/pubnub/endpoints/message_count.py b/pubnub/endpoints/message_count.py new file mode 100644 index 00000000..474063c8 --- /dev/null +++ b/pubnub/endpoints/message_count.py @@ -0,0 +1,66 @@ +from pubnub import utils +from pubnub.endpoints.endpoint import Endpoint +from pubnub.enums import HttpMethod, PNOperationType +from pubnub.exceptions import PubNubException +from pubnub.models.consumer.message_count import PNMessageCountResult + + +class MessageCount(Endpoint): + MESSAGE_COUNT_PATH = '/v3/history/sub-key/%s/message-counts/%s' + + def __init__(self, pubnub): + Endpoint.__init__(self, pubnub) + self._channel = [] + self._channels_timetoken = [] + + def channel(self, channel): + utils.extend_list(self._channel, channel) + return self + + def channel_timetokens(self, timetokens): + timetokens = [str(item) for item in timetokens] + utils.extend_list(self._channels_timetoken, timetokens) + return self + + def custom_params(self): + params = {} + if len(self._channels_timetoken) > 0: + if len(self._channels_timetoken) > 1: + params['channelsTimetoken'] = utils.join_items(self._channels_timetoken) + else: + params['timetoken'] = self._channels_timetoken[0] + return params + + def build_path(self): + return MessageCount.MESSAGE_COUNT_PATH % ( + self.pubnub.config.subscribe_key, + utils.join_channels(self._channel) + ) + + def http_method(self): + return HttpMethod.GET + + def is_auth_required(self): + return True + + def validate_params(self): + self.validate_subscribe_key() + self.validate_channel() + + if len(self._channels_timetoken) != len(self._channel): + raise PubNubException('The number of channels and the number of timetokens do not match.') + + def create_response(self, result): # pylint: disable=W0221 + return PNMessageCountResult(result) + + def request_timeout(self): + return self.pubnub.config.non_subscribe_request_timeout + + def connect_timeout(self): + return self.pubnub.config.connect_timeout + + def operation_type(self): + return PNOperationType.PNMessageCountOperation + + def name(self): + return "Message Count" diff --git a/pubnub/endpoints/presence/get_state.py b/pubnub/endpoints/presence/get_state.py index 8f66cb26..ad6d8c7e 100644 --- a/pubnub/endpoints/presence/get_state.py +++ b/pubnub/endpoints/presence/get_state.py @@ -30,11 +30,11 @@ def custom_params(self): return params def build_path(self): - return GetState.GET_STATE_PATH % ( - self.pubnub.config.subscribe_key, - utils.join_channels(self._channels), - utils.url_encode(self.pubnub.uuid) - ) + return GetState.GET_STATE_PATH % ( + self.pubnub.config.subscribe_key, + utils.join_channels(self._channels), + utils.url_encode(self.pubnub.uuid) + ) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/presence/leave.py b/pubnub/endpoints/presence/leave.py index 8dfe20a0..0023a859 100644 --- a/pubnub/endpoints/presence/leave.py +++ b/pubnub/endpoints/presence/leave.py @@ -39,7 +39,7 @@ def custom_params(self): return params def build_path(self): - return Leave.LEAVE_PATH % (self.pubnub.config.subscribe_key, utils.join_channels(self._channels)) + return Leave.LEAVE_PATH % (self.pubnub.config.subscribe_key, utils.join_channels(self._channels)) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/presence/set_state.py b/pubnub/endpoints/presence/set_state.py index 77edb369..9e6c259d 100644 --- a/pubnub/endpoints/presence/set_state.py +++ b/pubnub/endpoints/presence/set_state.py @@ -46,11 +46,11 @@ def custom_params(self): return params def build_path(self): - return SetState.SET_STATE_PATH % ( - self.pubnub.config.subscribe_key, - utils.join_channels(self._channels), - utils.url_encode(self.pubnub.uuid) - ) + return SetState.SET_STATE_PATH % ( + self.pubnub.config.subscribe_key, + utils.join_channels(self._channels), + utils.url_encode(self.pubnub.uuid) + ) def http_method(self): return HttpMethod.GET @@ -66,7 +66,7 @@ def validate_params(self): raise PubNubException(pn_error=PNERR_STATE_MISSING) def create_response(self, envelope): - if 'status' in envelope and envelope['status'] is 200: + if 'status' in envelope and envelope['status'] == 200: return PNSetStateResult(envelope['payload']) else: return envelope diff --git a/pubnub/endpoints/push/add_channels_to_push.py b/pubnub/endpoints/push/add_channels_to_push.py index 4345ed3f..50d94b63 100644 --- a/pubnub/endpoints/push/add_channels_to_push.py +++ b/pubnub/endpoints/push/add_channels_to_push.py @@ -39,8 +39,8 @@ def custom_params(self): return params def build_path(self): - return AddChannelsToPush.ADD_PATH % ( - self.pubnub.config.subscribe_key, self._device_id) + return AddChannelsToPush.ADD_PATH % ( + self.pubnub.config.subscribe_key, self._device_id) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/push/list_push_provisions.py b/pubnub/endpoints/push/list_push_provisions.py index 91abe007..04c78a46 100644 --- a/pubnub/endpoints/push/list_push_provisions.py +++ b/pubnub/endpoints/push/list_push_provisions.py @@ -33,8 +33,8 @@ def custom_params(self): return params def build_path(self): - return ListPushProvisions.LIST_PATH % ( - self.pubnub.config.subscribe_key, self._device_id) + return ListPushProvisions.LIST_PATH % ( + self.pubnub.config.subscribe_key, self._device_id) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/push/remove_channels_from_push.py b/pubnub/endpoints/push/remove_channels_from_push.py index 9d7a185f..063d4151 100644 --- a/pubnub/endpoints/push/remove_channels_from_push.py +++ b/pubnub/endpoints/push/remove_channels_from_push.py @@ -36,8 +36,8 @@ def custom_params(self): return params def build_path(self): - return RemoveChannelsFromPush.REMOVE_PATH % ( - self.pubnub.config.subscribe_key, self._device_id) + return RemoveChannelsFromPush.REMOVE_PATH % ( + self.pubnub.config.subscribe_key, self._device_id) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/push/remove_device.py b/pubnub/endpoints/push/remove_device.py index 84a6429a..2c4c6924 100644 --- a/pubnub/endpoints/push/remove_device.py +++ b/pubnub/endpoints/push/remove_device.py @@ -33,8 +33,8 @@ def custom_params(self): return params def build_path(self): - return RemoveDeviceFromPush.REMOVE_PATH % ( - self.pubnub.config.subscribe_key, self._device_id) + return RemoveDeviceFromPush.REMOVE_PATH % ( + self.pubnub.config.subscribe_key, self._device_id) def http_method(self): return HttpMethod.GET diff --git a/pubnub/endpoints/time.py b/pubnub/endpoints/time.py index 3199ef32..3504ad68 100644 --- a/pubnub/endpoints/time.py +++ b/pubnub/endpoints/time.py @@ -10,7 +10,7 @@ def custom_params(self): return {} def build_path(self): - return Time.TIME_PATH + return Time.TIME_PATH def http_method(self): return HttpMethod.GET diff --git a/pubnub/enums.py b/pubnub/enums.py index 4bb7f0ba..9c25f8d7 100644 --- a/pubnub/enums.py +++ b/pubnub/enums.py @@ -59,6 +59,7 @@ class PNOperationType(object): PNAccessManagerGrant = 21 PNAccessManagerRevoke = 22 PNHistoryDeleteOperation = 23 + PNMessageCountOperation = 24 class PNHeartbeatNotificationOptions(object): diff --git a/pubnub/managers.py b/pubnub/managers.py index 08492c8a..946789ef 100644 --- a/pubnub/managers.py +++ b/pubnub/managers.py @@ -421,6 +421,7 @@ def endpoint_name_for_operation(operation_type): PNOperationType.PNHistoryOperation: 'hist', PNOperationType.PNHistoryDeleteOperation: 'hist', + PNOperationType.PNMessageCountOperation: 'mc', PNOperationType.PNUnsubscribeOperation: 'pres', PNOperationType.PNWhereNowOperation: 'pres', diff --git a/pubnub/models/consumer/message_count.py b/pubnub/models/consumer/message_count.py new file mode 100644 index 00000000..7a13709c --- /dev/null +++ b/pubnub/models/consumer/message_count.py @@ -0,0 +1,12 @@ +class PNMessageCountResult(object): + def __init__(self, result): + """ + Representation of message count server response + + :param result: result of message count operation + """ + self._result = result + self.channels = result['channels'] + + def __str__(self): + return "Message count for channels: {}".format(self.channels) diff --git a/pubnub/pubnub_asyncio.py b/pubnub/pubnub_asyncio.py index 08e3d927..46d4b634 100644 --- a/pubnub/pubnub_asyncio.py +++ b/pubnub/pubnub_asyncio.py @@ -480,7 +480,7 @@ def _perform_heartbeat_loop(self): if heartbeat_verbosity == PNHeartbeatNotificationOptions.ALL: self._listener_manager.announce_status(envelope.status) - except PubNubAsyncioException as e: + except PubNubAsyncioException: pass # TODO: check correctness # if e.status is not None and e.status.category == PNStatusCategory.PNTimeoutCategory: diff --git a/pubnub/pubnub_core.py b/pubnub/pubnub_core.py index 925533f3..55302455 100644 --- a/pubnub/pubnub_core.py +++ b/pubnub/pubnub_core.py @@ -22,6 +22,7 @@ from .endpoints.presence.here_now import HereNow from .endpoints.presence.where_now import WhereNow from .endpoints.history_delete import HistoryDelete +from .endpoints.message_count import MessageCount from .endpoints.push.add_channels_to_push import AddChannelsToPush from .endpoints.push.remove_channels_from_push import RemoveChannelsFromPush @@ -157,6 +158,9 @@ def remove_device_from_push(self): def history(self): return History(self) + def message_counts(self): + return MessageCount(self) + def time(self): return Time(self) diff --git a/pubnub/pubnub_tornado.py b/pubnub/pubnub_tornado.py index bd3bea43..f30ff537 100644 --- a/pubnub/pubnub_tornado.py +++ b/pubnub/pubnub_tornado.py @@ -502,8 +502,7 @@ def _register_heartbeat_timer(self): super(TornadoSubscriptionManager, self)._register_heartbeat_timer() self._heartbeat_periodic_callback = PeriodicCallback( stack_context.wrap(self._perform_heartbeat_loop), - self._pubnub.config.heartbeat_interval * - TornadoSubscriptionManager.HEARTBEAT_INTERVAL_MULTIPLIER, + self._pubnub.config.heartbeat_interval * TornadoSubscriptionManager.HEARTBEAT_INTERVAL_MULTIPLIER, self._pubnub.ioloop) self._heartbeat_periodic_callback.start() diff --git a/pubnub/request_handlers/requests_handler.py b/pubnub/request_handlers/requests_handler.py index 9491695a..452d2add 100644 --- a/pubnub/request_handlers/requests_handler.py +++ b/pubnub/request_handlers/requests_handler.py @@ -2,6 +2,7 @@ import threading import requests import six +import json # noqa # pylint: disable=W0611 from requests import Session from requests.adapters import HTTPAdapter @@ -15,6 +16,12 @@ from pubnub.request_handlers.base import BaseRequestHandler from pubnub.structures import RequestOptions, PlatformOptions, ResponseInfo, Envelope +try: + from json.decoder import JSONDecodeError +except ImportError: + JSONDecodeError = ValueError + + logger = logging.getLogger("pubnub") @@ -144,12 +151,15 @@ def _build_envelope(self, p_options, e_options): err = PNERR_SERVER_ERROR else: err = PNERR_CLIENT_ERROR - + try: + response = res.json() + except JSONDecodeError: + response = None return Envelope( result=None, status=e_options.create_status( category=status_category, - response=res.json(), + response=response, response_info=response_info, exception=PubNubException( pn_error=err, diff --git a/pubnub/utils.py b/pubnub/utils.py index d098e37d..ba399084 100644 --- a/pubnub/utils.py +++ b/pubnub/utils.py @@ -57,7 +57,7 @@ def uuid(): def split_items(items_string): - if len(items_string) is 0: + if len(items_string) == 0: return [] else: return items_string.split(",") diff --git a/requirements-dev.txt b/requirements-dev.txt index b834c92f..4d2c6f81 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,4 @@ -pytest -pytest-cov codacy-coverage pycryptodomex -flake8 +flake8==3.6.0 -e git://github.com/pubnub/vcrpy@twisted#egg=vcrpy diff --git a/requirements-pypy-dev.txt b/requirements-pypy-dev.txt index c2177e8f..2c13de5d 100644 --- a/requirements-pypy-dev.txt +++ b/requirements-pypy-dev.txt @@ -1 +1,3 @@ tornado==4.5.3 +pytest==4.3.0 +pytest-cov<2.6.0 diff --git a/requirements27-dev.txt b/requirements27-dev.txt index 6f61555f..d2374bf1 100644 --- a/requirements27-dev.txt +++ b/requirements27-dev.txt @@ -1,3 +1,5 @@ +pytest==4.3.0 tornado==4.5.3 twisted pyopenssl +pytest-cov<2.6.0 diff --git a/requirements33-dev.txt b/requirements33-dev.txt deleted file mode 100644 index c2177e8f..00000000 --- a/requirements33-dev.txt +++ /dev/null @@ -1 +0,0 @@ -tornado==4.5.3 diff --git a/requirements34-dev.txt b/requirements34-dev.txt index b816915b..8928275d 100644 --- a/requirements34-dev.txt +++ b/requirements34-dev.txt @@ -1,4 +1,6 @@ +pytest==3.10.1 pytest-asyncio==0.5.0 +pytest-cov<2.6.0 tornado==4.5.3 aiohttp==2.3.10 typing==3.6.4 diff --git a/requirements35-dev.txt b/requirements35-dev.txt index 5dda7e18..709ef952 100644 --- a/requirements35-dev.txt +++ b/requirements35-dev.txt @@ -1,3 +1,5 @@ +pytest==4.3.0 pytest-asyncio tornado==4.5.3 aiohttp==2.3.10 +pytest-cov<2.6.0 diff --git a/requirements36-dev.txt b/requirements36-dev.txt index 5dda7e18..cd949156 100644 --- a/requirements36-dev.txt +++ b/requirements36-dev.txt @@ -1,3 +1,5 @@ +pytest==4.3.0 pytest-asyncio tornado==4.5.3 aiohttp==2.3.10 +pytest-cov diff --git a/requirements37-dev.txt b/requirements37-dev.txt index 5dda7e18..cd949156 100644 --- a/requirements37-dev.txt +++ b/requirements37-dev.txt @@ -1,3 +1,5 @@ +pytest==4.3.0 pytest-asyncio tornado==4.5.3 aiohttp==2.3.10 +pytest-cov diff --git a/scripts/install.sh b/scripts/install.sh index 82048376..b0d60c6e 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -2,7 +2,6 @@ pip install -r requirements-dev.txt if [[ $TRAVIS_PYTHON_VERSION == 2.7* ]]; then pip install -r requirements27-dev.txt; fi -if [[ $TRAVIS_PYTHON_VERSION == 3.3* ]]; then pip install -r requirements33-dev.txt; fi if [[ $TRAVIS_PYTHON_VERSION == 3.4* ]]; then pip install -r requirements34-dev.txt; fi if [[ $TRAVIS_PYTHON_VERSION == 3.5* ]]; then pip install -r requirements35-dev.txt; fi if [[ $TRAVIS_PYTHON_VERSION == 3.6* ]]; then pip install -r requirements36-dev.txt; fi diff --git a/scripts/run-tests.py b/scripts/run-tests.py index cecae903..aa21ff3c 100755 --- a/scripts/run-tests.py +++ b/scripts/run-tests.py @@ -30,9 +30,6 @@ def run(command): if version.startswith('2.7') or version.startswith('anaconda2'): run("%s,*asyncio*,*python_v35*,examples/" % fcmn) run('%s --ignore=tests/integrational/asyncio/ --ignore=tests/integrational/twisted/ --ignore=tests/integrational/python_v35/' % tcmn) -elif version.startswith('3.3'): - run("%s,*asyncio*,*python_v35*" % fcmn) - run('%s--ignore=tests/integrational/asyncio/ --ignore=tests/integrational/twisted/ --ignore=tests/integrational/python_v35/' % tcmn) elif version.startswith('3.4'): run("%s,*python_v35*,examples" % fcmn) run('%s--ignore=tests/integrational/python_v35/ --ignore=tests/integrational/twisted/' % tcmn) diff --git a/setup.py b/setup.py index 9263e500..dd7d3c3f 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='pubnub', - version='4.1.2', + version='4.1.3', description='PubNub Real-time push service in the cloud', author='PubNub', author_email='support@pubnub.com', @@ -14,7 +14,6 @@ 'Intended Audience :: Developers', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', diff --git a/tests/functional/test_audit.py b/tests/functional/test_audit.py index a02e25c6..d7429348 100644 --- a/tests/functional/test_audit.py +++ b/tests/functional/test_audit.py @@ -31,19 +31,19 @@ def test_audit_channel(self): self.assertEquals(self.audit.build_path(), Audit.AUDIT_PATH % pnconf_pam.subscribe_key) + pam_args = utils.prepare_pam_arguments({ + 'timestamp': 123, + 'channel': 'ch', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + sign_input = pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + "audit\n" + pam_args self.assertEqual(self.audit.build_params_callback()({}), { 'pnsdk': sdk_name, 'uuid': self.pubnub.uuid, 'timestamp': '123', 'channel': 'ch', - 'signature': utils.sign_sha256(pnconf_pam.secret_key, - pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + - "audit\n" + utils.prepare_pam_arguments({ - 'timestamp': 123, - 'channel': 'ch', - 'pnsdk': sdk_name, - 'uuid': self.pubnub.uuid - })) + 'signature': utils.sign_sha256(pnconf_pam.secret_key, sign_input) }) def test_audit_channel_group(self): @@ -51,17 +51,17 @@ def test_audit_channel_group(self): self.assertEquals(self.audit.build_path(), Audit.AUDIT_PATH % pnconf_pam.subscribe_key) + pam_args = utils.prepare_pam_arguments({ + 'timestamp': 123, + 'channel-group': 'gr1,gr2', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + sign_input = pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + "audit\n" + pam_args self.assertEqual(self.audit.build_params_callback()({}), { 'pnsdk': sdk_name, 'uuid': self.pubnub.uuid, 'timestamp': '123', 'channel-group': 'gr1,gr2', - 'signature': utils.sign_sha256(pnconf_pam.secret_key, - pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + - "audit\n" + utils.prepare_pam_arguments({ - 'timestamp': 123, - 'channel-group': 'gr1,gr2', - 'pnsdk': sdk_name, - 'uuid': self.pubnub.uuid - })) + 'signature': utils.sign_sha256(pnconf_pam.secret_key, sign_input) }) diff --git a/tests/functional/test_grant.py b/tests/functional/test_grant.py index c0b26633..5a735421 100644 --- a/tests/functional/test_grant.py +++ b/tests/functional/test_grant.py @@ -31,6 +31,16 @@ def test_grant_read_and_write_to_channel(self): self.assertEquals(self.grant.build_path(), Grant.GRANT_PATH % pnconf_pam.subscribe_key) + pam_args = utils.prepare_pam_arguments({ + 'r': '1', + 'w': '1', + 'ttl': '7', + 'timestamp': 123, + 'channel': 'ch', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + sign_input = pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + "grant\n" + pam_args self.assertEqual(self.grant.build_params_callback()({}), { 'pnsdk': sdk_name, 'uuid': self.pubnub.uuid, @@ -39,17 +49,7 @@ def test_grant_read_and_write_to_channel(self): 'ttl': '7', 'timestamp': '123', 'channel': 'ch', - 'signature': utils.sign_sha256(pnconf_pam.secret_key, - pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + - "grant\n" + utils.prepare_pam_arguments({ - 'r': '1', - 'w': '1', - 'ttl': '7', - 'timestamp': 123, - 'channel': 'ch', - 'pnsdk': sdk_name, - 'uuid': self.pubnub.uuid - })) + 'signature': utils.sign_sha256(pnconf_pam.secret_key, sign_input) }) def test_grant_read_and_write_to_channel_group(self): @@ -57,6 +57,15 @@ def test_grant_read_and_write_to_channel_group(self): self.assertEquals(self.grant.build_path(), Grant.GRANT_PATH % pnconf_pam.subscribe_key) + pam_args = utils.prepare_pam_arguments({ + 'r': '1', + 'w': '1', + 'timestamp': 123, + 'channel-group': 'gr1,gr2', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + sign_input = pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + "grant\n" + pam_args self.assertEqual(self.grant.build_params_callback()({}), { 'pnsdk': sdk_name, 'uuid': self.pubnub.uuid, @@ -64,14 +73,5 @@ def test_grant_read_and_write_to_channel_group(self): 'w': '1', 'timestamp': '123', 'channel-group': 'gr1,gr2', - 'signature': utils.sign_sha256(pnconf_pam.secret_key, - pnconf_pam.subscribe_key + "\n" + pnconf_pam.publish_key + "\n" + - "grant\n" + utils.prepare_pam_arguments({ - 'r': '1', - 'w': '1', - 'timestamp': 123, - 'channel-group': 'gr1,gr2', - 'pnsdk': sdk_name, - 'uuid': self.pubnub.uuid - })) + 'signature': utils.sign_sha256(pnconf_pam.secret_key, sign_input) }) diff --git a/tests/functional/test_message_count.py b/tests/functional/test_message_count.py new file mode 100644 index 00000000..3e829bad --- /dev/null +++ b/tests/functional/test_message_count.py @@ -0,0 +1,48 @@ +import pytest + +from pubnub.pubnub import PubNub +from pubnub.pnconfiguration import PNConfiguration +from pubnub.endpoints.message_count import MessageCount +from pubnub.exceptions import PubNubException + + +SUB_KEY = 'bla' + + +@pytest.fixture +def mc(): + config = PNConfiguration() + config.subscribe_key = SUB_KEY + return PubNub(config).message_counts() + + +def test_single_channel(mc): + mc.channel('chan') + assert mc.build_path() == MessageCount.MESSAGE_COUNT_PATH % (SUB_KEY, 'chan') + + with pytest.raises(PubNubException): + mc.validate_params() + mc.channel_timetokens([11]) + mc.validate_params() + + params = mc.custom_params() + assert 'timetoken' in params + assert params['timetoken'] == '11' + assert 'channelsTimetoken' not in params + + +def test_multi_channels(mc): + chans = 'chan,chan_2' + mc.channel(chans) + assert mc.build_path() == MessageCount.MESSAGE_COUNT_PATH % (SUB_KEY, chans) + + mc.channel_timetokens([11]) + with pytest.raises(PubNubException): + mc.validate_params() + mc.channel_timetokens([12]) + mc.validate_params() + + params = mc.custom_params() + assert 'channelsTimetoken' in params + assert params['channelsTimetoken'] == '11,12' + assert 'timetoken' not in params diff --git a/tests/functional/test_revoke.py b/tests/functional/test_revoke.py index be41073b..017c1c68 100644 --- a/tests/functional/test_revoke.py +++ b/tests/functional/test_revoke.py @@ -35,6 +35,16 @@ def test_revoke_to_channel(self): self.assertEquals(self.revoke.build_path(), Revoke.GRANT_PATH % pnconf.subscribe_key) + pam_args = utils.prepare_pam_arguments({ + 'timestamp': 123, + 'channel': 'ch', + 'r': '0', + 'w': '0', + 'm': '0', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + sign_input = pnconf.subscribe_key + "\n" + pnconf.publish_key + "\n" + "grant\n" + pam_args self.assertEqual(self.revoke.build_params_callback()({}), { 'pnsdk': sdk_name, 'uuid': self.pubnub.uuid, @@ -43,17 +53,7 @@ def test_revoke_to_channel(self): 'r': '0', 'w': '0', 'm': '0', - 'signature': utils.sign_sha256(pnconf.secret_key, - pnconf.subscribe_key + "\n" + pnconf.publish_key + "\n" + - "grant\n" + utils.prepare_pam_arguments({ - 'timestamp': 123, - 'channel': 'ch', - 'r': '0', - 'w': '0', - 'm': '0', - 'pnsdk': sdk_name, - 'uuid': self.pubnub.uuid - })) + 'signature': utils.sign_sha256(pnconf.secret_key, sign_input) }) def test_revoke_read_to_channel(self): @@ -67,6 +67,16 @@ def test_grant_read_and_write_to_channel_group(self): self.assertEquals(self.revoke.build_path(), Revoke.GRANT_PATH % pnconf.subscribe_key) + pam_args = utils.prepare_pam_arguments({ + 'r': '0', + 'w': '0', + 'm': '0', + 'timestamp': 123, + 'channel-group': 'gr1,gr2', + 'pnsdk': sdk_name, + 'uuid': self.pubnub.uuid + }) + sign_input = pnconf.subscribe_key + "\n" + pnconf.publish_key + "\n" + "grant\n" + pam_args self.assertEqual(self.revoke.build_params_callback()({}), { 'pnsdk': sdk_name, 'uuid': self.pubnub.uuid, @@ -75,15 +85,5 @@ def test_grant_read_and_write_to_channel_group(self): 'm': '0', 'timestamp': '123', 'channel-group': 'gr1,gr2', - 'signature': utils.sign_sha256(pnconf.secret_key, - pnconf.subscribe_key + "\n" + pnconf.publish_key + "\n" + - "grant\n" + utils.prepare_pam_arguments({ - 'r': '0', - 'w': '0', - 'm': '0', - 'timestamp': 123, - 'channel-group': 'gr1,gr2', - 'pnsdk': sdk_name, - 'uuid': self.pubnub.uuid - })) + 'signature': utils.sign_sha256(pnconf.secret_key, sign_input) }) diff --git a/tests/helper.py b/tests/helper.py index a9e9558e..f43134c2 100644 --- a/tests/helper.py +++ b/tests/helper.py @@ -54,6 +54,11 @@ pnconf_ssl.subscribe_key = sub_key pnconf_ssl.ssl = True +message_count_config = PNConfiguration() +message_count_config.publish_key = 'demo-36' +message_count_config.subscribe_key = 'demo-36' +message_count_config.origin = 'balancer1g.bronze.aws-pdx-1.ps.pn' + def pnconf_copy(): return copy(pnconf) @@ -79,6 +84,10 @@ def pnconf_ssl_copy(): return copy(pnconf_ssl) +def pnconf_mc_copy(): + return copy(message_count_config) + + sdk_name = "Python-UnitTest" diff --git a/tests/integrational/asyncio/test_message_count.py b/tests/integrational/asyncio/test_message_count.py new file mode 100644 index 00000000..b6cacb47 --- /dev/null +++ b/tests/integrational/asyncio/test_message_count.py @@ -0,0 +1,51 @@ +import pytest + +from pubnub.pubnub_asyncio import PubNubAsyncio, AsyncioEnvelope +from pubnub.models.consumer.message_count import PNMessageCountResult +from pubnub.models.consumer.common import PNStatus +from tests.helper import pnconf_mc_copy +from tests.integrational.vcr_helper import pn_vcr + + +@pytest.fixture +def pn(event_loop): + config = pnconf_mc_copy() + config.enable_subscribe = False + pn = PubNubAsyncio(config, custom_event_loop=event_loop) + yield pn + pn.stop() + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/message_count/single.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk', 'l_cg', 'l_pub']) +@pytest.mark.asyncio +def test_single_channel(pn): + chan = 'unique_asyncio' + envelope = yield from pn.publish().channel(chan).message('bla').future() + time = envelope.result.timetoken - 10 + envelope = yield from pn.message_counts().channel(chan).channel_timetokens([time]).future() + + assert(isinstance(envelope, AsyncioEnvelope)) + assert not envelope.status.is_error() + assert envelope.result.channels[chan] == 1 + assert isinstance(envelope.result, PNMessageCountResult) + assert isinstance(envelope.status, PNStatus) + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/message_count/multi.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk', 'l_cg', 'l_pub']) +@pytest.mark.asyncio +def test_multiple_channels(pn): + chan_1 = 'unique_asyncio_1' + chan_2 = 'unique_asyncio_2' + chans = ','.join([chan_1, chan_2]) + envelope = yield from pn.publish().channel(chan_1).message('something').future() + time = envelope.result.timetoken - 10 + envelope = yield from pn.message_counts().channel(chans).channel_timetokens([time, time]).future() + + assert(isinstance(envelope, AsyncioEnvelope)) + assert not envelope.status.is_error() + assert envelope.result.channels[chan_1] == 1 + assert envelope.result.channels[chan_2] == 0 + assert isinstance(envelope.result, PNMessageCountResult) + assert isinstance(envelope.status, PNStatus) diff --git a/tests/integrational/fixtures/asyncio/message_count/multi.yaml b/tests/integrational/fixtures/asyncio/message_count/multi.yaml new file mode 100644 index 00000000..8b0a4036 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/message_count/multi.yaml @@ -0,0 +1,39 @@ +interactions: +- request: + body: null + headers: + User-Agent: [PubNub-Python-Asyncio/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/publish/demo-36/demo-36/0/unique_asyncio_1/0/%22something%22 + response: + body: {string: '[1,"Sent","15510391962937056"]'} + headers: {Access-Control-Allow-Methods: GET, Access-Control-Allow-Origin: '*', + Cache-Control: no-cache, Connection: keep-alive, Content-Length: '30', Content-Type: text/javascript; + charset="UTF-8", Date: 'Sun, 24 Feb 2019 20:13:16 GMT'} + status: {code: 200, message: OK} + url: !!python/object/new:yarl.URL + state: !!python/tuple + - !!python/object/new:urllib.parse.SplitResult [http, balancer1g.bronze.aws-pdx-1.ps.pn, + /publish/demo-36/demo-36/0/unique_asyncio_1/0/%22something%22, seqn=1&pnsdk=PubNub-Python-Asyncio%2F4.1.0&uuid=d2a546ca-037c-499a-9d87-35951bbbd289, + ''] +- request: + body: null + headers: + User-Agent: [PubNub-Python-Asyncio/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/v3/history/sub-key/demo-36/message-counts/unique_asyncio_1,unique_asyncio_2?channelsTimetoken=15510391962937046%2C15510391962937046 + response: + body: {string: '{"status": 200, "error": false, "error_message": "", "channels": + {"unique_asyncio_1":1,"unique_asyncio_2":0}}'} + headers: {Accept-Ranges: bytes, Access-Control-Allow-Methods: 'GET, DELETE, OPTIONS', + Access-Control-Allow-Origin: '*', Age: '0', Cache-Control: no-cache, Connection: keep-alive, + Content-Length: '109', Content-Type: text/javascript; charset="UTF-8", Date: 'Sun, + 24 Feb 2019 20:13:16 GMT', Server: Pubnub} + status: {code: 200, message: OK} + url: !!python/object/new:yarl.URL + state: !!python/tuple + - !!python/object/new:urllib.parse.SplitResult [http, balancer1g.bronze.aws-pdx-1.ps.pn, + '/v3/history/sub-key/demo-36/message-counts/unique_asyncio_1,unique_asyncio_2', + 'channelsTimetoken=15510391962937046,15510391962937046&pnsdk=PubNub-Python-Asyncio%2F4.1.0&uuid=d2a546ca-037c-499a-9d87-35951bbbd289&l_pub=0.37061548233032227', + ''] +version: 1 diff --git a/tests/integrational/fixtures/asyncio/message_count/single.yaml b/tests/integrational/fixtures/asyncio/message_count/single.yaml new file mode 100644 index 00000000..1dc0f4a9 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/message_count/single.yaml @@ -0,0 +1,38 @@ +interactions: +- request: + body: null + headers: + User-Agent: [PubNub-Python-Asyncio/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/publish/demo-36/demo-36/0/unique_asyncio/0/%22bla%22 + response: + body: {string: '[1,"Sent","15510391957007182"]'} + headers: {Access-Control-Allow-Methods: GET, Access-Control-Allow-Origin: '*', + Cache-Control: no-cache, Connection: keep-alive, Content-Length: '30', Content-Type: text/javascript; + charset="UTF-8", Date: 'Sun, 24 Feb 2019 20:13:15 GMT'} + status: {code: 200, message: OK} + url: !!python/object/new:yarl.URL + state: !!python/tuple + - !!python/object/new:urllib.parse.SplitResult [http, balancer1g.bronze.aws-pdx-1.ps.pn, + /publish/demo-36/demo-36/0/unique_asyncio/0/%22bla%22, seqn=1&pnsdk=PubNub-Python-Asyncio%2F4.1.0&uuid=68f7b4f4-c169-4a49-b09d-7c68e22049b8, + ''] +- request: + body: null + headers: + User-Agent: [PubNub-Python-Asyncio/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/v3/history/sub-key/demo-36/message-counts/unique_asyncio?timetoken=15510391957007172 + response: + body: {string: '{"status": 200, "error": false, "error_message": "", "channels": + {"unique_asyncio":1}}'} + headers: {Accept-Ranges: bytes, Access-Control-Allow-Methods: 'GET, DELETE, OPTIONS', + Access-Control-Allow-Origin: '*', Age: '0', Cache-Control: no-cache, Connection: keep-alive, + Content-Length: '86', Content-Type: text/javascript; charset="UTF-8", Date: 'Sun, + 24 Feb 2019 20:13:15 GMT', Server: Pubnub} + status: {code: 200, message: OK} + url: !!python/object/new:yarl.URL + state: !!python/tuple + - !!python/object/new:urllib.parse.SplitResult [http, balancer1g.bronze.aws-pdx-1.ps.pn, + /v3/history/sub-key/demo-36/message-counts/unique_asyncio, timetoken=15510391957007172&pnsdk=PubNub-Python-Asyncio%2F4.1.0&uuid=68f7b4f4-c169-4a49-b09d-7c68e22049b8&l_pub=0.4618048667907715, + ''] +version: 1 diff --git a/tests/integrational/fixtures/native_sync/message_count/multi.yaml b/tests/integrational/fixtures/native_sync/message_count/multi.yaml new file mode 100644 index 00000000..5eb94029 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/message_count/multi.yaml @@ -0,0 +1,46 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/publish/demo-36/demo-36/0/unique_sync_1/0/%22something%22 + response: + body: {string: '[1,"Sent","15510379567122102"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 24 Feb 2019 19:52:36 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/v3/history/sub-key/demo-36/message-counts/unique_sync_1,unique_sync_2?channelsTimetoken=15510379567122092%2C15510379567122092 + response: + body: {string: '{"status": 200, "error": false, "error_message": "", "channels": + {"unique_sync_1":1,"unique_sync_2":0}}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: ['GET, DELETE, OPTIONS'] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['103'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 24 Feb 2019 19:52:37 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/native_sync/message_count/single.yaml b/tests/integrational/fixtures/native_sync/message_count/single.yaml new file mode 100644 index 00000000..15e4d048 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/message_count/single.yaml @@ -0,0 +1,46 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/publish/demo-36/demo-36/0/unique_sync/0/%22bla%22 + response: + body: {string: '[1,"Sent","15510379559483751"]'} + headers: + Access-Control-Allow-Methods: [GET] + Access-Control-Allow-Origin: ['*'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['30'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 24 Feb 2019 19:52:35 GMT'] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [PubNub-Python/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/v3/history/sub-key/demo-36/message-counts/unique_sync?timetoken=15510379559483741 + response: + body: {string: '{"status": 200, "error": false, "error_message": "", "channels": + {"unique_sync":1}}'} + headers: + Accept-Ranges: [bytes] + Access-Control-Allow-Methods: ['GET, DELETE, OPTIONS'] + Access-Control-Allow-Origin: ['*'] + Age: ['0'] + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['83'] + Content-Type: [text/javascript; charset="UTF-8"] + Date: ['Sun, 24 Feb 2019 19:52:36 GMT'] + Server: [Pubnub] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/integrational/fixtures/tornado/message_count/multi.yaml b/tests/integrational/fixtures/tornado/message_count/multi.yaml new file mode 100644 index 00000000..bf463fcf --- /dev/null +++ b/tests/integrational/fixtures/tornado/message_count/multi.yaml @@ -0,0 +1,78 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/publish/demo-36/demo-36/0/unique_asyncio_1/0/%22something%22 + response: + body: {string: '[1,"Sent","15510394390136005"]'} + headers: + - !!python/tuple + - Date + - ['Sun, 24 Feb 2019 20:17:19 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://balancer1g.bronze.aws-pdx-1.ps.pn/publish/demo-36/demo-36/0/unique_asyncio_1/0/%22something%22?seqn=1&pnsdk=PubNub-Python-Tornado%2F4.1.0&uuid=367fcb65-053e-4790-ba94-dcc0d4e56750 +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/v3/history/sub-key/demo-36/message-counts/unique_asyncio_1,unique_asyncio_2?channelsTimetoken=15510394390135995%2C15510394390135995 + response: + body: {string: '{"status": 200, "error": false, "error_message": "", "channels": + {"unique_asyncio_1":1,"unique_asyncio_2":0}}'} + headers: + - !!python/tuple + - Date + - ['Sun, 24 Feb 2019 20:17:19 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['109'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['GET, DELETE, OPTIONS'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Server + - [Pubnub] + status: {code: 200, message: OK} + url: http://balancer1g.bronze.aws-pdx-1.ps.pn/v3/history/sub-key/demo-36/message-counts/unique_asyncio_1,unique_asyncio_2?channelsTimetoken=15510394390135995,15510394390135995&pnsdk=PubNub-Python-Tornado%2F4.1.0&uuid=367fcb65-053e-4790-ba94-dcc0d4e56750&l_pub=0.368375301361084 +version: 1 diff --git a/tests/integrational/fixtures/tornado/message_count/single.yaml b/tests/integrational/fixtures/tornado/message_count/single.yaml new file mode 100644 index 00000000..db7a6b7c --- /dev/null +++ b/tests/integrational/fixtures/tornado/message_count/single.yaml @@ -0,0 +1,78 @@ +interactions: +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/publish/demo-36/demo-36/0/unique_tornado/0/%22bla%22 + response: + body: {string: '[1,"Sent","15510394397882441"]'} + headers: + - !!python/tuple + - Date + - ['Sun, 24 Feb 2019 20:17:19 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['30'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - [GET] + status: {code: 200, message: OK} + url: http://balancer1g.bronze.aws-pdx-1.ps.pn/publish/demo-36/demo-36/0/unique_tornado/0/%22bla%22?seqn=1&pnsdk=PubNub-Python-Tornado%2F4.1.0&uuid=e2282d1f-2682-4d11-9722-721d1a555bdb +- request: + body: null + headers: + Accept-Encoding: [utf-8] + User-Agent: [PubNub-Python-Tornado/4.1.0] + method: GET + uri: http://balancer1g.bronze.aws-pdx-1.ps.pn/v3/history/sub-key/demo-36/message-counts/unique_tornado?timetoken=15510394397882431 + response: + body: {string: '{"status": 200, "error": false, "error_message": "", "channels": + {"unique_tornado":1}}'} + headers: + - !!python/tuple + - Date + - ['Sun, 24 Feb 2019 20:17:20 GMT'] + - !!python/tuple + - Content-Type + - [text/javascript; charset="UTF-8"] + - !!python/tuple + - Content-Length + - ['86'] + - !!python/tuple + - Connection + - [close] + - !!python/tuple + - Cache-Control + - [no-cache] + - !!python/tuple + - Access-Control-Allow-Origin + - ['*'] + - !!python/tuple + - Access-Control-Allow-Methods + - ['GET, DELETE, OPTIONS'] + - !!python/tuple + - Accept-Ranges + - [bytes] + - !!python/tuple + - Age + - ['0'] + - !!python/tuple + - Server + - [Pubnub] + status: {code: 200, message: OK} + url: http://balancer1g.bronze.aws-pdx-1.ps.pn/v3/history/sub-key/demo-36/message-counts/unique_tornado?timetoken=15510394397882431&pnsdk=PubNub-Python-Tornado%2F4.1.0&uuid=e2282d1f-2682-4d11-9722-721d1a555bdb&l_pub=0.36996030807495117 +version: 1 diff --git a/tests/integrational/native_sync/test_message_count.py b/tests/integrational/native_sync/test_message_count.py new file mode 100644 index 00000000..6c91fdd8 --- /dev/null +++ b/tests/integrational/native_sync/test_message_count.py @@ -0,0 +1,48 @@ +import pytest + +from pubnub.pubnub import PubNub +from pubnub.models.consumer.message_count import PNMessageCountResult +from pubnub.models.consumer.common import PNStatus +from pubnub.structures import Envelope +from tests.helper import pnconf_mc_copy +from tests.integrational.vcr_helper import pn_vcr + + +@pytest.fixture +def pn(): + config = pnconf_mc_copy() + config.enable_subscribe = False + return PubNub(config) + + +@pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/message_count/single.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +def test_single_channel(pn): + chan = 'unique_sync' + envelope = pn.publish().channel(chan).message('bla').sync() + time = envelope.result.timetoken - 10 + envelope = pn.message_counts().channel(chan).channel_timetokens([time]).sync() + + assert(isinstance(envelope, Envelope)) + assert not envelope.status.is_error() + assert envelope.result.channels[chan] == 1 + assert isinstance(envelope.result, PNMessageCountResult) + assert isinstance(envelope.status, PNStatus) + + +@pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/message_count/multi.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk']) +def test_multiple_channels(pn): + chan_1 = 'unique_sync_1' + chan_2 = 'unique_sync_2' + chans = ','.join([chan_1, chan_2]) + envelope = pn.publish().channel(chan_1).message('something').sync() + time = envelope.result.timetoken - 10 + envelope = pn.message_counts().channel(chans).channel_timetokens([time, time]).sync() + + assert(isinstance(envelope, Envelope)) + assert not envelope.status.is_error() + assert envelope.result.channels[chan_1] == 1 + assert envelope.result.channels[chan_2] == 0 + assert isinstance(envelope.result, PNMessageCountResult) + assert isinstance(envelope.status, PNStatus) diff --git a/tests/integrational/native_threads/test_publish.py b/tests/integrational/native_threads/test_publish.py index 4d7baafe..6667049b 100644 --- a/tests/integrational/native_threads/test_publish.py +++ b/tests/integrational/native_threads/test_publish.py @@ -144,7 +144,7 @@ def test_invalid_key(self): assert self.status.is_error() assert self.status.category is PNStatusCategory.PNBadRequestCategory - assert self.status.original_response[0] is 0 + assert self.status.original_response[0] == 0 assert self.status.original_response[1] == 'Invalid Key' assert "HTTP Client Error (400):" in str(self.status.error_data.exception) assert "Invalid Key" in str(self.status.error_data.exception) diff --git a/tests/integrational/tornado/test_message_count.py b/tests/integrational/tornado/test_message_count.py new file mode 100644 index 00000000..98814262 --- /dev/null +++ b/tests/integrational/tornado/test_message_count.py @@ -0,0 +1,53 @@ +import tornado +from tornado.testing import AsyncTestCase + +from pubnub.pubnub_tornado import PubNubTornado, TornadoEnvelope +from pubnub.models.consumer.message_count import PNMessageCountResult +from pubnub.models.consumer.common import PNStatus +from tests.helper import pnconf_mc_copy +from tests.integrational.vcr_helper import pn_vcr + + +class TestMessageCount(AsyncTestCase): + def setUp(self): + AsyncTestCase.setUp(self) + config = pnconf_mc_copy() + config.enable_subscribe = False + self.pn = PubNubTornado(config, custom_ioloop=self.io_loop) + + @pn_vcr.use_cassette('tests/integrational/fixtures/tornado/message_count/single.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk', 'l_cg', 'l_pub']) + @tornado.testing.gen_test + def test_single_channel(self): + chan = 'unique_tornado' + envelope = yield self.pn.publish().channel(chan).message('bla').future() + time = envelope.result.timetoken - 10 + envelope = yield self.pn.message_counts().channel(chan).channel_timetokens([time]).future() + + assert(isinstance(envelope, TornadoEnvelope)) + assert not envelope.status.is_error() + assert envelope.result.channels[chan] == 1 + assert isinstance(envelope.result, PNMessageCountResult) + assert isinstance(envelope.status, PNStatus) + + self.pn.stop() + + @pn_vcr.use_cassette('tests/integrational/fixtures/tornado/message_count/multi.yaml', + filter_query_parameters=['uuid', 'seqn', 'pnsdk', 'l_cg', 'l_pub']) + @tornado.testing.gen_test + def test_multiple_channels(self): + chan_1 = 'unique_asyncio_1' + chan_2 = 'unique_asyncio_2' + chans = ','.join([chan_1, chan_2]) + envelope = yield self.pn.publish().channel(chan_1).message('something').future() + time = envelope.result.timetoken - 10 + envelope = yield self.pn.message_counts().channel(chans).channel_timetokens([time, time]).future() + + assert(isinstance(envelope, TornadoEnvelope)) + assert not envelope.status.is_error() + assert envelope.result.channels[chan_1] == 1 + assert envelope.result.channels[chan_2] == 0 + assert isinstance(envelope.result, PNMessageCountResult) + assert isinstance(envelope.status, PNStatus) + + self.pn.stop() diff --git a/tests/integrational/vcr_helper.py b/tests/integrational/vcr_helper.py index 8c8017a5..8999f84e 100644 --- a/tests/integrational/vcr_helper.py +++ b/tests/integrational/vcr_helper.py @@ -102,7 +102,7 @@ def string_list_in_path_matcher(r1, r2, positions=None): else: assert v == path2[k] - except (AssertionError, IndexError) as e: + except (AssertionError, IndexError): return False except Exception as e: print("Non-Assertion Exception: %s" % e) @@ -150,7 +150,7 @@ def string_list_in_query_matcher(r1, r2, list_keys=None, filter_keys=None): else: assert v == list2[ik][1] - except (AssertionError, IndexError) as e: + except (AssertionError, IndexError): return False except Exception as e: print("Non-Assertion Exception: %s" % e)