diff --git a/pubnub/pnconfiguration.py b/pubnub/pnconfiguration.py index 8ee9992a..72d3ecfa 100644 --- a/pubnub/pnconfiguration.py +++ b/pubnub/pnconfiguration.py @@ -1,3 +1,6 @@ +import warnings +from typing import Any +from copy import deepcopy from Cryptodome.Cipher import AES from pubnub.enums import PNHeartbeatNotificationOptions, PNReconnectionPolicy from pubnub.exceptions import PubNubException @@ -12,6 +15,7 @@ class PNConfiguration(object): RECONNECTION_MIN_EXPONENTIAL_BACKOFF = 1 RECONNECTION_MAX_EXPONENTIAL_BACKOFF = 32 DEFAULT_CRYPTO_MODULE = LegacyCryptoModule + _locked = False def __init__(self): # TODO: add validation @@ -48,9 +52,13 @@ def __init__(self): self.cryptor = None self.file_cryptor = None self._crypto_module = None + self.disable_config_locking = True + self._locked = False def validate(self): PNConfiguration.validate_not_empty_string(self.uuid) + if self.disable_config_locking: + warnings.warn(DeprecationWarning('Mutable config will be deprecated in the future.')) def validate_not_empty_string(value: str): assert value and isinstance(value, str) and value.strip() != "", "UUID missing or invalid type" @@ -168,3 +176,23 @@ def user_id(self): def user_id(self, user_id): PNConfiguration.validate_not_empty_string(user_id) self._uuid = user_id + + def lock(self): + self.__dict__['_locked'] = False if self.disable_config_locking else True + + def copy(self): + config_copy = deepcopy(self) + config_copy.__dict__['_locked'] = False + return config_copy + + def __setattr__(self, name: str, value: Any) -> None: + if self._locked: + warnings.warn(UserWarning('Configuration is locked. Any changes made won\'t have any effect')) + return + if name in ['uuid', 'user_id']: + PNConfiguration.validate_not_empty_string(value) + self.__dict__['_uuid'] = value + elif name in ['cipher_mode', 'fallback_cipher_mode', 'crypto_module']: + self.__dict__[f'_{name}'] = value + else: + self.__dict__[name] = value diff --git a/pubnub/pubnub_core.py b/pubnub/pubnub_core.py index e5c615e8..87c4a86a 100644 --- a/pubnub/pubnub_core.py +++ b/pubnub/pubnub_core.py @@ -1,6 +1,7 @@ import logging import time from warnings import warn +from copy import deepcopy from pubnub.endpoints.entities.membership.add_memberships import AddSpaceMembers, AddUserSpaces from pubnub.endpoints.entities.membership.update_memberships import UpdateSpaceMembers, UpdateUserSpaces from pubnub.endpoints.entities.membership.fetch_memberships import FetchSpaceMemberships, FetchUserMemberships @@ -25,6 +26,8 @@ from abc import ABCMeta, abstractmethod +from pubnub.pnconfiguration import PNConfiguration + from .endpoints.objects_v2.uuid.set_uuid import SetUuid from .endpoints.objects_v2.channel.get_all_channels import GetAllChannels from .endpoints.objects_v2.channel.get_channel import GetChannel @@ -98,8 +101,12 @@ class PubNubCore: _subscription_registry: PNSubscriptionRegistry - def __init__(self, config): - self.config = config + def __init__(self, config: PNConfiguration): + if not config.disable_config_locking: + config.lock() + self.config = deepcopy(config) + else: + self.config = config self.config.validate() self.headers = { 'User-Agent': self.sdk_name diff --git a/tests/integrational/asyncio/test_change_uuid.py b/tests/integrational/asyncio/test_change_uuid.py index 9ba9bee2..3247cbf0 100644 --- a/tests/integrational/asyncio/test_change_uuid.py +++ b/tests/integrational/asyncio/test_change_uuid.py @@ -8,33 +8,48 @@ from tests.helper import pnconf_demo_copy -@pn_vcr.use_cassette( - 'tests/integrational/fixtures/asyncio/signal/uuid.yaml', - filter_query_parameters=['seqn', 'pnsdk', 'l_sig'] -) +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/signal/uuid.json', + filter_query_parameters=['seqn', 'pnsdk', 'l_sig'], serializer='pn_json') @pytest.mark.asyncio -async def test_single_channel(event_loop): - pnconf_demo = pnconf_demo_copy() - pn = PubNubAsyncio(pnconf_demo, custom_event_loop=event_loop) +async def test_change_uuid(): + with pytest.warns(UserWarning): + pnconf = pnconf_demo_copy() + pnconf.disable_config_locking = False + pn = PubNubAsyncio(pnconf) + + chan = 'unique_sync' + envelope = await pn.signal().channel(chan).message('test').future() + + pnconf.uuid = 'new-uuid' + envelope = await pn.signal().channel(chan).message('test').future() + + assert isinstance(envelope, AsyncioEnvelope) + assert not envelope.status.is_error() + assert envelope.result.timetoken == '17224117487136760' + assert isinstance(envelope.result, PNSignalResult) + assert isinstance(envelope.status, PNStatus) + + +@pn_vcr.use_cassette('tests/integrational/fixtures/asyncio/signal/uuid_no_lock.json', + filter_query_parameters=['seqn', 'pnsdk', 'l_sig'], serializer='pn_json') +@pytest.mark.asyncio +async def test_change_uuid_no_lock(): + pnconf = pnconf_demo_copy() + pnconf.disable_config_locking = True + pn = PubNubAsyncio(pnconf) + chan = 'unique_sync' envelope = await pn.signal().channel(chan).message('test').future() - assert isinstance(envelope, AsyncioEnvelope) - assert not envelope.status.is_error() - assert envelope.result.timetoken == '15640051159323676' - assert isinstance(envelope.result, PNSignalResult) - assert isinstance(envelope.status, PNStatus) - - pnconf_demo.uuid = 'new-uuid' + pnconf.uuid = 'new-uuid' envelope = await pn.signal().channel(chan).message('test').future() + assert isinstance(envelope, AsyncioEnvelope) assert not envelope.status.is_error() - assert envelope.result.timetoken == '15640051159323677' + assert envelope.result.timetoken == '17224117494275030' assert isinstance(envelope.result, PNSignalResult) assert isinstance(envelope.status, PNStatus) - await pn.stop() - def test_uuid_validation_at_init(event_loop): with pytest.raises(AssertionError) as exception: diff --git a/tests/integrational/fixtures/asyncio/signal/uuid.json b/tests/integrational/fixtures/asyncio/signal/uuid.json new file mode 100644 index 00000000..5d5d29ce --- /dev/null +++ b/tests/integrational/fixtures/asyncio/signal/uuid.json @@ -0,0 +1,111 @@ +{ + "version": 1, + "interactions": [ + { + "request": { + "method": "GET", + "uri": "https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock", + "body": null, + "headers": { + "User-Agent": [ + "PubNub-Python/8.0.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Connection": [ + "keep-alive" + ], + "Cache-Control": [ + "no-cache" + ], + "Date": [ + "Wed, 31 Jul 2024 07:42:28 GMT" + ], + "Content-Length": [ + "30" + ], + "Content-Type": [ + "text/javascript; charset=\"UTF-8\"" + ], + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Origin": [ + "*" + ] + }, + "body": { + "string": "[1,\"Sent\",\"17224117484567462\"]" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock", + "body": null, + "headers": { + "User-Agent": [ + "PubNub-Python/8.0.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Connection": [ + "keep-alive" + ], + "Cache-Control": [ + "no-cache" + ], + "Date": [ + "Wed, 31 Jul 2024 07:42:28 GMT" + ], + "Content-Length": [ + "30" + ], + "Content-Type": [ + "text/javascript; charset=\"UTF-8\"" + ], + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Origin": [ + "*" + ] + }, + "body": { + "string": "[1,\"Sent\",\"17224117487136760\"]" + } + } + } + ] +} diff --git a/tests/integrational/fixtures/asyncio/signal/uuid.yaml b/tests/integrational/fixtures/asyncio/signal/uuid.yaml deleted file mode 100644 index 0a5e543c..00000000 --- a/tests/integrational/fixtures/asyncio/signal/uuid.yaml +++ /dev/null @@ -1,62 +0,0 @@ -interactions: -- request: - body: null - headers: - User-Agent: - - PubNub-Python-Asyncio/4.1.0 - method: GET - uri: https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock - response: - body: - string: '[1,"Sent","15640051159323676"]' - 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: Wed, 24 Jul 2019 21:51:55 GMT - PN-MsgEntityType: '1' - status: - code: 200 - message: OK - url: !!python/object/new:yarl.URL - state: !!python/tuple - - !!python/object/new:urllib.parse.SplitResult - - http - - ps.pndsn.com - - /signal/demo/demo/0/unique_sync/0/%22test%22 - - pnsdk=PubNub-Python-Asyncio%2F4.1.0&uuid=f5706789-e3a0-459e-871d-e4aed46e5458 - - '' -- request: - body: null - headers: - User-Agent: - - PubNub-Python-Asyncio/4.1.0 - method: GET - uri: https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=new-uuid - response: - body: - string: '[1,"Sent","15640051159323677"]' - 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: Wed, 24 Jul 2019 21:51:56 GMT - PN-MsgEntityType: '1' - status: - code: 200 - message: OK - url: !!python/object/new:yarl.URL - state: !!python/tuple - - !!python/object/new:urllib.parse.SplitResult - - http - - ps.pndsn.com - - /signal/demo/demo/0/unique_sync/0/%22test%22 - - pnsdk=PubNub-Python-Asyncio%2F4.1.0&uuid=f5706789-e3a0-459e-871d-e4aed46e5458 - - '' -version: 1 diff --git a/tests/integrational/fixtures/asyncio/signal/uuid_no_lock.json b/tests/integrational/fixtures/asyncio/signal/uuid_no_lock.json new file mode 100644 index 00000000..8b8421c6 --- /dev/null +++ b/tests/integrational/fixtures/asyncio/signal/uuid_no_lock.json @@ -0,0 +1,111 @@ +{ + "version": 1, + "interactions": [ + { + "request": { + "method": "GET", + "uri": "https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock", + "body": null, + "headers": { + "User-Agent": [ + "PubNub-Python/8.0.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Connection": [ + "keep-alive" + ], + "Cache-Control": [ + "no-cache" + ], + "Date": [ + "Wed, 31 Jul 2024 07:42:29 GMT" + ], + "Content-Length": [ + "30" + ], + "Content-Type": [ + "text/javascript; charset=\"UTF-8\"" + ], + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Origin": [ + "*" + ] + }, + "body": { + "string": "[1,\"Sent\",\"17224117491724049\"]" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=new-uuid", + "body": null, + "headers": { + "User-Agent": [ + "PubNub-Python/8.0.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Connection": [ + "keep-alive" + ], + "Cache-Control": [ + "no-cache" + ], + "Date": [ + "Wed, 31 Jul 2024 07:42:29 GMT" + ], + "Content-Length": [ + "30" + ], + "Content-Type": [ + "text/javascript; charset=\"UTF-8\"" + ], + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Origin": [ + "*" + ] + }, + "body": { + "string": "[1,\"Sent\",\"17224117494275030\"]" + } + } + } + ] +} diff --git a/tests/integrational/fixtures/native_sync/signal/uuid.json b/tests/integrational/fixtures/native_sync/signal/uuid.json new file mode 100644 index 00000000..5d5d29ce --- /dev/null +++ b/tests/integrational/fixtures/native_sync/signal/uuid.json @@ -0,0 +1,111 @@ +{ + "version": 1, + "interactions": [ + { + "request": { + "method": "GET", + "uri": "https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock", + "body": null, + "headers": { + "User-Agent": [ + "PubNub-Python/8.0.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Connection": [ + "keep-alive" + ], + "Cache-Control": [ + "no-cache" + ], + "Date": [ + "Wed, 31 Jul 2024 07:42:28 GMT" + ], + "Content-Length": [ + "30" + ], + "Content-Type": [ + "text/javascript; charset=\"UTF-8\"" + ], + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Origin": [ + "*" + ] + }, + "body": { + "string": "[1,\"Sent\",\"17224117484567462\"]" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock", + "body": null, + "headers": { + "User-Agent": [ + "PubNub-Python/8.0.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Connection": [ + "keep-alive" + ], + "Cache-Control": [ + "no-cache" + ], + "Date": [ + "Wed, 31 Jul 2024 07:42:28 GMT" + ], + "Content-Length": [ + "30" + ], + "Content-Type": [ + "text/javascript; charset=\"UTF-8\"" + ], + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Origin": [ + "*" + ] + }, + "body": { + "string": "[1,\"Sent\",\"17224117487136760\"]" + } + } + } + ] +} diff --git a/tests/integrational/fixtures/native_sync/signal/uuid.yaml b/tests/integrational/fixtures/native_sync/signal/uuid.yaml deleted file mode 100644 index 1f3d5a83..00000000 --- a/tests/integrational/fixtures/native_sync/signal/uuid.yaml +++ /dev/null @@ -1,76 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - PubNub-Python/4.1.0 - method: GET - uri: https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock - response: - body: - string: '[1,"Sent","15640049765289377"]' - 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: - - Wed, 24 Jul 2019 21:49:36 GMT - PN-MsgEntityType: - - '1' - 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: https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=new-uuid - response: - body: - string: '[1,"Sent","15640049765289377"]' - 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: - - Wed, 24 Jul 2019 21:49:36 GMT - PN-MsgEntityType: - - '1' - status: - code: 200 - message: OK - -version: 1 diff --git a/tests/integrational/fixtures/native_sync/signal/uuid_no_lock.json b/tests/integrational/fixtures/native_sync/signal/uuid_no_lock.json new file mode 100644 index 00000000..8b8421c6 --- /dev/null +++ b/tests/integrational/fixtures/native_sync/signal/uuid_no_lock.json @@ -0,0 +1,111 @@ +{ + "version": 1, + "interactions": [ + { + "request": { + "method": "GET", + "uri": "https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock", + "body": null, + "headers": { + "User-Agent": [ + "PubNub-Python/8.0.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Connection": [ + "keep-alive" + ], + "Cache-Control": [ + "no-cache" + ], + "Date": [ + "Wed, 31 Jul 2024 07:42:29 GMT" + ], + "Content-Length": [ + "30" + ], + "Content-Type": [ + "text/javascript; charset=\"UTF-8\"" + ], + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Origin": [ + "*" + ] + }, + "body": { + "string": "[1,\"Sent\",\"17224117491724049\"]" + } + } + }, + { + "request": { + "method": "GET", + "uri": "https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=new-uuid", + "body": null, + "headers": { + "User-Agent": [ + "PubNub-Python/8.0.0" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Accept": [ + "*/*" + ], + "Connection": [ + "keep-alive" + ] + } + }, + "response": { + "status": { + "code": 200, + "message": "OK" + }, + "headers": { + "Connection": [ + "keep-alive" + ], + "Cache-Control": [ + "no-cache" + ], + "Date": [ + "Wed, 31 Jul 2024 07:42:29 GMT" + ], + "Content-Length": [ + "30" + ], + "Content-Type": [ + "text/javascript; charset=\"UTF-8\"" + ], + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Origin": [ + "*" + ] + }, + "body": { + "string": "[1,\"Sent\",\"17224117494275030\"]" + } + } + } + ] +} diff --git a/tests/integrational/native_sync/test_change_uuid.py b/tests/integrational/native_sync/test_change_uuid.py index 35486a3d..5a813605 100644 --- a/tests/integrational/native_sync/test_change_uuid.py +++ b/tests/integrational/native_sync/test_change_uuid.py @@ -9,10 +9,32 @@ from tests.helper import pnconf_demo_copy -@pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/signal/uuid.yaml', - filter_query_parameters=['seqn', 'pnsdk']) +@pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/signal/uuid.json', + filter_query_parameters=['seqn', 'pnsdk'], serializer='pn_json') def test_change_uuid(): + with pytest.warns(UserWarning): + pnconf = pnconf_demo_copy() + pnconf.disable_config_locking = False + pn = PubNub(pnconf) + + chan = 'unique_sync' + envelope = pn.signal().channel(chan).message('test').sync() + + pnconf.uuid = 'new-uuid' + envelope = pn.signal().channel(chan).message('test').sync() + + assert isinstance(envelope, Envelope) + assert not envelope.status.is_error() + assert envelope.result.timetoken == '17224117487136760' + assert isinstance(envelope.result, PNSignalResult) + assert isinstance(envelope.status, PNStatus) + + +@pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/signal/uuid_no_lock.json', + filter_query_parameters=['seqn', 'pnsdk'], serializer='pn_json') +def test_change_uuid_no_lock(): pnconf = pnconf_demo_copy() + pnconf.disable_config_locking = True pn = PubNub(pnconf) chan = 'unique_sync' @@ -23,7 +45,7 @@ def test_change_uuid(): assert isinstance(envelope, Envelope) assert not envelope.status.is_error() - assert envelope.result.timetoken == '15640049765289377' + assert envelope.result.timetoken == '17224117494275030' assert isinstance(envelope.result, PNSignalResult) assert isinstance(envelope.status, PNStatus) diff --git a/tests/pytest.ini b/tests/pytest.ini new file mode 100644 index 00000000..9e27e99c --- /dev/null +++ b/tests/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +filterwarnings = + ignore:Mutable config will be deprecated in the future.:DeprecationWarning diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py new file mode 100644 index 00000000..0605295e --- /dev/null +++ b/tests/unit/test_config.py @@ -0,0 +1,121 @@ +import pytest + +from pubnub.pubnub import PubNub +from pubnub.pubnub_asyncio import PubNubAsyncio +from pubnub.pnconfiguration import PNConfiguration + + +class TestPubNubConfig: + def test_config_copy_with_mutability_lock(self): + config = PNConfiguration() + config.disable_config_locking = False + config.publish_key = 'demo' + config.subscribe_key = 'demo' + config.user_id = 'demo' + + pubnub = PubNub(config) + assert config is not pubnub.config + assert config.user_id == 'demo' + + def test_config_copy_with_mutability_lock_disabled(self): + config = PNConfiguration() + config.disable_config_locking = True + config.publish_key = 'demo' + config.subscribe_key = 'demo' + config.user_id = 'demo' + + pubnub = PubNub(config) + assert config is pubnub.config + assert config.user_id == 'demo' + + def test_config_mutability_lock(self): + with pytest.warns(UserWarning): + config = PNConfiguration() + config.disable_config_locking = False + config.publish_key = 'demo' + config.subscribe_key = 'demo' + config.user_id = 'demo' + + pubnub = PubNub(config) + assert config is not pubnub.config + + config.user_id = 'test' + assert pubnub.config.user_id == 'demo' + + def test_config_mutability_lock_disabled(self): + config = PNConfiguration() + config.disable_config_locking = True + config.publish_key = 'demo' + config.subscribe_key = 'demo' + config.user_id = 'demo' + + pubnub = PubNub(config) + assert config is pubnub.config + + config.user_id = 'test' + assert pubnub.config.user_id == 'test' + + @pytest.mark.asyncio + async def test_asyncio_config_copy_with_mutability_lock(self): + config = PNConfiguration() + config.disable_config_locking = False + config.publish_key = 'demo' + config.subscribe_key = 'demo' + config.user_id = 'demo' + + pubnub = PubNubAsyncio(config) + assert config is not pubnub.config + assert config.user_id == 'demo' + + @pytest.mark.asyncio + async def test_asyncio_config_copy_with_mutability_lock_disabled(self): + config = PNConfiguration() + config.disable_config_locking = True + config.publish_key = 'demo' + config.subscribe_key = 'demo' + config.user_id = 'demo' + + pubnub = PubNubAsyncio(config) + assert config is pubnub.config + assert config.user_id == 'demo' + + @pytest.mark.asyncio + async def test_asyncio_config_mutability_lock(self): + with pytest.warns(UserWarning): + config = PNConfiguration() + config.disable_config_locking = False + config.publish_key = 'demo' + config.subscribe_key = 'demo' + config.user_id = 'demo' + + pubnub = PubNubAsyncio(config) + assert config is not pubnub.config + + config.user_id = 'test' + assert pubnub.config.user_id == 'demo' + + @pytest.mark.asyncio + async def test_asyncio_config_mutability_lock_disabled(self): + config = PNConfiguration() + config.disable_config_locking = True + config.publish_key = 'demo' + config.subscribe_key = 'demo' + config.user_id = 'demo' + + pubnub = PubNubAsyncio(config) + assert config is pubnub.config + + config.user_id = 'test' + assert pubnub.config.user_id == 'test' + + def test_config_copy(self): + config = PNConfiguration() + config.disable_config_locking = False + config.publish_key = 'demo' + config.subscribe_key = 'demo' + config.user_id = 'demo' + config.lock() + config_copy = config.copy() + assert id(config) != id(config_copy) + assert config._locked is True + assert config_copy._locked is False