diff --git a/examples/cameraServiceExample.py b/examples/cameraServiceExample.py index a0ece2f..2299d60 100644 --- a/examples/cameraServiceExample.py +++ b/examples/cameraServiceExample.py @@ -1,3 +1,5 @@ +from uuid import uuid4 + from context_logger import get_logger, setup_logging from examples import setup_shutdown @@ -15,7 +17,7 @@ def main() -> None: group = Group(name='effectiverange/sniper', url='udp://239.0.1.1:5555') # Define the service information for the camera - info = ServiceInfo(name='er-sniper-camera-1', role='camera', urls={ + info = ServiceInfo(uuid=uuid4(), name='er-sniper-camera-1', role='camera', urls={ 'device-api': 'grpc://er-sniper-camera-1/device', 'video-stream': 'blob:http://er-sniper-camera-1/video' }) diff --git a/hello/advertizer.py b/hello/advertizer.py index f2fe6b5..c95629e 100644 --- a/hello/advertizer.py +++ b/hello/advertizer.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky +# SPDX-FileCopyrightText: 2024 Attila Gombos +# SPDX-License-Identifier: MIT + import random import time from typing import Any diff --git a/hello/api.py b/hello/api.py index 741fbb2..7364a7e 100644 --- a/hello/api.py +++ b/hello/api.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky +# SPDX-FileCopyrightText: 2024 Attila Gombos +# SPDX-License-Identifier: MIT + from dataclasses import dataclass from typing import Any diff --git a/hello/discoverer.py b/hello/discoverer.py index 0340b5a..ea0cf24 100644 --- a/hello/discoverer.py +++ b/hello/discoverer.py @@ -1,6 +1,11 @@ +# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky +# SPDX-FileCopyrightText: 2024 Attila Gombos +# SPDX-License-Identifier: MIT + from dataclasses import dataclass from enum import Enum from typing import Any, Protocol +from uuid import UUID from common_utility import IReusableTimer from context_logger import get_logger @@ -36,7 +41,7 @@ def stop(self) -> None: def discover(self, query: ServiceQuery | None = None) -> None: raise NotImplementedError() - def get_services(self) -> dict[str, ServiceInfo]: + def get_services(self) -> dict[UUID, ServiceInfo]: raise NotImplementedError() def register(self, handler: OnDiscoveryEvent) -> None: @@ -56,7 +61,7 @@ def __init__(self, sender: Sender, receiver: Receiver) -> None: self._receiver = receiver self._group: Group | None = None self._matcher: ServiceMatcher | None = None - self._cache: dict[str, ServiceInfo] = {} + self._cache: dict[UUID, ServiceInfo] = {} self._handlers: list[OnDiscoveryEvent] = [] def __enter__(self) -> Discoverer: @@ -88,7 +93,7 @@ def discover(self, query: ServiceQuery | None = None) -> None: else: log.warning('Cannot discover services, discoverer not started', query=query) - def get_services(self) -> dict[str, ServiceInfo]: + def get_services(self) -> dict[UUID, ServiceInfo]: return self._cache.copy() def register(self, handler: OnDiscoveryEvent) -> None: @@ -102,14 +107,14 @@ def get_handlers(self) -> list[OnDiscoveryEvent]: def _handle_message(self, message: dict[str, Any]) -> None: try: - service = ServiceInfo(**message) + service = ServiceInfo(UUID(message['uuid']), message['name'], message['role'], message.get('urls', {})) self._handle_service(service) except Exception as error: log.warn('Failed to handle received message', data=message, error=error) def _handle_service(self, service: ServiceInfo) -> None: if self._matcher and self._matcher.matches(service): - cached = self._cache.get(service.name) + cached = self._cache.get(service.uuid) if event := self._create_event(cached, service): self._handle_event(event) @@ -128,7 +133,7 @@ def _create_event(self, cached: ServiceInfo | None, service: ServiceInfo) -> Dis def _handle_event(self, event: DiscoveryEvent) -> None: service = event.service - self._cache[service.name] = service + self._cache[service.uuid] = service for callback in self._handlers: try: callback(event) @@ -158,7 +163,7 @@ def stop(self) -> None: def discover(self, query: ServiceQuery | None = None) -> None: self._discoverer.discover(query) - def get_services(self) -> dict[str, ServiceInfo]: + def get_services(self) -> dict[UUID, ServiceInfo]: return self._discoverer.get_services() def register(self, handler: OnDiscoveryEvent) -> None: diff --git a/hello/group.py b/hello/group.py index 68d8f8b..f553510 100644 --- a/hello/group.py +++ b/hello/group.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky +# SPDX-FileCopyrightText: 2024 Attila Gombos +# SPDX-License-Identifier: MIT + from dataclasses import dataclass from enum import Enum diff --git a/hello/receiver.py b/hello/receiver.py index dd7c150..c032bc9 100644 --- a/hello/receiver.py +++ b/hello/receiver.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky +# SPDX-FileCopyrightText: 2024 Attila Gombos +# SPDX-License-Identifier: MIT + from concurrent.futures import ThreadPoolExecutor from typing import Any, Protocol diff --git a/hello/scheduler.py b/hello/scheduler.py index 5cfd16c..2517a66 100644 --- a/hello/scheduler.py +++ b/hello/scheduler.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky +# SPDX-FileCopyrightText: 2024 Attila Gombos +# SPDX-License-Identifier: MIT + from typing import TypeVar, Generic, Any from common_utility import IReusableTimer diff --git a/hello/sender.py b/hello/sender.py index 67ad7d4..5a42be9 100644 --- a/hello/sender.py +++ b/hello/sender.py @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky +# SPDX-FileCopyrightText: 2024 Attila Gombos +# SPDX-License-Identifier: MIT + from typing import Any, cast from context_logger import get_logger @@ -65,6 +69,10 @@ def send(self, data: Any) -> None: def _convert_to_dict(self, data: Any) -> dict[str, Any] | None: if isinstance(data, dict): return data + elif hasattr(data, 'to_dict') and callable(getattr(data, 'to_dict')): + return cast(dict[str, Any], data.to_dict()) + elif hasattr(data, 'as_dict') and callable(getattr(data, 'as_dict')): + return cast(dict[str, Any], data.as_dict()) elif hasattr(data, '__dict__'): return cast(dict[str, Any], data.__dict__) return None diff --git a/hello/service.py b/hello/service.py index db791f3..663a1c3 100644 --- a/hello/service.py +++ b/hello/service.py @@ -1,13 +1,31 @@ +# SPDX-FileCopyrightText: 2024 Ferenc Nandor Janky +# SPDX-FileCopyrightText: 2024 Attila Gombos +# SPDX-License-Identifier: MIT + import re from dataclasses import dataclass, field +from typing import Any +from uuid import UUID @dataclass class ServiceInfo: + uuid: UUID name: str role: str urls: dict[str, str] = field(default_factory=dict) + def __repr__(self) -> str: + return f"ServiceInfo(uuid='{self.uuid}', name='{self.name}', role='{self.role}', urls='{self.urls}')" + + def to_dict(self) -> dict[str, Any]: + return { + 'uuid': str(self.uuid), + 'name': self.name, + 'role': self.role, + 'urls': self.urls + } + @dataclass class ServiceQuery(object): diff --git a/tests/advertizerIntegrationTest.py b/tests/advertizerIntegrationTest.py index ddb2457..7023aa7 100644 --- a/tests/advertizerIntegrationTest.py +++ b/tests/advertizerIntegrationTest.py @@ -1,5 +1,6 @@ import unittest from unittest import TestCase +from uuid import uuid4 from common_utility import ReusableTimer from context_logger import setup_logging @@ -10,10 +11,10 @@ ServiceQuery, ScheduledAdvertizer GROUP = Group('test-group', 'udp://239.0.0.1:5555') +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) class AdvertizerIntegrationTest(TestCase): - SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) @classmethod def setUpClass(cls): @@ -21,7 +22,6 @@ def setUpClass(cls): def setUp(self): print() - self.SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) def test_sends_hello_when_advertises_service(self): # Given @@ -35,12 +35,12 @@ def test_sends_hello_when_advertises_service(self): advertizer.start(GROUP) # When - advertizer.advertise(self.SERVICE_INFO) + advertizer.advertise(SERVICE_INFO) wait_for_assertion(0.1, lambda: self.assertEqual(1, len(messages))) # Then - self.assertEqual([self.SERVICE_INFO.__dict__], messages) + self.assertEqual([SERVICE_INFO.to_dict()], messages) def test_sends_hello_when_query_received(self): # Given @@ -56,7 +56,7 @@ def test_sends_hello_when_query_received(self): test_receiver.start(GROUP.hello()) test_receiver.register(lambda message: messages.append(message)) - advertizer.start(GROUP, self.SERVICE_INFO) + advertizer.start(GROUP, SERVICE_INFO) # When test_sender.send(ServiceQuery('test-service', 'test-role')) @@ -64,7 +64,7 @@ def test_sends_hello_when_query_received(self): wait_for_assertion(0.1, lambda: self.assertEqual(1, len(messages))) # Then - self.assertEqual([self.SERVICE_INFO.__dict__], messages) + self.assertEqual([SERVICE_INFO.to_dict()], messages) def test_sends_hello_when_info_changed_and_query_received(self): # Given @@ -81,15 +81,17 @@ def test_sends_hello_when_info_changed_and_query_received(self): test_receiver.register(lambda message: messages.append(message)) advertizer.start(GROUP) - advertizer.advertise(self.SERVICE_INFO) + advertizer.advertise(SERVICE_INFO) query = ServiceQuery('test-service', 'test-role') test_sender.send(query) wait_for_assertion(0.1, lambda: self.assertEqual(2, len(messages))) - self.SERVICE_INFO.urls['test'] = 'http://localhost:9090' - advertizer.advertise(self.SERVICE_INFO) + new_service_info = ServiceInfo( + SERVICE_INFO.uuid, SERVICE_INFO.name, SERVICE_INFO.role, {'test': 'http://localhost:9090'} + ) + advertizer.advertise(new_service_info) # When test_sender.send(query) @@ -98,10 +100,7 @@ def test_sends_hello_when_info_changed_and_query_received(self): # Then self.assertEqual([ - {'name': 'test-service', 'role': 'test-role', 'urls': {'test': 'http://localhost:8080'}}, - {'name': 'test-service', 'role': 'test-role', 'urls': {'test': 'http://localhost:8080'}}, - {'name': 'test-service', 'role': 'test-role', 'urls': {'test': 'http://localhost:9090'}}, - {'name': 'test-service', 'role': 'test-role', 'urls': {'test': 'http://localhost:9090'}} + SERVICE_INFO.to_dict(), SERVICE_INFO.to_dict(), new_service_info.to_dict(), new_service_info.to_dict() ], messages) def test_sends_hello_when_schedules_advertisement_once(self): @@ -118,12 +117,12 @@ def test_sends_hello_when_schedules_advertisement_once(self): scheduled_advertizer.start(GROUP) # When - scheduled_advertizer.schedule(self.SERVICE_INFO, interval=0.01, one_shot=True) + scheduled_advertizer.schedule(SERVICE_INFO, interval=0.01, one_shot=True) wait_for_assertion(0.1, lambda: self.assertEqual(1, len(messages))) # Then - self.assertEqual([self.SERVICE_INFO.__dict__], messages) + self.assertEqual([SERVICE_INFO.to_dict()], messages) def test_sends_hello_when_schedules_advertisement_periodically(self): # Given @@ -139,7 +138,7 @@ def test_sends_hello_when_schedules_advertisement_periodically(self): scheduled_advertizer.start(GROUP) # When - scheduled_advertizer.schedule(self.SERVICE_INFO, interval=0.01) + scheduled_advertizer.schedule(SERVICE_INFO, interval=0.01) # Then wait_for_assertion(0.1, lambda: self.assertEqual(5, len(messages))) diff --git a/tests/apiIntegrationTest.py b/tests/apiIntegrationTest.py index 2ee0270..8d1f616 100644 --- a/tests/apiIntegrationTest.py +++ b/tests/apiIntegrationTest.py @@ -1,6 +1,7 @@ import unittest from threading import Thread from unittest import TestCase +from uuid import uuid4 from context_logger import setup_logging from test_utility import wait_for_assertion @@ -8,7 +9,7 @@ from hello import ServiceInfo, Group, ServiceQuery, Hello, HelloConfig GROUP = Group('test-group', 'udp://239.0.0.1:5555') -SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) SERVICE_QUERY = ServiceQuery('test-service', 'test-role') @@ -36,7 +37,7 @@ def test_discoverer_caches_advertised_service(self): wait_for_assertion(0.1, lambda: self.assertEqual(1, len(discoverer.get_services()))) # Then - self.assertEqual({SERVICE_INFO.name: SERVICE_INFO}, discoverer.get_services()) + self.assertEqual({SERVICE_INFO.uuid: SERVICE_INFO}, discoverer.get_services()) def test_discoverer_caches_advertised_services(self): # Given @@ -45,8 +46,8 @@ def test_discoverer_caches_advertised_services(self): with (Hello.builder(config).advertizer().default() as advertizer1, Hello.builder(config).advertizer().default() as advertizer2, Hello.builder(config).discoverer().default() as discoverer): - service_info1 = ServiceInfo('test-service1', 'test-role', {'test': 'http://localhost:8080'}) - service_info2 = ServiceInfo('test-service2', 'test-role', {'test': 'http://localhost:8080'}) + service_info1 = ServiceInfo(uuid4(), 'test-service1', 'test-role', {'test': 'http://localhost:8080'}) + service_info2 = ServiceInfo(uuid4(), 'test-service2', 'test-role', {'test': 'http://localhost:8080'}) advertizer1.start(GROUP, service_info1) advertizer2.start(GROUP, service_info2) discoverer.start(GROUP, ServiceQuery('test-service.+', 'test-role')) @@ -60,8 +61,8 @@ def test_discoverer_caches_advertised_services(self): # Then self.assertEqual({ - service_info1.name: service_info1, - service_info2.name: service_info2 + service_info1.uuid: service_info1, + service_info2.uuid: service_info2 }, discoverer.get_services()) def test_discoverer_caches_advertised_service_when_advertisement_scheduled_once(self): @@ -79,7 +80,7 @@ def test_discoverer_caches_advertised_service_when_advertisement_scheduled_once( wait_for_assertion(0.2, lambda: self.assertEqual(1, len(discoverer.get_services()))) # Then - self.assertEqual({SERVICE_INFO.name: SERVICE_INFO}, discoverer.get_services()) + self.assertEqual({SERVICE_INFO.uuid: SERVICE_INFO}, discoverer.get_services()) def test_discoverer_caches_advertised_service_when_advertisement_scheduled_periodically(self): # Given @@ -96,7 +97,7 @@ def test_discoverer_caches_advertised_service_when_advertisement_scheduled_perio wait_for_assertion(0.2, lambda: self.assertEqual(1, len(discoverer.get_services()))) # Then - self.assertEqual({SERVICE_INFO.name: SERVICE_INFO}, discoverer.get_services()) + self.assertEqual({SERVICE_INFO.uuid: SERVICE_INFO}, discoverer.get_services()) def test_discoverer_caches_discovery_response_service(self): # Given @@ -113,7 +114,7 @@ def test_discoverer_caches_discovery_response_service(self): wait_for_assertion(0.2, lambda: self.assertEqual(1, len(discoverer.get_services()))) # Then - self.assertEqual({SERVICE_INFO.name: SERVICE_INFO}, discoverer.get_services()) + self.assertEqual({SERVICE_INFO.uuid: SERVICE_INFO}, discoverer.get_services()) def test_discoverer_caches_discovery_response_services(self): # Given @@ -122,8 +123,8 @@ def test_discoverer_caches_discovery_response_services(self): with (Hello.builder(config).advertizer().default() as advertizer1, Hello.builder(config).advertizer().default() as advertizer2, Hello.builder(config).discoverer().default() as discoverer): - service_info1 = ServiceInfo('test-service1', 'test-role', {'test': 'http://localhost:8080'}) - service_info2 = ServiceInfo('test-service2', 'test-role', {'test': 'http://localhost:8080'}) + service_info1 = ServiceInfo(uuid4(), 'test-service1', 'test-role', {'test': 'http://localhost:8080'}) + service_info2 = ServiceInfo(uuid4(), 'test-service2', 'test-role', {'test': 'http://localhost:8080'}) advertizer1.start(GROUP, service_info1) advertizer2.start(GROUP, service_info2) discoverer.start(GROUP, ServiceQuery('test-service.+', 'test-role')) @@ -135,8 +136,8 @@ def test_discoverer_caches_discovery_response_services(self): # Then self.assertEqual({ - service_info1.name: service_info1, - service_info2.name: service_info2 + service_info1.uuid: service_info1, + service_info2.uuid: service_info2 }, discoverer.get_services()) def test_discoverer_caches_discovery_response_service_when_discovery_scheduled_once(self): @@ -154,7 +155,7 @@ def test_discoverer_caches_discovery_response_service_when_discovery_scheduled_o wait_for_assertion(0.2, lambda: self.assertEqual(1, len(discoverer.get_services()))) # Then - self.assertEqual({SERVICE_INFO.name: SERVICE_INFO}, discoverer.get_services()) + self.assertEqual({SERVICE_INFO.uuid: SERVICE_INFO}, discoverer.get_services()) def test_discoverer_caches_discovery_response_service_when_discovery_scheduled_periodically(self): # Given @@ -171,7 +172,7 @@ def test_discoverer_caches_discovery_response_service_when_discovery_scheduled_p wait_for_assertion(0.2, lambda: self.assertEqual(1, len(discoverer.get_services()))) # Then - self.assertEqual({SERVICE_INFO.name: SERVICE_INFO}, discoverer.get_services()) + self.assertEqual({SERVICE_INFO.uuid: SERVICE_INFO}, discoverer.get_services()) if __name__ == '__main__': diff --git a/tests/defaultAdvertizerTest.py b/tests/defaultAdvertizerTest.py index bc26b0b..4429705 100644 --- a/tests/defaultAdvertizerTest.py +++ b/tests/defaultAdvertizerTest.py @@ -1,13 +1,14 @@ import unittest from unittest import TestCase from unittest.mock import MagicMock +from uuid import uuid4 from context_logger import setup_logging from hello import ServiceInfo, Group, Sender, DefaultAdvertizer GROUP = Group('test-group', 'udp://239.0.0.1:5555') -SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) class DefaultAdvertizerTest(TestCase): @@ -82,7 +83,7 @@ def test_sends_last_info_when_passed_at_start_and_at_advertise(self): # Given sender = MagicMock(spec=Sender) advertizer = DefaultAdvertizer(sender) - advertizer.start(GROUP, ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:9090'})) + advertizer.start(GROUP, ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:9090'})) # When advertizer.advertise(SERVICE_INFO) diff --git a/tests/defaultDiscovererTest.py b/tests/defaultDiscovererTest.py index 6275923..6dc8e70 100644 --- a/tests/defaultDiscovererTest.py +++ b/tests/defaultDiscovererTest.py @@ -1,6 +1,7 @@ import unittest from unittest import TestCase from unittest.mock import MagicMock +from uuid import uuid4 from context_logger import setup_logging @@ -9,7 +10,7 @@ GROUP = Group('test-group', 'udp://239.0.0.1:5555') SERVICE_QUERY = ServiceQuery('test-.*', 'test-.*') -SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) class DefaultDiscovererTest(TestCase): @@ -99,10 +100,10 @@ def test_caches_service_and_calls_handler_when_receives_matching_info(self): discoverer.register(handler) # When - discoverer._handle_message(SERVICE_INFO.__dict__) + discoverer._handle_message(SERVICE_INFO.to_dict()) # Then - self.assertEqual({SERVICE_INFO.name: SERVICE_INFO}, discoverer.get_services()) + self.assertEqual({SERVICE_INFO.uuid: SERVICE_INFO}, discoverer.get_services()) handler.assert_called_once_with(DiscoveryEvent(SERVICE_INFO, DiscoveryEventType.DISCOVERED)) def test_updates_service_and_calls_handler_when_receives_matching_info(self): @@ -113,15 +114,17 @@ def test_updates_service_and_calls_handler_when_receives_matching_info(self): discoverer.start(GROUP, SERVICE_QUERY) handler = MagicMock(spec=OnDiscoveryEvent) discoverer.register(handler) - discoverer._handle_message(SERVICE_INFO.__dict__) + discoverer._handle_message(SERVICE_INFO.to_dict()) handler.reset_mock() - new_service_info = ServiceInfo(SERVICE_INFO.name, SERVICE_INFO.role, {'test': 'http://localhost:9090'}) + new_service_info = ServiceInfo( + SERVICE_INFO.uuid, SERVICE_INFO.name, SERVICE_INFO.role, {'test': 'http://localhost:9090'} + ) # When - discoverer._handle_message(new_service_info.__dict__) + discoverer._handle_message(new_service_info.to_dict()) # Then - self.assertEqual({SERVICE_INFO.name: new_service_info}, discoverer.get_services()) + self.assertEqual({SERVICE_INFO.uuid: new_service_info}, discoverer.get_services()) handler.assert_called_once_with(DiscoveryEvent(new_service_info, DiscoveryEventType.UPDATED)) def test_does_not_call_handler_when_service_info_not_changed(self): @@ -132,11 +135,11 @@ def test_does_not_call_handler_when_service_info_not_changed(self): discoverer.start(GROUP, SERVICE_QUERY) handler = MagicMock(spec=OnDiscoveryEvent) discoverer.register(handler) - discoverer._handle_message(SERVICE_INFO.__dict__) + discoverer._handle_message(SERVICE_INFO.to_dict()) handler.reset_mock() # When - discoverer._handle_message(SERVICE_INFO.__dict__) + discoverer._handle_message(SERVICE_INFO.to_dict()) # Then handler.assert_not_called() @@ -152,10 +155,10 @@ def test_handles_handler_error_gracefully(self): discoverer.register(handler) # When - discoverer._handle_message(SERVICE_INFO.__dict__) + discoverer._handle_message(SERVICE_INFO.to_dict()) # Then - self.assertEqual({SERVICE_INFO.name: SERVICE_INFO}, discoverer.get_services()) + self.assertEqual({SERVICE_INFO.uuid: SERVICE_INFO}, discoverer.get_services()) handler.assert_called_once_with(DiscoveryEvent(SERVICE_INFO, DiscoveryEventType.DISCOVERED)) def test_handles_invalid_message_gracefully(self): @@ -178,10 +181,10 @@ def test_does_not_cache_service_when_info_not_matching_query(self): discoverer = DefaultDiscoverer(sender, receiver) discoverer.start(GROUP, SERVICE_QUERY) - non_matching_info = ServiceInfo('other-service', 'test-role', {'test': 'http://localhost:8080'}) + non_matching_info = ServiceInfo(uuid4(), 'other-service', 'test-role', {'test': 'http://localhost:8080'}) # When - discoverer._handle_message(non_matching_info.__dict__) + discoverer._handle_message(non_matching_info.to_dict()) # Then self.assertEqual({}, discoverer.get_services()) @@ -194,7 +197,7 @@ def test_does_not_cache_service_when_no_query_set(self): discoverer.start(GROUP) # When - discoverer._handle_message(SERVICE_INFO.__dict__) + discoverer._handle_message(SERVICE_INFO.to_dict()) # Then self.assertEqual({}, discoverer.get_services()) diff --git a/tests/discovererIntegrationTest.py b/tests/discovererIntegrationTest.py index cb518f6..f444990 100644 --- a/tests/discovererIntegrationTest.py +++ b/tests/discovererIntegrationTest.py @@ -1,5 +1,6 @@ import unittest from unittest import TestCase +from uuid import uuid4 from common_utility import ReusableTimer from context_logger import setup_logging @@ -10,10 +11,10 @@ GROUP = Group('test-group', 'udp://239.0.0.1:5555') SERVICE_QUERY = ServiceQuery('test-service', 'test-role') +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) class DiscovererIntegrationTest(TestCase): - SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) @classmethod def setUpClass(cls): @@ -21,7 +22,6 @@ def setUpClass(cls): def setUp(self): print() - self.SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) def test_discovers_service_when_hello_received(self): # Given @@ -34,12 +34,12 @@ def test_discovers_service_when_hello_received(self): discoverer.start(GROUP, SERVICE_QUERY) # When - test_sender.send(self.SERVICE_INFO) + test_sender.send(SERVICE_INFO) wait_for_assertion(0.1, lambda: self.assertEqual(1, len(discoverer.get_services()))) # Then - self.assertEqual({self.SERVICE_INFO.name: self.SERVICE_INFO}, discoverer.get_services()) + self.assertEqual({SERVICE_INFO.uuid: SERVICE_INFO}, discoverer.get_services()) def test_updates_service_when_info_changed(self): # Given @@ -51,23 +51,25 @@ def test_updates_service_when_info_changed(self): test_sender.start(GROUP.hello()) discoverer.start(GROUP, SERVICE_QUERY) - test_sender.send(self.SERVICE_INFO) + test_sender.send(SERVICE_INFO) wait_for_assertion(0.1, lambda: self.assertEqual(1, len(discoverer.get_services()))) # When - self.SERVICE_INFO.urls['test'] = 'http://localhost:9090' - test_sender.send(self.SERVICE_INFO) + new_service_info = ServiceInfo( + SERVICE_INFO.uuid, SERVICE_INFO.name, SERVICE_INFO.role, {'test': 'http://localhost:9090'} + ) + test_sender.send(new_service_info) wait_for_assertion(0.1, lambda: self.assertEqual( 'http://localhost:9090', - discoverer.get_services()[self.SERVICE_INFO.name].urls['test'] + discoverer.get_services()[SERVICE_INFO.uuid].urls['test'] )) # Then self.assertEqual( 'http://localhost:9090', - discoverer.get_services()[self.SERVICE_INFO.name].urls['test'] + discoverer.get_services()[SERVICE_INFO.uuid].urls['test'] ) def test_sends_query(self): diff --git a/tests/dishReceiverTest.py b/tests/dishReceiverTest.py index 339d191..f7ad484 100644 --- a/tests/dishReceiverTest.py +++ b/tests/dishReceiverTest.py @@ -1,6 +1,7 @@ import unittest from unittest import TestCase from unittest.mock import MagicMock +from uuid import uuid4 from context_logger import setup_logging from test_utility import wait_for_assertion @@ -9,7 +10,7 @@ from hello import ServiceInfo, Group, DishReceiver, OnMessage GROUP = Group('test-group', 'udp://239.0.0.1:5555') -SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) class DishReceiverTest(TestCase): @@ -112,7 +113,7 @@ def test_calls_registered_handler_on_message(self): # Given group = GROUP.hello() context = MagicMock(spec=Context) - context.socket.return_value.recv_json.return_value = SERVICE_INFO.__dict__ + context.socket.return_value.recv_json.return_value = SERVICE_INFO.to_dict() handler = MagicMock(spec=OnMessage) with DishReceiver(context) as receiver: @@ -126,7 +127,7 @@ def test_calls_registered_handler_on_message(self): receiver.start(group) # Then - wait_for_assertion(0.1, lambda: handler.assert_called_once_with(SERVICE_INFO.__dict__)) + wait_for_assertion(0.1, lambda: handler.assert_called_once_with(SERVICE_INFO.to_dict())) def test_handles_message_receive_error_gracefully(self): # Given @@ -152,7 +153,7 @@ def test_handles_handler_execution_error_gracefully(self): # Given group = GROUP.hello() context = MagicMock(spec=Context) - context.socket.return_value.recv_json.return_value = SERVICE_INFO.__dict__ + context.socket.return_value.recv_json.return_value = SERVICE_INFO.to_dict() handler = MagicMock(spec=OnMessage) handler.side_effect = Exception("Execution failed") @@ -167,7 +168,7 @@ def test_handles_handler_execution_error_gracefully(self): receiver.start(group) # Then - wait_for_assertion(0.1, lambda: handler.assert_called_once_with(SERVICE_INFO.__dict__)) + wait_for_assertion(0.1, lambda: handler.assert_called_once_with(SERVICE_INFO.to_dict())) if __name__ == '__main__': diff --git a/tests/radioSenderTest.py b/tests/radioSenderTest.py index 681992e..af4afa2 100644 --- a/tests/radioSenderTest.py +++ b/tests/radioSenderTest.py @@ -1,15 +1,18 @@ import unittest +from typing import Any from unittest import TestCase from unittest.mock import MagicMock +from uuid import uuid4 from context_logger import setup_logging from zmq import Context, ZMQError -from hello import ServiceInfo, Group +from hello import ServiceInfo, Group, ServiceQuery from hello.sender import RadioSender GROUP = Group('test-group', 'udp://239.0.0.1:5555') -SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) +SERVICE_QUERY = ServiceQuery('test-service', 'test-role') class RadioSenderTest(TestCase): @@ -88,11 +91,44 @@ def test_sends_message_when_convertible_to_dict(self): sender = RadioSender(context) sender.start(group) + # When + sender.send(SERVICE_QUERY) + + # Then + context.socket.return_value.send_json.assert_called_with(SERVICE_QUERY.__dict__, group='hello:test-group') + + def test_sends_message_when_convertible_to_dict_with_to_dict(self): + # Given + group = GROUP.hello() + context = MagicMock(spec=Context) + sender = RadioSender(context) + sender.start(group) + # When sender.send(SERVICE_INFO) # Then - context.socket.return_value.send_json.assert_called_with(SERVICE_INFO.__dict__, group='hello:test-group') + context.socket.return_value.send_json.assert_called_with(SERVICE_INFO.to_dict(), group='hello:test-group') + + def test_sends_message_when_convertible_to_dict_with_as_dict(self): + # Given + group = GROUP.hello() + context = MagicMock(spec=Context) + sender = RadioSender(context) + sender.start(group) + + class TestData: + + def as_dict(self) -> dict[str, Any]: + return SERVICE_INFO.to_dict() + + data = TestData() + + # When + sender.send(data) + + # Then + context.socket.return_value.send_json.assert_called_with(data.as_dict(), group='hello:test-group') def test_sends_message_when_type_is_dict(self): # Given @@ -102,10 +138,10 @@ def test_sends_message_when_type_is_dict(self): sender.start(group) # When - sender.send(SERVICE_INFO.__dict__) + sender.send(SERVICE_INFO.to_dict()) # Then - context.socket.return_value.send_json.assert_called_with(SERVICE_INFO.__dict__, group='hello:test-group') + context.socket.return_value.send_json.assert_called_with(SERVICE_INFO.to_dict(), group='hello:test-group') def test_does_not_send_message_when_not_serializable(self): # Given @@ -143,7 +179,7 @@ def test_handles_send_message_error_gracefully(self): sender.send(SERVICE_INFO) # Then - context.socket.return_value.send_json.assert_called_once_with(SERVICE_INFO.__dict__, group='hello:test-group') + context.socket.return_value.send_json.assert_called_once_with(SERVICE_INFO.to_dict(), group='hello:test-group') if __name__ == '__main__': diff --git a/tests/receiverIntegrationTest.py b/tests/receiverIntegrationTest.py index 665905a..fd17dba 100644 --- a/tests/receiverIntegrationTest.py +++ b/tests/receiverIntegrationTest.py @@ -1,5 +1,6 @@ import unittest from unittest import TestCase +from uuid import uuid4 from context_logger import setup_logging from test_utility import wait_for_assertion @@ -8,7 +9,7 @@ from hello import ServiceInfo, Group, DishReceiver GROUP = Group('test-group', 'udp://239.0.0.1:5555') -SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) class ReceiverIntegrationTest(TestCase): @@ -32,12 +33,12 @@ def test_receives_message(self): receiver.start(group) # When - radio.send_json(SERVICE_INFO.__dict__, group=group.name) + radio.send_json(SERVICE_INFO.to_dict(), group=group.name) wait_for_assertion(0.1, lambda: self.assertEqual(1, len(messages))) # Then - self.assertEqual([SERVICE_INFO.__dict__], messages) + self.assertEqual([SERVICE_INFO.to_dict()], messages) if __name__ == '__main__': diff --git a/tests/respondingAdvertizerTest.py b/tests/respondingAdvertizerTest.py index 96e6334..518db3e 100644 --- a/tests/respondingAdvertizerTest.py +++ b/tests/respondingAdvertizerTest.py @@ -1,13 +1,14 @@ import unittest from unittest import TestCase from unittest.mock import MagicMock +from uuid import uuid4 from context_logger import setup_logging from hello import ServiceInfo, Group, Sender, Receiver, RespondingAdvertizer, ServiceQuery GROUP = Group('test-group', 'udp://239.0.0.1:5555') -SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) class RespondingAdvertizerTest(TestCase): diff --git a/tests/scheduledAdvertizerTest.py b/tests/scheduledAdvertizerTest.py index 4872dea..f572ddd 100644 --- a/tests/scheduledAdvertizerTest.py +++ b/tests/scheduledAdvertizerTest.py @@ -1,6 +1,7 @@ import unittest from unittest import TestCase from unittest.mock import MagicMock +from uuid import uuid4 from common_utility import IReusableTimer from context_logger import setup_logging @@ -8,7 +9,7 @@ from hello import ServiceInfo, Group, ScheduledAdvertizer, Advertizer GROUP = Group('test-group', 'udp://239.0.0.1:5555') -SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) class ScheduledAdvertizerTest(TestCase): diff --git a/tests/senderIntegrationTest.py b/tests/senderIntegrationTest.py index 5c43f47..319cb7e 100644 --- a/tests/senderIntegrationTest.py +++ b/tests/senderIntegrationTest.py @@ -1,6 +1,7 @@ import unittest from threading import Thread from unittest import TestCase +from uuid import uuid4 from context_logger import setup_logging from test_utility import wait_for_assertion @@ -10,7 +11,7 @@ from hello.sender import RadioSender GROUP = Group('test-group', 'udp://239.0.0.1:5555') -SERVICE_INFO = ServiceInfo('test-service', 'test-role', {'test': 'http://localhost:8080'}) +SERVICE_INFO = ServiceInfo(uuid4(), 'test-service', 'test-role', {'test': 'http://localhost:8080'}) class SenderIntegrationTest(TestCase): @@ -40,7 +41,7 @@ def test_sends_message(self): wait_for_assertion(0.1, lambda: self.assertEqual(1, len(messages))) # Then - self.assertEqual([SERVICE_INFO.__dict__], messages) + self.assertEqual([SERVICE_INFO.to_dict()], messages) if __name__ == '__main__':